Salesforce DX

Goals

  • Source-Driven way to manage and develop apps on Force.com platform
    • Source code and metadata exist outside of the Salesforce Org
    • Version Control System is source of truth
  • Team Collaboration
  • Agile
  • CLI (command-line-interface) to support Continuous Integration (CI) and Delviery (CD)
  • Open and Prescriptive Developer Experience
  • Rapid Testing and Development

Sandbox types

With DX

Scratch Org Limits

  • A scratch org expires in 7 days unless you set a duration when you create it (max: 30 days)
  • 200 MB for data, 50 MB for files

New Items

  • SFDX CLI
  • IDE plugins
    • Apex Code Editor
    • Apex Debugger
    • Lightning Support
    • Visualforce support
    • CLI integration
    • Codescan

SFDX CLI


CLI integration

CLI integration - contd.

Extensions

Built-in Codescan

ANT task based Apex Codescan - Jenkins CI

GitHub

ANT task based Lightning Linter - Jenkins CI


GitHub

Lightning Linter on Org

Lightning Linter - Code

Browser Automation Tool

yeoman scaffolding for Lightning Projects

yeoman scaffolding repo

Continuous Integration and Continuous Delivery

Elements

Principles

Before DX

Before DX - contd.

DX - Developer mind to users hand

Pipeline - Developer mind to users hand

More testing - better product!

  • Perf testing and UAT can be swapped, if needed

Branching - Suggestion!

  • Lineup Branch for every env
  • 2 developers (my and yer)
  • develop branch is team's feature branch
  • integrate branch is lined with what is deployed to UAT
  • master branch is lined with Staging and Prod

Branching

  • Current 1.0, need to make 1.1

Branching - contd.

  • Check back to develop branch
  • CI kicks in and runs set of tests

Branching - contd..

  • Another user finishes the development
  • This users needs to pull down the changes from develop branch

Branching - contd...

  • Run the tests and push into develop branch
  • After testing, release candiate moved to integrate branch
  • After integration testing, moved to master branch

References

DX - Hands-on

An ounce of practice is worth more than tons of preaching.
- Mohandas K Gandhi

Dev Hub

  • Comprises Objects with Permission that allow admins to control level-of-access available to a user and org
  • Provides ability to create and manage scratch orgs
  • You can choose an org to function as your Dev Hub
  • Sign up for Dev Hub 30-day Trial Org
Login into DevHub:

Install CLI (command-line-interface)

$ sfdx --version
$ sfdx update
sfdx-cli: Updating CLI... already on latest version: 6.1.19-4341549
sfdx-cli: Updating plugins... done
         

Login to Dev Hub via CLI

$ sfdx force:auth:web:login  -h
Usage: sfdx force:auth:web:login [-i ] [-r ] [-d] [-s] [-a ] [--json] [--loglevel ]

authorize an org using the web login flow

Flags:
 -i, --clientid CLIENTID         OAuth client ID (sometimes called the consumer key)
 -r, --instanceurl INSTANCEURL   the login URL of the instance the org lives on
 -a, --setalias SETALIAS         set an alias for the authenticated org
 -d, --setdefaultdevhubusername  set the authenticated org as the default dev hub org for scratch org creation
 -s, --setdefaultusername        set the authenticated org as the default username that all commands run against
 --json                          format output as json
 --loglevel LOGLEVEL             logging level for this command invocation (error*,trace,debug,info,warn,fatal)

To log in to a sandbox, set --instanceurl to https://test.salesforce.com.

Examples:
   $ sfdx force:auth:web:login -a TestOrg1
   $ sfdx force:auth:web:login -i 
   $ sfdx force:auth:web:login -r https://test.salesforce.com
  $ sfdx force:auth:web:login -d -a DevHub
  Successfully authorized username with org ID orgid

Login to Dev Hub via CLI - contd.

You may now close the browser
   

List the orgs with CLI

$ sfdx force:org:list
=== Orgs
     ALIAS   USERNAME                              ORG ID              CONNECTED STATUS
───  ──────  ────────────────────────────────────  ──────────────────  ─────────────────
(D)  DevHub  mohan.chinnappan.n_dh_1@gmail.com     00D6A000001LbwLUAS  Connected

   

Create a Salesforce DX Project

$ sfdx force:project:create -n geolocation
target dir = /Users/mchinnappan/sfdx
   create geolocation/sfdx-project.json
   create geolocation/README.md
   create geolocation/.forceignore
   create geolocation/config/project-scratch-def.json
$ ls
geolocation	sfdx-simple
~/sfdx:
$ tree geolocation/
geolocation/
├── README.md
├── config
│   └── project-scratch-def.json - config for scratch org
├── force-app - folder containing source code of the project
│   └── main
│       └── default
│           └── aura
└── sfdx-project.json  - has project info, path to source, clases, metadata, namespace, api-version

5 directories, 3 files
$ cat geolocation/config/project-scratch-def.json
{
    "orgName": "mchinnappan Company",
    "edition": "Developer",
    "orgPreferences" : {
        "enabled": ["S1DesktopEnabled"]
    }
}

$ cat geolocation/sfdx-project.json
{
  "packageDirectories": [
    {
      "path": "force-app",
      "default": true
    }
  ],
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "41.0"
}

  

