-
Notifications
You must be signed in to change notification settings - Fork 328
Adding a command
The following article describes how to add a new command to the CLI for Microsoft 365.
Each command consists of three files:
- command implementation, located under ./src/m365/[service]/commands, eg. ./src/m365/spo/commands/login.ts
- command unit tests, located under ./src/m365/[service]/commands, eg. ./src/m365/spo/commands/login.spec.ts
- command documentation page, located under ./docs/docs/cmd/[service], eg. ./docs/docs/cmd/spo/login.mdx
Additionally, each command is listed in:
- list of all commands for the given service, located in ./src/m365/[service]/commands.ts, eg. ./src/m365/spo/commands.ts
- the documentation table of contents, located in ./docs/src/config/sidebars.js
Commands are organized by the Microsoft 365 service, such as SharePoint Online (spo), that they apply to. Before building your command, find the right folder corresponding to your command in the project structure.
In the ./src/m365/[service]/commands folder, create two files for your command: my-command.ts for the command implementation, and my-command.spec.ts for the unit tests.
In the ./src/m365/[service]/commands.ts file, define a constant with your command's name including the service prefix. You will use this constant to refer to the command in its implementation, unit tests, help, etc.
In the ./docs/docs/cmd/[service] folder, create a new file for your command's help page: my-command.mdx. Next, open the ./docs/src/config/sidebars.js file and add the reference to the my-command.mdx file in the table of contents.
The table of contents is organized alphabetically so that users can quickly find the command they are looking for.
Each command in the CLI for Microsoft 365 is defined as a class extending the Command base class. At minimum, a command must define name
, description
, and commandAction
:
import config from '../../../config';
import commands from '../commands';
import Command from '../../../Command';
class MyCommand extends Command {
public get name(): string {
return commands.MYCOMMAND;
}
public get description(): string {
return 'My command';
}
public async commandAction(cmd: CommandInstance, args: CommandArgs): Promise<void> {
// command implementation goes here
}
}
module.exports = new MyCommand();
Depending on your command and the service for which you're building the command, there might be a base class that you can use to simplify the implementation. For example for SPO, you can inherit from the SpoCommand base class. This class contains a number of helper methods simplifying your implementation.
The CLI for Microsoft 365 tracks the usage of the different commands using Azure Application Insights. By default, for each command the CLI tracks its name and whether it's been executed in debug/verbose mode or not. If your command has additional properties that should be included in the telemetry, you can define them by implementing the #initTelemetry method and adding your properties to the this.telemetryProperties object:
class SpoMyCommand extends Command {
constructor() {
super();
this.#initTelemetry();
// ...
}
// ...
#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
id: typeof args.options.id !== 'undefined',
name: typeof args.options.name !== 'undefined',
listTitle: typeof args.options.listTitle !== 'undefined',
listId: typeof args.options.listId !== 'undefined',
listUrl: typeof args.options.listUrl !== 'undefined'
});
});
}
// ...
}
Important: if your command requires URLs or other user-defined strings, you should not include them in the telemetry as these strings might include personal or confidential information that we shouldn't have access to.
If an error occurred while executing the command, throw the error message that should be displayed to the user:
class SpoMyCommand extends Command {
// ...
public async commandAction(cmd: CommandInstance, args: CommandArgs): Promise<void> {
// command implementation goes here
throw 'An error has occurred'; // notify that an error has occurred
}
// ...
}
Finish the implementation of your command, by exporting the instance of the command class:
module.exports = new SpoMyCommand();
On runtime, CLI for Microsoft 365 iterates through all JavaScript files in the m365 folder and registers all exported command classes as commands in the CLI.
When building CLI for Microsoft 365 commands, you can use additional features such as optional and required arguments, autocomplete, or validation.
Each command must be accompanied by a set of unit tests. We aim for 100% code and branch coverage in every command file.
To see the current code coverage, run
npm test
. Once testing completes, open the ./coverage/lcov-report/index.html file in the web browser and browser through the different project files.
See the existing test files to get a better understanding of how they are structured and how different elements such as objects or web requests are mocked. To run just your tests, either add .only
to your test suite (eg. describe.only(commands.MY_COMMAND)
) or update the glob in the .mocharc.json
file, spec
property to match the path to your test files, like dist/m365/aad/commands/approleassignment/**/*.spec.js
.
Once you're done with your unit tests, run npm test
to verify that you're covering all code and branches of your command with your unit tests.
For more information about working with tests in CLI for Microsoft 365, check out this awesome article by Martin Lingstuyl.
Each command has a corresponding manual page. The contents of this page are almost identical to the help implemented in the command itself. This way, users working with the CLI can get the help directly inside the CLI, while users interested in the capabilities of the CLI, can browse through the help pages published on the Internet.
In the CLI for Microsoft 365 we extend this basic information with additional remarks and examples to help users work with the CLI. The CLI will look in the ./docs/docs/cmd/[service] folder for your documentation file and output to users when requesting help for your command:
m365 spo my-command --help
If the project is still building, your command is working as expected, all unit tests are passing, you have 100% code coverage on your command file and the documentation is in place, you're ready to submit a PR.