Saying hello to the world is always fun, but you’re likely ready to do more. Let’s add two more commands to your plugin.
Call an External Service
This section shows how to create a new command called call external service. The command makes an HTTP request to an external service that returns an interesting fact about a number. The command has no flags other than the ones you get for “free” (--help, -h and --json). When you enter the service’s URL in your browser, it displays JSON with the interesting fact and some metadata about it.
Install Got, our recommended npm HTTP library, into your plugin by running this command:
1yarn add got
Because the new command makes HTTP requests, you need an HTTP library. While you can use any library you want, we recommend Got because it’s simple yet full featured.
In a terminal, change to the top-level directory of your plugin and run this command to generate the initial files, or scaffolding:
1sf dev generate command --name call:external:service
The command prompts if you want to overwrite the existing package.json file, enter y to update the file with information about the new command.
The --name flag specifies the full colon-separated name of the command you want to create. For example, if you want to create the command do awesome stuff, set --name to do:awesome:stuff.
The Typescript files contain import statements for the minimum required Salesforce libraries, and scaffold some basic code. The new type names come from the value you passed to the --name flag.
Open up src/commands/call/external/service.ts in your IDE, such as Visual Studio Code.
Add this import statement to the beginning of the file with the other import statements:
1import got from 'got';
Let’s now handle the JSON returned by our new command when a user runs it with the --json flag, which all CLI commands have by default. We want our command to return the JSON provided by the external service’s API. The underlying CLI framework uses the return type of the command class’ run method, which in this case is CallExternalServiceResult. So replace the code for CallExternalServiceResult to reflect the service’s JSON:
Let’s next work with the command flags. Remember that our command doesn’t have any flags beyond the default ones, so remove this code inside the CallExternalService class which declares a flag that we no longer need:
Then remove the Flags import from @salesforce/sf-plugins-core at the beginning of the file. After removing Flags from the import statement, the import looks like this:
The final coding step is to update the run method inside the CallExternalService class. This method is where you put the logic for your command. In our case, we want our command to make an HTTP GET request to the service API and return the result. Replace the run method with this code:
In the preceding code, this.log logs the interesting fact to the terminal only if the –json flag is not provided. In other words, the underlying CLI framework is smart enough to know whether to log non-JSON output, like strings, to the terminal.
We’re ready to test! Open a Terminal, either in VS Code or outside, and run the command using bin/dev.js.
1bin/dev.js call external service
Hopefully you see an interesting fact like this:
54 is the number of countries in Africa.
Let’s try the freebie flags, the ones that are magically available even though you didn’t explicitly add them. First get help for the command:
1bin/dev.js call external service --help
The help message isn’t that useful yet because you haven’t updated the boilerplate content in messages/call.external.service.md. We show an example of updating the help in the next example. Now run the command again but get JSON output instead of the default human-readable:
1bin/dev.js call external service --json
Good job adding a new command! Now let’s create something more Salesforce-y.
Connect to a Salesforce Org
Next create a more complex command that connects to a Salesforce org and displays a list of all Account names and IDs.
The command has one new flag, --target-org, with short name -o. Use the flag to specify the username or alias of an org you’ve previously logged into, also known as authorized, with the login org command. Do you recognize the flag? It’s a standard flag that many of the core Salesforce CLI commands use, such as project deploy start. You can use the flag too, including its existing code to connect to an org. Let’s give it a try.
In a terminal, change to the top-level directory of your plugin and run this command to generate the initial command files; answer y to overwrite the package.json file:
1sf dev generate command --name connect:org
Here’s the list of generated files:
src/commands/connect/org.ts
messages/connect.org.md
test/command/connect/org.nut.ts
test/command/connect/org.test.ts
Run this interactive command to generate a flag. In this context, “generate” means update the existing command files with the required information for the new --target-org flag:
1sf dev generate flag
Be sure you specify these properties of the new flag when prompted:
Command to add a new flag: connect org
Type: requiredOrg
Use standard definition: Y
Flag name: --target-org (default)
The command updates two files: src/commands/connect/org.ts with the new flag code and messages/connect.org.md with a new entry for the flag’s summary, which is displayed when you run the command with the --help flag.
Open src/commands/connect/org.ts in your IDE and review the new generated code for the --target-org flag:
1'target-org': Flags.requiredOrg(),
Because we’re using existing code for the flag, the single line of code is all you need – magic!
Because org.ts still contains code for the --name flag, which was added automatically when you originally generated the command, remove it now, just to keep things tidy. This is the code you can remove from public static readonly flags:
The new flag is ready! Let’s now code the command functionality.
Update the command’s return type (ConnectOrgResult) with this code:
1export type ConnectOrgResult = Array<{Name: string; Id: string}>;
Update the run method with this code:
1public async run(): Promise<ConnectOrgResult>{2 // parse the provided flags3 const {flags} = await this.parse(ConnectOrg);45 // Get the orgId from the Org instance stored in the `target-org` flag6 const orgId = flags['target-org'].getOrgId();7 // Get the connection from the Org instance stored in the `target-org` flag8 const connection = flags['target-org'].getConnection();910 this.log(`Connected to ${flags['target-org'].getUsername()} (${orgId}) with API version ${connection.version}`);1112 // Use the connection to execute a SOQL query against the org13 const result = await connection.query<{ Name: string; Id: string}>('SELECT Id, Name FROM Account');1415 // Log the results16 if(result.records.length>0){17 this.log('Found the following Accounts:');18 for(const record of result.records){19 this.log(` • ${record.Name}: ${record.Id}`);20}21}else{22 this.log('No Accounts found.');23}2425 return result.records;26}
Your new command is now ready to test!
If you haven’t already done so, log in to your test org with the org login web command. Run org list to see the orgs you’ve previously logged into and their associated usernames and aliases. We’ll use the username or alias to test your new command.
Logging into an org before you work with it is standard Salesforce CLI practice. Think about project deploy start: it too takes a username (also via the --target-org flag) of the org you want to deploy to, and it’s assumed that you’ve previously logged into it.
Test your new command with bin/dev.js; replace <org-username-or-alias> with the username or alias of the org you just logged into:
Hopefully you see output similar to this (the record IDs have been truncated for security):
1Connected to<your-org-username>(<your-org-id>) with API version 64.02Found the following Accounts:`3 • Acme: 001B...`4 • salesforce.com: 001B...`5 • Global Media: 001B...6 • TestAccount: 001B...7 • xyz: 001B...8 • Test1: 001B...