Scratch Org

  • Temporary Salesforce environments where you do the bulk of your development work in source-driven development paradigm (SFDX)
  • You can :
    • Push the source and metadata to a scratch org
    • Pull any changes you make in the scratch org back to your local project
    • Sync this project with your VCS (version control system) repo

List Active and Daily Scratch Orgs


$ sfdx force:limits:api:display -u DevHub
NAME                                   REMAINING  MAXIMUM
─────────────────────────────────────  ─────────  ─────────
ActiveScratchOrgs                      18         20
ConcurrentAsyncGetReportInstances      200        200
ConcurrentSyncReportRuns               20         20
DailyApiRequests                       14998      15000
DailyAsyncApexExecutions               250000     250000
DailyBulkApiRequests                   10000      10000
DailyDurableGenericStreamingApiEvents  10000      10000
DailyDurableStreamingApiEvents         10000      10000
DailyGenericStreamingApiEvents         10000      10000
DailyScratchOrgs                       40         40
DailyStreamingApiEvents                10000      10000
DailyWorkflowEmails                    150        150
DataStorageMB                          1073       1073
DurableStreamingApiConcurrentClients   20         20
FileStorageMB                          1073       1073
HourlyAsyncReportRuns                  1200       1200
HourlyDashboardRefreshes               200        200
HourlyDashboardResults                 5000       5000
HourlyDashboardStatuses                999999999  999999999
HourlyODataCallout                     10000      10000
HourlySyncReportRuns                   500        500
HourlyTimeBasedWorkflow                50         50
MassEmail                              10         10
PermissionSets                         1500       1500
SingleEmail                            15         15
StreamingApiConcurrentClients          20         20
       

Scratch Org Parameters

Create Scratch Org

$ sfdx force:org:create -h
Usage: sfdx force:org:create name=value... [-f ] [-n] [-c] [-i ] [-s] [-a ] [-w ] [-d ] [-v ] [--json] [--loglevel ]
create a scratch org
Flags:
-i, --clientid CLIENTID                          connected app consumer key
-f, --definitionfile DEFINITIONFILE              path to a scratch org definition file
-d, --durationdays DURATIONDAYS                  duration of the scratch org (in days) (default:7, min:1, max:30)
-c, --noancestors                                do not include second-generation package ancestors in the scratch org
-n, --nonamespace                                creates the scratch org with no namespace
-a, --setalias SETALIAS                          set an alias for for the created scratch org
-s, --setdefaultusername                         set the created org as the default org for this project
-v, --targetdevhubusername TARGETDEVHUBUSERNAME  username or alias for the dev hub org; overrides default dev hub org
-w, --wait WAIT                                  the streaming client socket timeout (in minutes) (default:6, min:2)
--json                                           format output as json
--loglevel LOGLEVEL                              logging level for this command invocation (error*,trace,debug,info,warn,fatal)
To set up a connected app for your new scratch org, specify the value that was returned when you created a connected app in your Dev Hub org as --clientid.
Examples:
$ sfdx force:org:create -f config/enterprise-scratch-def.json -a TestOrg1
$ sfdx force:org:create -a MyDevOrg -s -v me@myhub.org edition=Developer
$ sfdx force:org:create -f config/enterprise-scratch-def.json -a OrgWithOverrides username=testuser1@mycompany.org
#----
$ cd geolocation/
$ sfdx force:org:create -s -f config/project-scratch-def.json -a geolocationAppScratch1
Successfully created scratch org: 00DR00000001xNfMAI, username: test-irkcftkmuyzr@example.com
#----
$ sfdx force:org:list
=== Orgs
     ALIAS   USERNAME                              ORG ID              CONNECTED STATUS
───  ──────  ────────────────────────────────────  ──────────────────  ─────────────────
(D)  DevHub  mohan.chinnappan.n_dh_1@gmail.com     00D6A000001LbwLUAS  Connected
     ALIAS                   SCRATCH ORG NAME     USERNAME                       ORG ID              EXPIRATION DATE
───  ──────────────────────  ───────────────────  ─────────────────────────────  ──────────────────  ───────────────
(U)  geolocationAppScratch1  mchinnappan Company  test-irkcftkmuyzr@example.com  00DR00000001xNfMAI  2018-02-16

Open the created scratch org


$ sfdx force:org:open -h
Usage: sfdx force:org:open [-p ] [-r] [-u ] [--json] [--loglevel ]
open an org in your browser

Flags:
-p, --path PATH                      navigation URL path
-u, --targetusername TARGETUSERNAME  username or alias for the target org; overrides default target org
-r, --urlonly                        display navigation URL, but don’t launch browser
--json                               format output as json
--loglevel LOGLEVEL                  logging level for this command invocation (error*,trace,debug,info,warn,fatal)

Opens your default scratch org, or another org that you specify.

To open a specific page, specify the portion of the URL after "yourInstance.salesforce.com/" as --path.
For example, specify "--path one/one.app" to open Lightning Experience, or specify "--path /apex/YourPage"
to open a Visualforce page.

To generate a URL but not launch your browser, specify --urlonly.

