diff --git a/docs/docs/contribute/contributing-guide.mdx b/docs/docs/contribute/contributing-guide.mdx index 9bf17d0364d..9c0d0f243ad 100644 --- a/docs/docs/contribute/contributing-guide.mdx +++ b/docs/docs/contribute/contributing-guide.mdx @@ -29,7 +29,7 @@ Typos are embarrassing! Most PRs that fix typos will be accepted immediately. To - **DO** keep discussions focused. When a new or related topic comes up, it's often better to create a new issue than to side-track the conversation. - **DO NOT** submit PRs for coding style changes. These changes can cause unnecessary conflicts and make the review process more complicated. - **DO NOT** surprise us with big PRs. Instead, file an issue and start a discussion so we can agree on a direction before you invest a large amount of time. -- **DO NOT** commit code you didn't write. We encourage you to contribute your own work to maintain +- **DO NOT** commit code you didn't write. We encourage you to contribute your own work to maintain project quality and integrity. ## Contribution paths diff --git a/docs/docs/contribute/expect-during-pr.mdx b/docs/docs/contribute/expect-during-pr.mdx index 8fb9c57b87d..eaee087e06a 100644 --- a/docs/docs/contribute/expect-during-pr.mdx +++ b/docs/docs/contribute/expect-during-pr.mdx @@ -10,6 +10,7 @@ This article aims to provide an understanding of how we review pull requests, wh Once you submit a PR, one of our maintainers will examine it and check if the PR build has passed. The automated checks include: +- Successful docs build - Successful code build - Passing automated tests with 100% coverage - Absence of issues caught by our linter (ensuring adherence to naming conventions and code consistency) diff --git a/docs/docs/contribute/new-command/build-command-logic.mdx b/docs/docs/contribute/new-command/build-command-logic.mdx index 2e1e8006e1a..8b9729049fd 100644 --- a/docs/docs/contribute/new-command/build-command-logic.mdx +++ b/docs/docs/contribute/new-command/build-command-logic.mdx @@ -46,11 +46,21 @@ class SpoGroupGetCommand extends SpoCommand { export default new SpoGroupGetCommand(); ``` +Each command in the CLI for Microsoft 365 follows a consistent class naming convention to ensure clarity and maintainability. The command class name is based on the command's name and follows PascalCase. It is composed of the command's hierarchy segments joined together, followed by the suffix Command. For example, for the command `spo group get`, the class name would be `SpoGroupGetCommand`. + +:::warning + +To prevent an uncontrolled expansion of command names, we maintain a dictionary of approved command words that can be used when defining a command. This dictionary is located in the [`eslint.config.mjs`](https://github.com/pnp/cli-microsoft365/blob/main/eslint.config.mjs) file. + +If a desired command name includes a word that isn't in the approved list, that word should be added to the dictionary to allow the automated workflow to pass successfully. + +::: + 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](https://github.com/pnp/cli-microsoft365/blob/main/src/m365/base/SpoCommand.ts) base class. This class contains several helper methods to simplify your implementation. ### Include command name -When you create the minimum file, you'll get an error about a nonexistent type within `commands`. This is correct because we haven't defined the name of the command yet. Let's add this to the `commands` export located in `src/m365/spo/commands.ts`: +When you create the minimum file, you'll get an error about a nonexistent type within `commands`. This is correct because we haven't defined the name of the command yet. Let's add this to the `commands` export located in `src/m365/spo/commands.ts`. Note that all commands in this file are sorted strictly **alphabetically**. ```ts title="src/m365/spo/commands.ts" const prefix: string = 'spo'; @@ -232,7 +242,7 @@ enum AssociatedGroup { export const options = globalOptionsZod .extend({ webUrl: zod.alias('u', z.string().refine(url => validation.isValidSharePointUrl(url) === true, url => ({ - message: `Specified URL ${url} is not a valid SharePoint URL`, + message: `Specified URL '${url}' is not a valid SharePoint URL.`, }))), id: zod.alias('i', z.number().optional()), name: z.string().optional(), @@ -254,14 +264,14 @@ class SpoGroupGetCommand extends SpoCommand { public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { return schema - .refine(options => options.id !== undefined || options.name !== undefined && !(options.id !== undefined && options.name !== undefined), { + .refine(options => [options.id, options.name].filter(x => x !== undefined).length === 1, { message: `Either id or name is required, but not both.` }); } public async commandAction(logger: Logger, args: CommandArgs): Promise { if (this.verbose) { - await logger.logToStderr(`Retrieving information for group in site at ${args.options.webUrl}...`); + await logger.logToStderr(`Retrieving information for group in site at '${args.options.webUrl}'...`); } // Command implementation goes here @@ -317,4 +327,4 @@ If this command fails, be sure to check if your environment has been set up corr ## Next step -Now that the command is fully functional we will need to add some tests to ensure that the command works as expected. This will be explained in the next chapter: [Unit Tests](./unit-tests.mdx). \ No newline at end of file +Now that the command is fully functional we will need to add some tests to ensure that the command works as expected. This will be explained in the next chapter: [Unit Tests](./unit-tests.mdx). diff --git a/docs/docs/contribute/new-command/unit-tests.mdx b/docs/docs/contribute/new-command/unit-tests.mdx index 8b3e0442a81..7bd6b580c0b 100644 --- a/docs/docs/contribute/new-command/unit-tests.mdx +++ b/docs/docs/contribute/new-command/unit-tests.mdx @@ -46,7 +46,7 @@ With the basis of `Mocha` ready, we will set up the basis for `Sinon`. This will Before we start with the test suite, we want to make sure that the basic functions like `telemetry` and `authentication` are ignored. This will make it so functions like `restoreAuth` return a response when our code, `group-get.ts`, requires it. -```ts title="group-get.spec.ts" +```ts title="src/m365/spo/commands/group/group-get.spec.ts" // ... import sinon from 'sinon'; import auth from '../../../../Auth.js'; @@ -221,7 +221,7 @@ describe(commands.GROUP_GET, () => { }; sinon.stub(request, 'get').callsFake(async opts => { - if (opts.url!.endsWith('/_api/web/AssociatedOwnerGroup')) { + if (opts.url === 'https://contoso.sharepoint.com/sites/Marketing/_api/web/AssociatedOwnerGroup') { return ownerGroupResponse; } @@ -230,6 +230,7 @@ describe(commands.GROUP_GET, () => { await command.action(logger, { options: { + webUrl: 'https://contoso.sharepoint.com/sites/Marketing', associatedGroup: 'Owner' } }); @@ -253,10 +254,11 @@ describe(commands.GROUP_GET, () => { // ... it('handles errors correctly', async () => { - sinon.stub(request, 'get').rejects({error: { error: { message: 'An error has occured' } } }); + sinon.stub(request, 'get').rejects({error: { error: { message: 'An error has occurred' } } }); await assert.rejects(command.action(logger, { options: { + webUrl: 'https://contoso.sharepoint.com/sites/Marketing', associatedGroup: 'Visitor' } }), new CommandError('An error has occurred')); diff --git a/docs/docs/contribute/new-command/writing-the-docs.mdx b/docs/docs/contribute/new-command/writing-the-docs.mdx index bd7065dff92..9ffd584d010 100644 --- a/docs/docs/contribute/new-command/writing-the-docs.mdx +++ b/docs/docs/contribute/new-command/writing-the-docs.mdx @@ -28,7 +28,7 @@ npm start With our help file created, we can start writing down the command specs. A help file for a command will have at minimum the following topics. The command name as the title, a description, the usage, options, a few examples on how to use them, and a sample response output. We'll start with the `title`, `description`, and `usage`. -````md title="docs\docs\cmd\spo\group\group-get.mdx" +````md title="docs/docs/cmd/spo/group/group-get.mdx" # spo group get Gets site group @@ -44,7 +44,7 @@ m365 spo group get [options] Most commands will have unique options but every command will make use of our global options. This can be achieved by including `` under the heading options. This will include the global options in the help page. Before we can use the global options tag, we need to import it from `/docs/cmd/_global.mdx`. This can be done by adding the following import at the top of your help file. -````md title="docs\docs\cmd\spo\group\group-get.mdx" +````md title="docs/docs/cmd/spo/group/group-get.mdx" import Global from '/docs/cmd/_global.mdx'; @@ -86,7 +86,7 @@ node ./scripts/generate-docs-permissions.mjs The script above will output the permissions section, you can find an example below. -```md title="docs\docs\cmd\spo\group\group-get.mdx" +```md title="docs/docs/cmd/spo/group/group-get.mdx" import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -116,7 +116,7 @@ import TabItem from '@theme/TabItem'; As they say, a picture is worth a thousand words, same goes for examples of code. This is why we strive to add several examples for each command with some logical option data. This gives more insight to users on how they can implement the command in their script. We require at least 2 examples per command (if possible). -````md title="docs\docs\cmd\spo\group\group-get.mdx" +````md title="docs/docs/cmd/spo/group/group-get.mdx" ## Examples Get a specific group by its ID @@ -147,7 +147,7 @@ Some pointers to keep in mind when including sample data in our examples. We include the response output for each command to give more insight into what users can expect from this command. This is especially useful for commands that return a lot of data. We include the response output in the following formats: JSON, Text, CSV, and Markdown. This is done by using the `Tabs` component from the Docusaurus library. This component allows us to include multiple tabs with different content. The `TabItem` component is used to include the content for each tab. The `value` attribute is used to specify the name of the tab. Make sure to include the `Tabs` and `TabItem` imports at the top of your help file. -````md title="docs\docs\cmd\spo\group\group-get.mdx" +````md title="docs/docs/cmd/spo/group/group-get.mdx" import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -197,7 +197,7 @@ import TabItem from '@theme/TabItem'; ```csv Id,IsHiddenInUI,LoginName,Title,PrincipalType,AllowMembersEditMembership,AllowRequestToJoinLeave,AutoAcceptRequestToJoinLeave,Description,OnlyAllowMembersViewMembership,OwnerTitle,RequestToJoinLeaveEmailSetting - 1,,Contoso Visitors,Contoso Visitors,8,,,,Contoso Visitors,,Contoso Owners, + 1,0,Contoso Visitors,Contoso Visitors,8,0,0,0,Contoso Visitors,0,Contoso Owners, ``` @@ -233,30 +233,26 @@ import TabItem from '@theme/TabItem'; Each different verb of command can return a different type of response. This means that a `get` command will return a single object response while a `list` command returns an object list. Below you'll find different examples for different scenarios. - **A single object response** (planner plan get) - -[The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-get/) -[The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-get.mdx?plain=1) + - [The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-get/) + - [The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-get.mdx?plain=1) - **An object list response** (planner plan list) + - [The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-list/) + - [The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-list.mdx?plain=1) -[The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-list/) -[The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-list.mdx?plain=1) - -:::warning + :::warning -Make sure the `Text` output is also in the list format with a single object. + Make sure the `Text` output is also in the list format with a single object. -::: + ::: - **Multiple responses based on the options used** (planner plan add) - -[The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-add/) -[The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-add.mdx?plain=1) + - [The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-add/) + - [The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-add.mdx?plain=1) - **No response** (planner plan remove) - -[The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-remove/) -[The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-remove.mdx?plain=1) + - [The docs article](https://pnp.github.io/cli-microsoft365/cmd/planner/plan/plan-remove/) + - [The MarkDown file location](https://github.com/pnp/cli-microsoft365/blob/main/docs/docs/cmd/planner/plan/plan-remove.mdx?plain=1) Some general pointers to keep in mind when writing the verbose output. @@ -277,7 +273,7 @@ It is possible that a command needs to include some more information. Some addit This heading can be used to communicate some important details about your command to the user. This could be to communicate that you used a preview API or that a number option is specified as a 0-based index. Remarks headings are most commonly placed between the headings `Options` and `Examples`. -```md title="graph/schemaextension/schemaextension-list.mdx" +```md title="docs/docs/cmd/graph/schemaextension/schemaextension-list.mdx" ## Remarks pageNumber is specified as a 0-based index. A value of 2 returns the third page of items. @@ -287,7 +283,7 @@ pageNumber is specified as a 0-based index. A value of 2 returns the third page Here we can include some links to the APIs we used in the command or some documentation pages that explain the command usage in more detail. This isn't required but it is nice to have. This heading is most commonly placed at the end of your help page. -```md title="spo/homesite/homesite-get.mdx" +```md title="docs/docs/cmd/spo/homesite/homesite-get.mdx" ## More information - SharePoint home sites: a landing for your organization on the intelligent intranet: https://techcommunity.microsoft.com/t5/Microsoft-SharePoint-Blog/SharePoint-home-sites-a-landing-for-your-organization-on-the/ba-p/621933 @@ -295,7 +291,7 @@ Here we can include some links to the APIs we used in the command or some docume ## Include into the command navigation -Now that your page is finished, we need to make it available from the command navigation, most commonly found on the left side of the page. To include this, we need to edit the file `sidebars.js` found in the folder `src/config`. Navigate through the section `commands` and locate your commands command group. Here you can add the path to your new help page. +Now that your page is finished, we need to make it available from the command navigation, most commonly found on the left side of the page. To include this, we need to edit the file `sidebars.js` found in the folder `src/config`. Navigate through the section `commands` and locate your commands command group. Here you can add the path to your new help page. Note that all navigation nodes are somewhat sorted alphabetically. ```js title="src/config/sidebars.js" const sidebars = {