Skip to content

Commit

Permalink
Refactor Commands to Use Cobra For Flag and Argument Passing
Browse files Browse the repository at this point in the history
Use cobra for flag and argument passing to make commands more
consistent.

Add global --account flag to use an account named on the command line
rather than the current active account.

Update bulk commands to only support sub-command mode.

Generate documentation from cobra.

Add flags to logins command filter logins by user id and org id.

Allow :: to separate class and method in `force test`.
  • Loading branch information
cwarden committed Oct 12, 2022
1 parent c5c1a07 commit 7242507
Show file tree
Hide file tree
Showing 142 changed files with 4,910 additions and 2,603 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ $(basename $(WINDOWS)).zip: $(WINDOWS)
zip $@ $<
7za rn $@ $< $(EXECUTABLE)

docs:
go run docs/mkdocs.go

dist: test $(addsuffix .zip,$(basename $(ALL)))

fmt:
Expand Down
88 changes: 56 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,62 @@ Can be downloaded from the [Current Release Page](https://github.com/ForceCLI/fo

### Usage

Usage: force <command> [<args>]

Available commands:
login Log in to force.com
logout Log out from force.com
logins List force.com logins used
active Show or set the active force.com account
whoami Show information about the active account
describe Describe the object or list of available objects
sobject Manage standard & custom objects
field Manage sobject fields
record Create, modify, or view records
bulk Load csv file use Bulk API
fetch Export specified artifact(s) to a local directory
import Import metadata from a local directory
export Export metadata to a local directory
query Execute a SOQL statement
apex Execute anonymous Apex code
log Fetch debug logs
oauth Manage ConnectedApp credentials
test Run apex tests
security Displays the OLS and FLS for a given SObject
version Display current version
apiversion Set the Salesforce API version metadata is pulled with
push Deploy single artifact from a local directory
aura Retrieve or deploy Aura components
password See password status or reset password
notify Should notifications be used
limits Display current limits
help Show this help

Run 'force help [command]' for details.
See [docs/force.md](docs/force.md) for all supported commands.

Tab completion simplifies use of the force CLI.
Enable bash completion or see `force completion --help` for other options.

```
$ source <(force completion bash)
```

Usage:
force [command]

Available Commands:
active Show or set the active force.com account
apex Execute anonymous Apex code
apiversion Display/Set current API version
bigobject Manage big objects
bulk Load csv file or query data using Bulk API
completion Generate the autocompletion script for the specified shell
create Creates a new, empty Apex Class, Trigger, Visualforce page, or Component.
datapipe Manage DataPipes
describe Describe the object or list of available objects
eventlogfile List and fetch event log file
export Export metadata to a local directory
fetch Export specified artifact(s) to a local directory
field Manage SObject fields
help Help about any command
import Import metadata from a local directory
limits Display current limits
log Fetch debug logs
login force login [-i=<instance>] [<-u=username> <-p=password>] [-scratch] [-s]
logins List force.com logins used
logout Log out from Force.com
notify Should notifications be used
oauth Manage ConnectedApp credentials
open Open a browser window, logged into an authenticated Salesforce org
package Manage installed packages
password See password status or reset password
push Deploy metadata from a local directory
query Execute a SOQL statement
quickdeploy Quick deploy validation id
record Create, modify, or view records
rest Execute a REST request
security Displays the OLS and FLS for a given SObject
sobject Manage standard & custom objects
test Run apex tests
trace Manage trace flags
usedxauth Authenticate with SFDX Scratch Org User
version Display current version
whoami Show information about the active account

Flags:
-a, --account username account username to use
-h, --help help for force

Use "force [command] --help" for more information about a command.

### login
When you login using the CLI a record of the login is saved. Eventually your token will expire requiring re-authentication. The default login is for all production instances of salesforce.com. Two predefined non-production instances are available using the test and pre aliases. You can set an arbitrary instance to log in to by specifying the instance url in the form of subdomain.domain. For example login-blitz.soma.salesforce.com.
Expand Down
117 changes: 54 additions & 63 deletions command/active.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,81 +8,72 @@ import (

. "github.com/ForceCLI/force/config"
. "github.com/ForceCLI/force/error"
. "github.com/ForceCLI/force/lib"
"github.com/spf13/cobra"
)

var cmdActive = &Command{
Usage: "active [account]",
Short: "Show or set the active force.com account",
Long: `
Set the active force.com account
Examples:
func init() {
activeCmd.Flags().BoolP("json", "j", false, "output in JSON format")
activeCmd.Flags().BoolP("local", "l", false, "set active account locally, for current directory")
activeCmd.Flags().BoolP("session", "s", false, "output session id")
RootCmd.AddCommand(activeCmd)
}

var activeCmd = &cobra.Command{
Use: "active [account]",
Short: "Show or set the active force.com account",
Long: "Get or set the active force.com account",
Example: `
force active
force active user@example.org
Options:
-json, -j Output in JSON format
-local, -l Set active account locally, for current directory
-session, -s Output session id
`,
MaxExpectedArgs: 1,
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
printCurrentAccount(cmd)
return
}
local, _ := cmd.Flags().GetBool("local")
setAccount(args[0], local)
},
}
var (
tojson bool
account string
local bool
sessionId bool
)

func init() {
cmdActive.Flag.BoolVar(&tojson, "j", false, "output to json")
cmdActive.Flag.BoolVar(&tojson, "json", false, "output to json")
cmdActive.Flag.StringVar(&account, "a", "", "deprecated: set active account")
cmdActive.Flag.StringVar(&account, "account", "", "deprecated: set active account")
cmdActive.Flag.BoolVar(&local, "l", false, "set active account for current directory")
cmdActive.Flag.BoolVar(&local, "local", false, "set active account for current directory")
cmdActive.Flag.BoolVar(&sessionId, "session", false, "display session id")
cmdActive.Flag.BoolVar(&sessionId, "s", false, "display session id")
cmdActive.Run = runActive
func printCurrentAccount(cmd *cobra.Command) {
if force == nil {
ErrorAndExit("No active session")
}
creds := force.Credentials
if creds == nil || creds.UserInfo == nil {
ErrorAndExit("No active session")
}
sessionId, _ := cmd.Flags().GetBool("session")
tojson, _ := cmd.Flags().GetBool("json")
if sessionId {
fmt.Println(creds.AccessToken)
} else if tojson {
fmt.Printf(fmt.Sprintf("{ \"login\": \"%s\", \"instanceUrl\": \"%s\", \"namespace\":\"%s\" }", creds.SessionName(), creds.InstanceUrl, creds.UserInfo.OrgNamespace))
} else {
fmt.Println(fmt.Sprintf("%s - %s - ns:%s", creds.SessionName(), creds.InstanceUrl, creds.UserInfo.OrgNamespace))
}
}

func runActive(cmd *Command, args []string) {
if account == "" && len(args) == 0 {
creds, err := ActiveCredentials(true)
if err != nil {
ErrorAndExit(err.Error())
}
if sessionId {
fmt.Println(creds.AccessToken)
} else if tojson {
fmt.Printf(fmt.Sprintf("{ \"login\": \"%s\", \"instanceUrl\": \"%s\", \"namespace\":\"%s\" }", creds.SessionName(), creds.InstanceUrl, creds.UserInfo.OrgNamespace))
func setAccount(account string, local bool) {
accounts, _ := Config.List("accounts")
i := sort.SearchStrings(accounts, account)
if i < len(accounts) && accounts[i] == account {
if runtime.GOOS == "windows" {
cmd := exec.Command("title", account)
cmd.Run()
} else {
fmt.Println(fmt.Sprintf("%s - %s - ns:%s", creds.SessionName(), creds.InstanceUrl, creds.UserInfo.OrgNamespace))
}
} else {
if account == "" {
account = args[0]
title := fmt.Sprintf("\033];%s\007", account)
fmt.Printf(title)
}
accounts, _ := Config.List("accounts")
i := sort.SearchStrings(accounts, account)
if i < len(accounts) && accounts[i] == account {
if runtime.GOOS == "windows" {
cmd := exec.Command("title", account)
cmd.Run()
} else {
title := fmt.Sprintf("\033];%s\007", account)
fmt.Printf(title)
}
fmt.Printf("%s now active\n", account)
if local {
Config.SaveLocal("current", "account", account)
} else {
Config.SaveGlobal("current", "account", account)
}
fmt.Printf("%s now active\n", account)
if local {
Config.SaveLocal("current", "account", account)
} else {
ErrorAndExit(fmt.Sprintf("no such account %s\n", account))
Config.SaveGlobal("current", "account", account)
}
} else {
ErrorAndExit(fmt.Sprintf("no such account %s\n", account))
}
}
112 changes: 61 additions & 51 deletions command/apex.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,72 +6,82 @@ import (
"os"

. "github.com/ForceCLI/force/error"
. "github.com/ForceCLI/force/lib"
"github.com/spf13/cobra"
)

var cmdApex = &Command{
Run: runApex,
Usage: "apex [file]",
Short: "Execute anonymous Apex code",
Long: `
Execute anonymous Apex code
Apex Options
-test Run in test context
Examples:
func init() {
apexCmd.Flags().BoolP("test", "t", false, "run in test context")
RootCmd.AddCommand(apexCmd)
}

var apexCmd = &cobra.Command{
Use: "apex [file]",
Short: "Execute anonymous Apex code",
Example: `
force apex ~/test.apex
force apex
>> Start typing Apex code; press CTRL-D(for Mac/Linux) / Ctrl-Z (for Windows) when finished
`,
MaxExpectedArgs: 2,
}

func init() {
cmdApex.Flag.BoolVar(&testContext, "test", false, "run apex from in a test context")
`,
Run: func(cmd *cobra.Command, args []string) {
testContext, _ := cmd.Flags().GetBool("test")
switch len(args) {
case 1:
runApexInFile(args[0], testContext)
case 0:
runApexFromStdin(testContext)
default:
fmt.Println("Got test indication. DEPRECATED.")
getTestCoverage(args[1])
}
},
}

var (
testContext bool
)

func runApex(cmd *Command, args []string) {
var code []byte
var err error
if len(args) == 1 {
code, err = ioutil.ReadFile(args[0])
} else if len(args) > 1 {
fmt.Println("Got test indication.")
func runApexFromStdin(testContext bool) {
fmt.Println(">> Start typing Apex code; press CTRL-D(for Mac/Linux) / Ctrl-Z (for Windows) when finished")
code, err := ioutil.ReadAll(os.Stdin)
fmt.Println("\n\n>> Executing code...")
var output string
if testContext {
output, err = executeAsTest(code)
} else {
fmt.Println(">> Start typing Apex code; press CTRL-D(for Mac/Linux) / Ctrl-Z (for Windows) when finished")
code, err = ioutil.ReadAll(os.Stdin)
fmt.Println("\n\n>> Executing code...")
output, err = execute(code)
}
if err != nil {
ErrorAndExit(err.Error())
}
fmt.Println(output)
}

func runApexInFile(filename string, testContext bool) {
code, err := ioutil.ReadFile(filename)
if err != nil {
ErrorAndExit(err.Error())
}
force, _ := ActiveForce()
var output string
if testContext {
output, err := force.Partner.ExecuteAnonymousTest(string(code))
if err != nil {
ErrorAndExit(err.Error())
}
fmt.Println(output)
} else if len(args) <= 1 {
output, err := force.Partner.ExecuteAnonymous(string(code))
if err != nil {
ErrorAndExit(err.Error())
}
fmt.Println(output)
output, err = executeAsTest(code)
} else {
apexclass := args[1]
fmt.Println(apexclass)
err := force.GetCodeCoverage("", apexclass)
if err != nil {
ErrorAndExit(err.Error())
}
output, err = execute(code)
}
if err != nil {
ErrorAndExit(err.Error())
}
fmt.Println(output)
}

func executeAsTest(code []byte) (string, error) {
return force.Partner.ExecuteAnonymousTest(string(code))
}

func execute(code []byte) (string, error) {
return force.Partner.ExecuteAnonymous(string(code))
}

func getTestCoverage(apexclass string) {
fmt.Println(apexclass)
err := force.GetCodeCoverage("", apexclass)
if err != nil {
ErrorAndExit(err.Error())
}
}
Loading

0 comments on commit 7242507

Please sign in to comment.