Examples:
$ sfdx force:org:open
$ sfdx force:org:open -u me@my.org
$ sfdx force:org:open -u MyTestOrg1
$ sfdx force:org:open -r -p one/one.app
#----
$ sfdx force:org:open
Access org 00DR00000001xNfMAI as user test-irkcftkmuyzr@example.com with
the following URL: https://java-enterprise-1064-dev-ed.cs2.my.salesforce.com/secur/frontdoor.jsp?sid=00DR00000001xNf!AQgAQM5.0dDRj.rFldgd6qTLG8qPSRuWuxCvrljFEjcatDhElIZ33_tx8tLwkt3IZ8QZiDGK44i3kbRPymejTlY.R.t1xdeP


  

Create a Permission Set

  • Create Permission Set Geolocation for Account.Location

Assign Permission Set to users using CLI

$ sfdx force:user:permset:assign -h
Usage: sfdx force:user:permset:assign -n  [-o ...] [-u ] [--json] [--loglevel ]
assign a permission set to one or more users of an org

Flags:
-o, --onbehalfof ONBEHALFOF          comma-separated list of usernames or aliases to assign the permission set to
-n, --permsetname PERMSETNAME        (required) the name of the permission set to assign
-u, --targetusername TARGETUSERNAME  username or alias for the target org; overrides default target org
--json                               format output as json
--loglevel LOGLEVEL                  logging level for this command invocation (error*,trace,debug,info,warn,fatal)

Defaults to the defaultusername.
Examples:
$ sfdx force:user:permset:assign -n DreamHouse
$ sfdx force:user:permset:assign -n DreamHouse -u me@my.org
$ sfdx force:user:permset:assign -n DreamHouse -o user1@my.org,user2,user3
$ sfdx force:user:permset:assign -n Geolocation
=== Permsets Assigned
USERNAME                       PERMISSION SET ASSIGNMENT
─────────────────────────────  ─────────────────────────
test-irkcftkmuyzr@example.com  Geolocation

Now local project is out-of-date with the scratch org, since we did some changes using UI.
Let us see how we can Pull these changes  into local project folder (geolocation)?

Pull command


$ sfdx force:source:pull -h
Usage: sfdx force:source:pull [-w ] [-f] [-u ] [--json] [--loglevel ]

pull source from the scratch org to the project

Flags:
-f, --forceoverwrite                 ignore conflict warnings and overwrite changes to the project
-u, --targetusername TARGETUSERNAME  username or alias for the target org; overrides default target org
-w, --wait WAIT                      wait time for command to finish in minutes (default: 33) (default:33, min:1)
--json                               format output as json
--loglevel LOGLEVEL                  logging level for this command invocation (error*,trace,debug,info,warn,fatal)

If the command detects a conflict, it displays the conflicts but does not complete the process. After reviewing the conflict, rerun the command with the --forceoverwrite parameter.


$ sfdx force:source:pull
=== Pulled Source
STATE  FULL NAME                               TYPE           PROJECT PATH
─────  ──────────────────────────────────────  ─────────────  ─────────────────────────────────────────────────────────────────────────────────────
Add    Account.Location__c                    CustomField    force-app/main/default/objects/Account/fields/Location__c.field-meta.xml
Add    Account-Account Layout                  Layout         force-app/main/default/layouts/Account-Account Layout.layout-meta.xml
Add    Account-Account %28Support%29 Layout    Layout         force-app/main/default/layouts/Account-Account %28Support%29 Layout.layout-meta.xml
Add    Account-Account %28Sales%29 Layout      Layout         force-app/main/default/layouts/Account-Account %28Sales%29 Layout.layout-meta.xml
Add    Account-Account %28Marketing%29 Layout  Layout         force-app/main/default/layouts/Account-Account %28Marketing%29 Layout.layout-meta.xml
Add    Geolocation                           PermissionSet   force-app/main/default/permissionsets/Geolocation.permissionset-meta.xml

Version Control


$ git init
$ git add -A
$ git commit -m 'Add custom object Account.location and permission set Geolocation'

  

Add Some Data via UI

Export Scratch Org to local folder


$ sfdx force:data:tree:export -h
Usage: sfdx force:data:tree:export -q  [-p] [-x ] [-d ] [-u ] [--json] [--loglevel ]

export data from an org into sObject tree format for force:data:tree:import consumption

Flags:
-d, --outputdir OUTPUTDIR            directory to store files
-p, --plan                           generate mulitple sobject tree files and a plan definition file for aggregated import
-x, --prefix PREFIX                  prefix of generated files
-q, --query QUERY                    (required) soql query, or filepath of file containing a soql query, to retrieve records
-u, --targetusername TARGETUSERNAME  username or alias for the target org; overrides default target org
--json                               format output as json
--loglevel LOGLEVEL                  logging level for this command invocation (error*,trace,debug,info,warn,fatal)

Generates JSON files for use with the force:data:tree:import command.

Examples:
$ sfdx force:data:tree:export -q "SELECT Id, Name, (SELECT Name, Address__c FROM Properties__r) FROM Broker__c"
$ sfdx force:data:tree:export -q  -x export-demo -d /tmp/sfdx-out -p


$ mkdir data
$ sfdx force:data:tree:export \
 -q "SELECT Name, Location__Latitude__s, Location__Longitude__s FROM Account WHERE Location__Latitude__s != NULL AND Location__Longitude__s != NULL" -d ./data
Wrote 3 records to data/Account.json

Exported Data


$ cat data/Account.json | jq
{
  "records": [
    {
      "attributes": {
        "type": "Account",
        "referenceId": "AccountRef1"
      },
      "Name": "Marriott Marquis",
      "Location__Latitude__s": 37.785143,
      "Location__Longitude__s": -122.403405
    },
    {
      "attributes": {
        "type": "Account",
        "referenceId": "AccountRef2"
      },
      "Name": "Hilton Union Square",
      "Location__Latitude__s": 37.786164,
      "Location__Longitude__s": -122.410137
    },
    {
      "attributes": {
        "type": "Account",
        "referenceId": "AccountRef3"
      },
      "Name": "Hyatt",
      "Location__Latitude__s": 37.794157,
      "Location__Longitude__s": -122.396311
    }
  ]
}

Import data into Scratch Org from Local Folder


$ sfdx force:data:tree:import -h
Usage: sfdx force:data:tree:import (-f ... | -p ) [-c ] [--confighelp] [-u ] [--json] [--loglevel ]

import data into an org using SObject Tree Save API

Flags:
-c, --contenttype CONTENTTYPE            if data file extension is not .json, provide content type (applies to all files)
-p, --plan PLAN                          path to plan to insert multiple data files that have master-detail relationships
-f, --sobjecttreefiles SOBJECTTREEFILES  comma-delimited, ordered paths of json files containing collection of record trees to insert
-u, --targetusername TARGETUSERNAME      username or alias for the target org; overrides default target org
--confighelp                             display schema information for the --plan configuration file to stdout; if you use this option, all other options except --json are ignored
--json                                   format output as json
--loglevel LOGLEVEL                      logging level for this command invocation (error*,trace,debug,info,warn,fatal)

To generate JSON files for use with force:data:tree:import, run "sfdx force:data:tree:export".

Examples:
To import records as individual files, first run the export commands:
$ sfdx force:data:tree:export -q "SELECT Id, Name FROM Account"
$ sfdx force:data:tree:export -q "SELECT Id, LastName, FirstName FROM Contact"
Then run the import command:
$ sfdx force:data:tree:import -f Contact.json,Account.json -u me@my.org

To import multiple data files as part of a plan, first run the export command with the -p | --plan flag:
$ sfdx force:data:tree:export -p -q "SELECT Id, Name, (SELECT Id, LastName, FirstName FROM Contacts) FROM Account"
Then run the import command, supplying a filepath value for the -p | --plan parameter:
$ sfdx force:data:tree:import -p Account-Contact-plan.json -u me@my.org

----

In our case: Importing into our Scratch Org:

$ sfdx force:data:tree:import -f data/Account.json
  

Creating Apex Class using CLI


$ sfdx force:apex:class:create -h
Usage: sfdx force:apex:class:create -n  [-t ] [-d ] [-a ] [--json] [--loglevel ]
create an Apex class
Flags:
-a, --apiversion APIVERSION  API version number (41.0*,40.0)
-n, --classname CLASSNAME    (required) name of the generated Apex class
-d, --outputdir OUTPUTDIR    folder for saving the created files
-t, --template TEMPLATE      template to use for file creation (DefaultApexClass*,ApexException,ApexUnitTest,InboundEmailService)
--json                       JSON output
--loglevel LOGLEVEL          logging level for this command invocation (error*,trace,debug,info,warn,fatal)

If not supplied, the apiversion, template, and outputdir use default values.
The outputdir can be an absolute path or relative to the current working directory.
Examples:
$ sfdx force:apex:class:create -n MyClass
$ sfdx force:apex:class:create -n MyClass -d classes
$ sfdx force:apex:class:create -n AccountCtrl -d force-app/main/default/classes
target dir = /Users/mchinnappan/sfdx/geolocation/force-app/main/default/classes
create AccountCtrl.cls
create AccountCtrl.cls-meta.xml
$ tree force-app/main/default/classes/
force-app/main/default/classes/
├── AccountCtrl.cls
└── AccountCtrl.cls-meta.xml
$ cat force-app/main/default/classes/AccountCtrl.cls
public with sharing class AccountCtrl {
  public AccountCtrl() {

  }
}

AccountCtrl - Modified

$ cat force-app/main/default/classes/AccountCtrl.cls

public with sharing class AccountCtrl {
   @AuraEnabled
   public static List findAll() {
      return [SELECT Id, Name, Location__Latitude__s, Location__Longitude__s FROM Account
            WHERE Location__Latitude__s != NULL AND Location__Longitude__s != NULL
            LIMIT 50];
   }
}

Pushing Code to Org from Local Project Folder


$ sfdx force:source:push -h
Usage: sfdx force:source:push [-f] [-g] [-w ] [-u ] [--json] [--loglevel ]

push source to an org from the project

Flags:
-f, --forceoverwrite                 ignore conflict warnings and overwrite changes to scratch org
-g, --ignorewarnings                 deploy changes even if warnings are generated
-u, --targetusername TARGETUSERNAME  username or alias for the target org; overrides default target org
-w, --wait WAIT                      wait time for command to finish in minutes (default: 33) (default:33, min:1)
--json                               format output as json
--loglevel LOGLEVEL                  logging level for this command invocation (error*,trace,debug,info,warn,fatal)

If the command detects a conflict, it displays the conflicts but does not complete the process. After reviewing the conflict, rerun the command with the --forceoverwrite parameter.


  

$ sfdx force:source:push
=== Pushed Source
STATE  FULL NAME    TYPE       PROJECT PATH
─────  ───────────  ─────────  ───────────────────────────────────────────────────────
Add    AccountCtrl  ApexClass  force-app/main/default/classes/AccountCtrl.cls
Add    AccountCtrl  ApexClass  force-app/main/default/classes/AccountCtrl.cls-meta.xml

  

Pushing Code to Org from Local Project Folder - contd.

Create Lightning Component using CLI

$ sfdx force:lightning:component:create -h
Usage: sfdx force:lightning:component:create -n  [-t ] [-d ] [-a ] [--json] [--loglevel ]
create a Lightning component
Flags:
-a, --apiversion APIVERSION        API version number (41.0*,40.0)
-n, --componentname COMPONENTNAME  (required) name of the generated Lightning component
-d, --outputdir OUTPUTDIR          folder for saving the created files
-t, --template TEMPLATE            template to use for file creation (DefaultLightningCmp*)
--json                             JSON output
--loglevel LOGLEVEL                logging level for this command invocation (error*,trace,debug,info,warn,fatal)
If not supplied, the apiversion, template, and outputdir use default values.
The outputdir can be an absolute path or relative to the current working directory.
Examples:
$ sfdx force:lightning:component:create -n mycomponent
$ sfdx force:lightning:component:create -n mycomponent -d lightning
  
$ sfdx force:lightning:component:create -n AccountLocator -d force-app/main/default/aura
target dir = /Users/mchinnappan/sfdx/geolocation/force-app/main/default/aura
create AccountLocator/AccountLocator.cmp
create AccountLocator/AccountLocator.cmp-meta.xml
create AccountLocator/AccountLocatorController.js
create AccountLocator/AccountLocatorHelper.js
create AccountLocator/AccountLocator.css
create AccountLocator/AccountLocatorRenderer.js
create AccountLocator/AccountLocator.svg
create AccountLocator/AccountLocator.auradoc
create AccountLocator/AccountLocator.design

$ tree force-app/main/default/aura/
force-app/main/default/aura/
└── AccountLocator
  ├── AccountLocator.auradoc
  ├── AccountLocator.cmp
  ├── AccountLocator.cmp-meta.xml
  ├── AccountLocator.css
  ├── AccountLocator.design
  ├── AccountLocator.svg
  ├── AccountLocatorController.js
  ├── AccountLocatorHelper.js
  └── AccountLocatorRenderer.js
  

Create Lightning Component using CLI - contd.


$ cat force-app/main/default/aura/AccountLocator/AccountLocator.cmp






$ cat force-app/main/default/aura/AccountLocator/AccountLocatorController.js

({
    myAction : function(component, event, helper) {

    }
})


$ cat force-app/main/default/aura/AccountLocator/AccountLocatorHelper.js

({
    helperMethod : function() {

    }
})


  

Update the generated component and stylesheet

$ cat force-app/main/default/aura/AccountLocator/AccountLocator.cmp


  
AccountMap goes here
AccountList goes here
$ cat force-app/main/default/aura/AccountLocator/AccountLocator.css .THIS { position:absolute; height: 100%; width: 100%; background: #FFFFFF; } .THIS>div { height: 50%; }

Push the generated component artifacts into Org

    $ sfdx force:source:push
    === Pushed Source
    STATE  FULL NAME                                   TYPE                  PROJECT PATH
    ─────  ──────────────────────────────────────────  ────────────────────  ──────────────────────────────────────────────────────────────────────
    Add    AccountLocator/AccountLocator.auradoc       AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.auradoc
    Add    AccountLocator/AccountLocator.cmp           AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.cmp
    Add    AccountLocator/AccountLocator.cmp           AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.cmp-meta.xml
    Add    AccountLocator/AccountLocator.css           AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.css
    Add    AccountLocator/AccountLocator.design        AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.design
    Add    AccountLocator/AccountLocator.svg           AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.svg
    Add    AccountLocator/AccountLocatorController.js  AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorController.js
    Add    AccountLocator/AccountLocatorHelper.js      AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorHelper.js
    Add    AccountLocator/AccountLocatorRenderer.js    AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorRenderer.js

  

Create Tab for the component

View the Lightning Component in Scratch Org

Pull Tab creation updates to Project Local Folder


$ sfdx force:source:pull
=== Pulled Source
STATE  FULL NAME                    TYPE       PROJECT PATH
─────  ───────────────────────────  ─────────  ────────────────────────────────────────────────────────────────────────────
Add    Account_Locator              CustomTab  force-app/main/default/tabs/Account_Locator.tab-meta.xml
Add    Custom%3A Support Profile    Profile    force-app/main/default/profiles/Custom%3A Support Profile.profile-meta.xml
Add    Custom%3A Sales Profile      Profile    force-app/main/default/profiles/Custom%3A Sales Profile.profile-meta.xml
Add    Custom%3A Marketing Profile  Profile    force-app/main/default/profiles/Custom%3A Marketing Profile.profile-meta.xml
Add    Admin                        Profile    force-app/main/default/profiles/Admin.profile-meta.xml

 

Create AccountListItem component using CLI


$ sfdx force:lightning:component:create -n AccountListItem -d force-app/main/default/aura
# update the component to:
$ cat force-app/main/default/aura/AccountListItem/AccountListItem.cmp


   
   
  • {!v.account.Name}
  • # update stylesheet to: $ cat force-app/main/default/aura/AccountListItem/AccountListItem.css .THIS { border-bottom: solid 1px #DDDDDD; } .THIS a { display: block; padding: 20px; color: inherit; } .THIS a:active { background-color: #E8F4FB; }

    Create AccountList component using CLI

    
    $ sfdx force:lightning:component:create -n AccountList -d force-app/main/default/aura
    
    
     # update the component to:
     $ cat force-app/main/default/aura/AccountList/AccountList.cmp
     
     
        
        
        
    # update AccountListController.js to: $ cat force-app/main/default/aura/AccountList/AccountListController.js ({ doInit : function(component, event) { var action = component.get("c.findAll"); action.setCallback(this, function(a) { component.set("v.accounts", a.getReturnValue()); }); $A.enqueueAction(action); } })

    AccountList stylesheet update:

    
    
          # update stylesheet to:
          $ cat force-app/main/default/aura/AccountListItem/AccountList.css
          
            .THIS {
                list-style-type: none;
                padding: 0;
                margin: 0;
                background: #FFFFFF;
                 height: 100%;
            }
          
      

    AccountLocator Component update:

    $ cat force-app/main/default/aura/AccountLocator/AccountLocator.cmp
    
    
      
    AccountMap goes here

    Push code from Local Project folder to Org

        $ sfdx force:source:push
      

    Install Leaflet JS lib in Static Resources

    # pull the static resouces into Local Project folder
    $ sfdx force:source:pull
    === Pulled Source
    STATE    FULL NAME  TYPE            PROJECT PATH
    ───────  ─────────  ──────────────  ────────────────────────────────────────────────────────────────────────
    Add      leaflet    StaticResource  force-app/main/default/staticresources/leaflet.resource-meta.xml
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/images/layers-2x.png
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/images/layers.png
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/images/marker-icon-2x.png
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/images/marker-icon.png
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/images/marker-shadow.png
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/leaflet-src.js
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/leaflet-src.js.map
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/leaflet.css
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/leaflet.js
    Changed  leaflet    StaticResource  force-app/main/default/staticresources/leaflet/leaflet.js.map
      

    Create AccountMap Component using CLI

    $ sfdx force:lightning:component:create -n AccountMap -d force-app/main/default/aura
    # Update AccountMap Component as:
    $ cat force-app/main/default/aura/AccountMap/AccountMap.cmp
    
    
      
      
      
    # update AccountMapController.js as: $ cat force-app/main/default/aura/AccountMap/AccountMapController.js ({ jsLoaded: function(component, event, helper) { var map = L.map('map', {zoomControl: false}).setView([37.784173, -122.401557], 14); L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', { attribution: 'Tiles © Esri' }).addTo(map); component.set("v.map", map); } }) $ cat force-app/main/default/aura/AccountMap/AccountMap.css .THIS { width: 100%; height: 100%; } $ cat force-app/main/default/aura/AccountLocator/AccountLocator.cmp
    # Push to Scratch org from Local Project Folder $ sfdx force:source:push

    AccountLocator Component with Map in Place

    Create Event - AccountsLoaded - using CLI

    $ sfdx force:lightning:event:create -n AccountsLoaded -d force-app/main/default/aura
     target dir = /Users/mchinnappan/sfdx/geolocation/force-app/main/default/aura
    create AccountsLoaded/AccountsLoaded.evt
    create AccountsLoaded/AccountsLoaded.evt-meta.xml
    $ cat force-app/main/default/aura/AccountsLoaded/AccountsLoaded.evt
    
    
      
    
    
    # Register the event AccountsLoaded in AccountList component
    $ cat force-app/main/default/aura/AccountList/AccountList.cmp
    
    
        
        
        
        

    Add Event firing in doInit() in AccountList Controller

        $ cat force-app/main/default/aura/AccountList/AccountListController.js
    
    ({
        doInit : function(component, event) {
            var action = component.get("c.findAll");
            action.setCallback(this, function(a) {
              component.set("v.accounts", a.getReturnValue());
              var event = $A.get("e.c:AccountsLoaded");
              event.setParams({"accounts": a.getReturnValue()});
                event.fire();
            });
            $A.enqueueAction(action);
        }
    })
    
    
      

    Event Handling Declaration in AccountMap

    Event Handling Action in AccountMap Controller

    Push these Event related updates to the Scratch org

    
    $ sfdx force:source:push
    === Pushed Source
    STATE    FULL NAME                             TYPE                  PROJECT PATH
    ───────  ────────────────────────────────────  ────────────────────  ──────────────────────────────────────────────────────────────────────
    Add      AccountsLoaded/AccountsLoaded.evt     AuraDefinitionBundle  force-app/main/default/aura/AccountsLoaded/AccountsLoaded.evt
    Add      AccountsLoaded/AccountsLoaded.evt     AuraDefinitionBundle  force-app/main/default/aura/AccountsLoaded/AccountsLoaded.evt-meta.xml
    Changed  AccountList/AccountList.cmp           AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.cmp
    Changed  AccountList/AccountListController.js  AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountListController.js
    Changed  AccountMap/AccountMap.cmp             AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.cmp
    Changed  AccountMap/AccountMapController.js    AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMapController.js
      
      # Open the org
      $ sfdx force:org:open
    

    View AccountMap Update Showing the Markers in the Map

    Create a Test Scratch Org

    • Use a Test Scratch Org to perform end-to-end testing of the development work done so far on the Development Scratch Org: geolocationAppScratch1
    • $ sfdx force:org:create -f config/project-scratch-def.json -a geolocationTestScratch1
      Successfully created scratch org: 00D560000004foREAQ, username: test-huvk8zzifavh@example.com
           
          $ sfdx force:org:list
      === Orgs
           ALIAS   USERNAME                              ORG ID              CONNECTED STATUS
      ───  ──────  ────────────────────────────────────  ──────────────────  ─────────────────
      (D)  DevHub  mohan.chinnappan.n_dh_1@gmail.com     00D6A000001LbwLUAS  Connected
           ALIAS                    SCRATCH ORG NAME     USERNAME                       ORG ID              EXPIRATION DATE
      ───  ───────────────────────  ───────────────────  ─────────────────────────────  ──────────────────  ───────────────
      (U)  geolocationAppScratch1   mchinnappan Company  test-irkcftkmuyzr@example.com  00DR00000001xNfMAI  2018-02-16
           geolocationTestScratch1  mchinnappan Company  test-huvk8zzifavh@example.com  00D560000004foREAQ  2018-02-17
      
        

    Push code/metadata to the newly created Test Scratch Org

    $ sfdx force:source:push -u geolocationTestScratch1
        === Pushed Source
    STATE  FULL NAME                                     TYPE                  PROJECT PATH
    ─────  ────────────────────────────────────────────  ────────────────────  ─────────────────────────────────────────────────────────────────────────────────────
    Add    AccountList/AccountList.auradoc               AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.auradoc
    Add    AccountList/AccountList.cmp                   AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.cmp
    Add    AccountList/AccountList.cmp                   AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.cmp-meta.xml
    Add    AccountList/AccountList.css                   AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.css
    Add    AccountList/AccountList.design                AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.design
    Add    AccountList/AccountList.svg                   AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountList.svg
    Add    AccountList/AccountListController.js          AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountListController.js
    Add    AccountList/AccountListHelper.js              AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountListHelper.js
    Add    AccountList/AccountListRenderer.js            AuraDefinitionBundle  force-app/main/default/aura/AccountList/AccountListRenderer.js
    Add    AccountListItem/AccountListItem.auradoc       AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.auradoc
    Add    AccountListItem/AccountListItem.cmp           AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.cmp
    Add    AccountListItem/AccountListItem.cmp           AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.cmp-meta.xml
    Add    AccountListItem/AccountListItem.css           AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.css
    Add    AccountListItem/AccountListItem.design        AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.design
    Add    AccountListItem/AccountListItem.svg           AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItem.svg
    Add    AccountListItem/AccountListItemController.js  AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItemController.js
    Add    AccountListItem/AccountListItemHelper.js      AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItemHelper.js
    Add    AccountListItem/AccountListItemRenderer.js    AuraDefinitionBundle  force-app/main/default/aura/AccountListItem/AccountListItemRenderer.js
    Add    AccountLocator/AccountLocator.auradoc         AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.auradoc
    Add    AccountLocator/AccountLocator.cmp             AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.cmp
    Add    AccountLocator/AccountLocator.cmp             AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.cmp-meta.xml
    Add    AccountLocator/AccountLocator.css             AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.css
    Add    AccountLocator/AccountLocator.design          AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.design
    Add    AccountLocator/AccountLocator.svg             AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocator.svg
    Add    AccountLocator/AccountLocatorController.js    AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorController.js
    Add    AccountLocator/AccountLocatorHelper.js        AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorHelper.js
    Add    AccountLocator/AccountLocatorRenderer.js      AuraDefinitionBundle  force-app/main/default/aura/AccountLocator/AccountLocatorRenderer.js
    Add    AccountMap/AccountMap.auradoc                 AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.auradoc
    Add    AccountMap/AccountMap.cmp                     AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.cmp
    Add    AccountMap/AccountMap.cmp                     AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.cmp-meta.xml
    Add    AccountMap/AccountMap.css                     AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.css
    Add    AccountMap/AccountMap.design                  AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.design
    Add    AccountMap/AccountMap.svg                     AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMap.svg
    Add    AccountMap/AccountMapController.js            AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMapController.js
    Add    AccountMap/AccountMapHelper.js                AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMapHelper.js
    Add    AccountMap/AccountMapRenderer.js              AuraDefinitionBundle  force-app/main/default/aura/AccountMap/AccountMapRenderer.js
    Add    AccountsLoaded/AccountsLoaded.evt             AuraDefinitionBundle  force-app/main/default/aura/AccountsLoaded/AccountsLoaded.evt
    Add    AccountsLoaded/AccountsLoaded.evt             AuraDefinitionBundle  force-app/main/default/aura/AccountsLoaded/AccountsLoaded.evt-meta.xml
    Add    AccountCtrl                                   ApexClass             force-app/main/default/classes/AccountCtrl.cls
    Add    AccountCtrl                                   ApexClass             force-app/main/default/classes/AccountCtrl.cls-meta.xml
    Add    Account-Account %28Marketing%29 Layout        Layout                force-app/main/default/layouts/Account-Account %28Marketing%29 Layout.layout-meta.xml
    Add    Account-Account %28Sales%29 Layout            Layout                force-app/main/default/layouts/Account-Account %28Sales%29 Layout.layout-meta.xml
    Add    Account-Account %28Support%29 Layout          Layout                force-app/main/default/layouts/Account-Account %28Support%29 Layout.layout-meta.xml
    Add    Account-Account Layout                        Layout                force-app/main/default/layouts/Account-Account Layout.layout-meta.xml
    Add    Account.Location__c                           CustomField           force-app/main/default/objects/Account/fields/Location__c.field-meta.xml
    Add    Geolocation                                   PermissionSet         force-app/main/default/permissionsets/Geolocation.permissionset-meta.xml
    Add    Admin                                         Profile               force-app/main/default/profiles/Admin.profile-meta.xml
    Add    Custom%3A Marketing Profile                   Profile               force-app/main/default/profiles/Custom%3A Marketing Profile.profile-meta.xml
    Add    Custom%3A Sales Profile                       Profile               force-app/main/default/profiles/Custom%3A Sales Profile.profile-meta.xml
    Add    Custom%3A Support Profile                     Profile               force-app/main/default/profiles/Custom%3A Support Profile.profile-meta.xml
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/images/layers-2x.png
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/images/layers.png
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/images/marker-icon-2x.png
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/images/marker-icon.png
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/images/marker-shadow.png
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/leaflet-src.js
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/leaflet-src.js.map
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/leaflet.css
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/leaflet.js
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet/leaflet.js.map
    Add    leaflet                                       StaticResource        force-app/main/default/staticresources/leaflet.resource-meta.xml
    Add    Account_Locator                               CustomTab             force-app/main/default/tabs/Account_Locator.tab-meta.xml
    
    
      

    Assign the permset : Geolocation to Test Scratch org

    $ sfdx force:user:permset:assign -n Geolocation -u geolocationTestScratch1
    === Permsets Assigned
    USERNAME                       PERMISSION SET ASSIGNMENT
    ─────────────────────────────  ─────────────────────────
    test-huvk8zzifavh@example.com  Geolocation
      
    # Import data into Test Scratch org
    $ sfdx force:data:tree:import -f data/Account.json -u geolocationTestScratch1
    === Import Results
    REFERENCE ID  TYPE     ID
    ────────────  ───────  ──────────────────
    AccountRef1   Account  0015600000AHhFxAAL
    AccountRef2   Account  0015600000AHhFyAAL
    AccountRef3   Account  0015600000AHhFzAAL
    
    # Open up the Test Scratch Org
    $ sfdx force:org:open -u geolocationTestScratch1
    

    Geolocation Component in Test Scratch Org

    Install VS Code Extension for PMD CodeScan

    Inline CodeScan

    Extending SFDX - plugins

    
    
    $ sfdx plugins
    mc-sfdx-code-generator 0.0.1 (link)
    
    $ sfdx mc:code:gen -h 
    Usage: sfdx mc:code:gen
    
    create source from a template
    
    Flags:
     -n, --name NAME            (required) file or bundle name
     -d, --outputdir OUTPUTDIR  (required) folder for saving the created files
     -t, --template TEMPLATE    (required) code template name
     -v, --vars VARS            variables required by the template
    
    help text for mc:code:gen
      

    Webinar - Get Started with Salesforce DX!

    Webinar - Salesforce DX Product Manager AMA

    Webinar - Slides - Salesforce DX Product Manager AMA

    Video - Migrating to Salesforce DX

    Video - Salesforce DX - Continuous Integration and Continuous Delivery

    Video - From Change Sets to Salesforce DX: The Evolution of Collaboration

    Video - Copying Your Org's Shape into Scratch Orgs

    Video - Everything You Ever Wanted To Know About Scratch Orgs

    Videos - Get Your Job Done Faster: Performance Tips for Using the CLI

    Video - Using the Salesforce CLI for your Ant Migration Tool Tasks

    Videos - Become a Salesforce DX CLI Ninja

    Video - Adopting Salesforce DX

    Video - Simplify your code with Salesforce DX and module development

    Video - Top 10 Things to Know About Salesforce DX

    Video - Copying Your Org's Shape into Scratch Orgs

    Video - Test Automation and Continuous Integration In SDLC Using Salesforce DX

    References

    Metadata Coverage

    Metadata Coverage - Demo

    Scratch org: Manually enable : Organization Admins Can Login as Any User