From ff8cbad32609d2507b68aa0a9132565391b35733 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Wed, 9 Oct 2024 17:29:43 +0000 Subject: [PATCH 01/42] spe container get --- docs/docs/cmd/spe/container/container-get.mdx | 92 ++++++++++++++++ docs/src/config/sidebars.ts | 9 ++ src/m365/spe/commands.ts | 1 + .../commands/container/container-get.spec.ts | 100 ++++++++++++++++++ .../spe/commands/container/container-get.ts | 64 +++++++++++ 5 files changed, 266 insertions(+) create mode 100644 docs/docs/cmd/spe/container/container-get.mdx create mode 100644 src/m365/spe/commands/container/container-get.spec.ts create mode 100644 src/m365/spe/commands/container/container-get.ts diff --git a/docs/docs/cmd/spe/container/container-get.mdx b/docs/docs/cmd/spe/container/container-get.mdx new file mode 100644 index 00000000000..88c505aaa7e --- /dev/null +++ b/docs/docs/cmd/spe/container/container-get.mdx @@ -0,0 +1,92 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spe container get + +Gets a container of a specific container type + +## Usage + +```sh +m365 spe container get [options] +``` + +## Options + +```md definition-list +`-i, --id ` +: The Id of the container instance. +``` + + + +## Examples + +Gets a container of a specific type. + +```sh +m365 spe container get --id "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z" +``` + +## Response + + + + + ```json + { + "id": "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z", + "displayName": "My Application Storage Container", + "description": "Description of My Application Storage Container", + "containerTypeId": "91710488-5756-407f-9046-fbe5f0b4de73", + "status": "active", + "createdDateTime": "2021-11-24T15:41:52.347Z", + "settings": { + "isOcrEnabled": false + } + } + ``` + + + + + ```text + id : b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z + displayName : My Application Storage Container + description : Description of My Application Storage Container + containerTypeId : 91710488-5756-407f-9046-fbe5f0b4de73 + status : active + createdDateTime : 2021-11-24T15:41:52.347Z + ``` + + + + + ```csv + id,displayName,description,containerTypeId,status,createdDateTime + b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z,My Application Storage Container,Description of My Application Storage Container,91710488-5756-407f-9046-fbe5f0b4de73,active,2021-11-24T15:41:52.347Z + ``` + + + + + ```md + # spe container get --id "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z" + + Date: 14/03/2024 + + ## My Application Storage Container (b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z) + + Property | Value + ---------|------- + id | b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z + displayName | My Application Storage Container + description | Description of My Application Storage Container + containerTypeId | 91710488-5756-407f-9046-fbe5f0b4de73 + status | active + createdDateTime | 2021-11-24T15:41:52.347Z + ``` + + + diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index c995ffe5bdb..858f2189040 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1960,6 +1960,15 @@ const sidebars: SidebarsConfig = { }, { 'SharePoint Embedded (spe)': [ + { + container: [ + { + type: 'doc', + label: 'container get', + id: 'cmd/spe/container/container-get' + } + ] + }, { containertype: [ { diff --git a/src/m365/spe/commands.ts b/src/m365/spe/commands.ts index ecd05a6e954..f013be34c0b 100644 --- a/src/m365/spe/commands.ts +++ b/src/m365/spe/commands.ts @@ -1,6 +1,7 @@ const prefix: string = 'spe'; export default { + CONTAINER_GET: `${prefix} container get`, CONTAINERTYPE_ADD: `${prefix} containertype add`, CONTAINERTYPE_LIST: `${prefix} containertype list` }; \ No newline at end of file diff --git a/src/m365/spe/commands/container/container-get.spec.ts b/src/m365/spe/commands/container/container-get.spec.ts new file mode 100644 index 00000000000..16fd757ac9a --- /dev/null +++ b/src/m365/spe/commands/container/container-get.spec.ts @@ -0,0 +1,100 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './container-get.js'; + +describe(commands.CONTAINER_GET, () => { + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.CONTAINER_GET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('correctly handles error', async () => { + const errorMessage = 'Bad request.'; + sinon.stub(request, 'get').rejects({ + error: { + message: errorMessage + } + }); + + await assert.rejects(command.action(logger, { options: { id: 'invalid', verbose: true } } as any), + new CommandError(errorMessage)); + }); + + it('gets container by id', async () => { + const containerId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIxNTU1MjcwOTQyNzIifQ'; + const response = { + id: containerId, + displayName: "My Application Storage Container", + description: "Description of My Application Storage Container", + containerTypeId: "91710488-5756-407f-9046-fbe5f0b4de73", + status: "active", + createdDateTime: "2021-11-24T15:41:52.347Z", + settings: { + isOcrEnabled: false + } + }; + + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/storage/fileStorage/containers/${containerId}`) { + return response; + } + + throw 'Invalid Request'; + }); + + await command.action(logger, { options: { id: containerId } } as any); + assert.deepStrictEqual(loggerLogSpy.lastCall.args[0], response); + }); +}); \ No newline at end of file diff --git a/src/m365/spe/commands/container/container-get.ts b/src/m365/spe/commands/container/container-get.ts new file mode 100644 index 00000000000..83241e31da5 --- /dev/null +++ b/src/m365/spe/commands/container/container-get.ts @@ -0,0 +1,64 @@ +import GlobalOptions from '../../../../GlobalOptions.js'; +import { Logger } from '../../../../cli/Logger.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; +import request, { CliRequestOptions } from '../../../../request.js'; + +interface CommandArgs { + options: Options; +} + +interface Options extends GlobalOptions { + id: string; +} + +class SpeContainerGetCommand extends GraphCommand { + public get name(): string { + return commands.CONTAINER_GET; + } + + public get description(): string { + return 'Gets a container of a specific container type'; + } + + constructor() { + super(); + + this.#initOptions(); + this.#initTypes(); + } + + #initOptions(): void { + this.options.unshift( + { option: '-i, --id ' } + ); + } + + #initTypes(): void { + this.types.string.push('id'); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (this.verbose) { + await logger.logToStderr(`Getting a container with id '${args.options.id}'...`); + } + + const requestOptions: CliRequestOptions = { + url: `${this.resource}/v1.0/storage/fileStorage/containers/${args.options.id}`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + + try { + const res = await request.get(requestOptions); + await logger.log(res); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new SpeContainerGetCommand(); \ No newline at end of file From 486deaf8c1f3dd524de9e63e3545058f19ffcbb2 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Mon, 25 Nov 2024 15:45:29 +0000 Subject: [PATCH 02/42] review comments --- src/m365/spe/commands/container/container-get.spec.ts | 1 - src/m365/spe/commands/container/container-get.ts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m365/spe/commands/container/container-get.spec.ts b/src/m365/spe/commands/container/container-get.spec.ts index 16fd757ac9a..ff34e2a3ec3 100644 --- a/src/m365/spe/commands/container/container-get.spec.ts +++ b/src/m365/spe/commands/container/container-get.spec.ts @@ -16,7 +16,6 @@ describe(commands.CONTAINER_GET, () => { let logger: Logger; let loggerLogSpy: sinon.SinonSpy; - before(() => { sinon.stub(auth, 'restoreAuth').resolves(); sinon.stub(telemetry, 'trackEvent').returns(); diff --git a/src/m365/spe/commands/container/container-get.ts b/src/m365/spe/commands/container/container-get.ts index 83241e31da5..bafbd4e5d84 100644 --- a/src/m365/spe/commands/container/container-get.ts +++ b/src/m365/spe/commands/container/container-get.ts @@ -3,6 +3,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { ContainerTypeProperties } from '../../ContainerTypeProperties.js'; interface CommandArgs { options: Options; @@ -52,7 +53,7 @@ class SpeContainerGetCommand extends GraphCommand { }; try { - const res = await request.get(requestOptions); + const res = await request.get(requestOptions); await logger.log(res); } catch (err: any) { From d233e8c3ae27928469000903f9f87bb44d707f36 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:34:27 +0200 Subject: [PATCH 03/42] Moves 'spo folder copy' to new endpoint. Closes #6310 --- docs/docs/cmd/spo/folder/folder-copy.mdx | 97 +++- docs/docs/v10-upgrade-guidance.mdx | 8 +- .../spo/commands/folder/folder-copy.spec.ts | 442 +++++++++++++----- src/m365/spo/commands/folder/folder-copy.ts | 92 ++-- 4 files changed, 457 insertions(+), 182 deletions(-) diff --git a/docs/docs/cmd/spo/folder/folder-copy.mdx b/docs/docs/cmd/spo/folder/folder-copy.mdx index 732b913e4c1..5d9c31dbe87 100644 --- a/docs/docs/cmd/spo/folder/folder-copy.mdx +++ b/docs/docs/cmd/spo/folder/folder-copy.mdx @@ -1,4 +1,6 @@ import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; # spo folder copy @@ -31,19 +33,14 @@ m365 spo folder copy [options] `--nameConflictBehavior [nameConflictBehavior]` : Behavior when a file or folder with the same name is already present at the destination. Allowed values: `fail`, `rename`. Defaults to `fail`. -`--resetAuthorAndCreated` -: Use this option to clear the author and created date. When not specified, the values from the source folder are used. - -`--bypassSharedLock` -: This indicates whether a folder with a shared lock can still be moved. Use this option to copy a folder that is locked. +`--skipWait` +: Don't wait for the copy operation to complete. ``` ## Remarks -When you copy a folder with documents that have version history, only the latest document version is copied. - When you specify a value for `nameConflictBehavior`, consider the following: - `fail` will throw an error when the destination folder already exists. @@ -69,16 +66,90 @@ Copy a folder to another location and use new name on conflict m365 spo folder copy --webUrl https://contoso.sharepoint.com/sites/project-x --sourceUrl "/sites/project-x/Shared Documents/Reports" --targetUrl "/sites/project-y/Shared Documents/Project files" --nameConflictBehavior rename ``` -Copy a folder referenced by its ID to another document library and reset author and created date +Copy a folder referenced by its ID to another document library and don't wait for the copy operation to finish ```sh -m365 spo folder copy --webUrl https://contoso.sharepoint.com/sites/project-x --sourceId b8cc341b-9c11-4f2d-aa2b-0ce9c18bcba2 --targetUrl "/sites/project-x/Project files" --retainEditorAndModified +m365 spo folder copy --webUrl https://contoso.sharepoint.com/sites/project-x --sourceId b8cc341b-9c11-4f2d-aa2b-0ce9c18bcba2 --targetUrl "/sites/project-x/Project files" --skipWait ``` ## Response -The command won't return a response on success. +### Standard Response + + + + + ```json + { + "Exists": true, + "ExistsAllowThrowForPolicyFailures": true, + "ExistsWithException": true, + "IsWOPIEnabled": false, + "ItemCount": 6, + "Name": "Company", + "ProgID": null, + "ServerRelativeUrl": "/sites/Sales/Icons/Company", + "TimeCreated": "2024-09-26T22:08:53Z", + "TimeLastModified": "2024-09-26T22:09:31Z", + "UniqueId": "d3a37396-ca16-467b-b968-48f5fc41f2b6", + "WelcomePage": "" + } + ``` + + + + + ```text + Exists : true + ExistsAllowThrowForPolicyFailures: true + ExistsWithException : true + IsWOPIEnabled : false + ItemCount : 6 + Name : Company + ProgID : null + ServerRelativeUrl : /sites/Sales/Icons/Company + TimeCreated : 2024-09-26T22:08:53Z + TimeLastModified : 2024-09-26T22:09:31Z + UniqueId : d3a37396-ca16-467b-b968-48f5fc41f2b6 + WelcomePage : + ``` + + + + + ```csv + Exists,ExistsAllowThrowForPolicyFailures,ExistsWithException,IsWOPIEnabled,ItemCount,Name,ProgID,ServerRelativeUrl,TimeCreated,TimeLastModified,UniqueId,WelcomePage + 1,1,1,0,6,Company,,/sites/Sales/Icons/Company,2024-09-26T22:08:53Z,2024-09-26T22:09:31Z,d3a37396-ca16-467b-b968-48f5fc41f2b6, + ``` + + + + + ```md + # spo folder copy --webUrl "https://contoso.sharepoint.com/sites/Marketing" --sourceUrl "/Logos/Contoso" --targetUrl "/sites/Sales/Logos" + + Date: 18/10/2024 + + ## Company (d3a37396-ca16-467b-b968-48f5fc41f2b6) + + Property | Value + ---------|------- + Exists | true + ExistsAllowThrowForPolicyFailures | true + ExistsWithException | true + IsWOPIEnabled | false + ItemCount | 6 + Name | Company + ServerRelativeUrl | /sites/Sales/Icons/Company + TimeCreated | 2024-09-26T22:08:53Z + TimeLastModified | 2024-09-26T22:09:31Z + UniqueId | d3a37396-ca16-467b-b968-48f5fc41f2b6 + WelcomePage | + ``` + + + + +### `skipWait` response -## More information - -- Copy items from a SharePoint document library: [https://support.office.com/en-us/article/move-or-copy-items-from-a-sharepoint-document-library-00e2f483-4df3-46be-a861-1f5f0c1a87bc](https://support.office.com/en-us/article/move-or-copy-items-from-a-sharepoint-document-library-00e2f483-4df3-46be-a861-1f5f0c1a87bc) +The command won't return a response on success. diff --git a/docs/docs/v10-upgrade-guidance.mdx b/docs/docs/v10-upgrade-guidance.mdx index a157e7c0367..acf2053e701 100644 --- a/docs/docs/v10-upgrade-guidance.mdx +++ b/docs/docs/v10-upgrade-guidance.mdx @@ -238,13 +238,13 @@ In the past versions of CLI for Microsoft 365, the command had no output. When u When using the [spo file move](./cmd/spo/file/file-move.mdx) command, please use the new command input. This means that you'll have to remove option `--retainEditorAndModified` from your scripts and automation tools. -### Updated command `spo folder move` +### Updated command `spo folder move` and `spo folder copy` -Because of some limitations of the current [spo folder move](./cmd/spo/folder/folder-move.mdx) command, we have decided to move it to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command input and output have changed. +Because of some limitations of the current commands [spo folder move](./cmd/spo/folder/folder-move.mdx) and [spo folder copy](./cmd/spo/folder/folder-copy.mdx), we have decided to move it to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command's input and output have changed. **Command options:** -Unfortunately, we had to drop the `--retainEditorAndModified` and `--bypassSharedLock` options as it's no longer supported by the new endpoint. In return, we were able to add a new option: +Unfortunately, we had to drop the `--retainEditorAndModified`, `--resetAuthorAndCreated` and `--bypassSharedLock` options as it's no longer supported by the new endpoint. In return, we were able to add a new option: - `--skipWait`: Don't wait for the move operation to complete. **Command output:** @@ -253,7 +253,7 @@ In the past versions of CLI for Microsoft 365, the command had no output. When u #### What action do I need to take? -When using the [spo folder move](./cmd/spo/folder/folder-move.mdx) command, please use the new command input. This means that you'll have to remove options `--retainEditorAndModified` and `--bypassSharedLock` from your scripts and automation tools. +When using these commands, please use the new command input. This means that you'll have to remove options `--retainEditorAndModified`, `--resetAuthorAndCreated` and `--bypassSharedLock` from your scripts and automation tools. ### Removed `spo folder rename` alias diff --git a/src/m365/spo/commands/folder/folder-copy.spec.ts b/src/m365/spo/commands/folder/folder-copy.spec.ts index 3893ce2139f..8ffad381133 100644 --- a/src/m365/spo/commands/folder/folder-copy.spec.ts +++ b/src/m365/spo/commands/folder/folder-copy.spec.ts @@ -11,22 +11,69 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import command from './folder-copy.js'; +import { CreateFolderCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js'; +import { settingsNames } from '../../../../settingsNames.js'; import { CommandError } from '../../../../Command.js'; -describe(commands.FOLDER_COPY, () => { - const folderName = 'Reports'; - const rootUrl = 'https://contoso.sharepoint.com'; - const webUrl = rootUrl + '/sites/project-x'; - const sourceUrl = '/sites/project-x/documents/' + folderName; - const targetUrl = '/sites/project-y/My Documents'; - const absoluteSourceUrl = rootUrl + sourceUrl; - const absoluteTargetUrl = rootUrl + targetUrl; - const sourceId = 'b8cc341b-9c11-4f2d-aa2b-0ce9c18bcba2'; +const sourceWebUrl = 'https://contoso.sharepoint.com/sites/Sales'; +const sourceFolderName = 'Logos'; +const sourceServerRelUrl = '/sites/Sales/Shared Documents/' + sourceFolderName; +const sourceSiteRelUrl = '/Shared Documents/' + sourceFolderName; +const sourceAbsoluteUrl = 'https://contoso.sharepoint.com' + sourceServerRelUrl; +const sourceFolderId = 'f09c4efe-b8c0-4e89-a166-03418661b89b'; + +const destWebUrl = 'https://contoso.sharepoint.com/sites/Marketing'; +const destSiteRelUrl = '/Documents'; +const destServerRelUrl = '/sites/Marketing' + destSiteRelUrl; +const destAbsoluteTargetUrl = 'https://contoso.sharepoint.com' + destServerRelUrl; +const destFolderId = '15488d89-b82b-40be-958a-922b2ed79383'; + +const copyJobInfo = { + EncryptionKey: '2by8+2oizihYOFqk02Tlokj8lWUShePAEE+WMuA9lzA=', + JobId: 'd812e5a0-d95a-4e4f-bcb7-d4415e88c8ee', + JobQueueUri: 'https://spoam1db1m020p4.queue.core.windows.net/2-1499-20240831-29533e6c72c6464780b756c71ea3fe92?sv=2018-03-28&sig=aX%2BNOkUimZ3f%2B%2BvdXI95%2FKJI1e5UE6TU703Dw3Eb5c8%3D&st=2024-08-09T00%3A00%3A00Z&se=2024-08-31T00%3A00%3A00Z&sp=rap', + SourceListItemUniqueIds: [ + sourceFolderId + ] +}; + +const copyJobResult = { + Event: 'JobFinishedObjectInfo', + JobId: '6d1eda82-0d1c-41eb-ab05-1d9cd4afe786', + Time: '08/10/2024 18:59:40.145', + SourceObjectFullUrl: sourceAbsoluteUrl, + TargetServerUrl: 'https://contoso.sharepoint.com', + TargetSiteId: '794dada8-4389-45ce-9559-0de74bf3554a', + TargetWebId: '8de9b4d3-3c30-4fd0-a9d7-2452bd065555', + TargetListId: '44b336a5-e397-4e22-a270-c39e9069b123', + TargetObjectUniqueId: destFolderId, + TargetObjectSiteRelativeUrl: destSiteRelUrl.substring(1), + CorrelationId: '5efd44a1-c034-9000-9692-4e1a1b3ca33b' +}; + +const destFolderResponse = { + Exists: true, + ExistsAllowThrowForPolicyFailures: true, + ExistsWithException: true, + IsWOPIEnabled: false, + ItemCount: 6, + Name: sourceFolderName, + ProgID: null, + ServerRelativeUrl: destServerRelUrl, + TimeCreated: '2024-09-26T20:52:07Z', + TimeLastModified: '2024-09-26T21:16:26Z', + UniqueId: '59abed95-34f9-470b-a133-ae8932480b53', + WelcomePage: '' +}; +describe(commands.FOLDER_COPY, () => { let log: any[]; let logger: Logger; let commandInfo: CommandInfo; - let postStub: sinon.SinonStub; + let loggerLogSpy: sinon.SinonSpy; + + let spoUtilCreateCopyJobStub: sinon.SinonStub; + let spoUtilGetCopyJobResultStub: sinon.SinonStub; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -36,6 +83,9 @@ describe(commands.FOLDER_COPY, () => { auth.connection.active = true; commandInfo = cli.getCommandInfo(command); + sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => settingName === settingsNames.prompt ? false : defaultValue); + spoUtilCreateCopyJobStub = sinon.stub(spo, 'createFolderCopyJob').resolves(copyJobInfo); + spoUtilGetCopyJobResultStub = sinon.stub(spo, 'getCopyJobResult').resolves(copyJobResult); }); beforeEach(() => { @@ -52,15 +102,8 @@ describe(commands.FOLDER_COPY, () => { } }; - postStub = sinon.stub(request, 'post').callsFake(async opts => { - if (opts.url === `${webUrl}/_api/SP.MoveCopyUtil.CopyFolderByPath`) { - return { - 'odata.null': true - }; - } - - throw 'Invalid request: ' + opts.url; - }); + loggerLogSpy = sinon.spy(logger, 'log'); + spoUtilGetCopyJobResultStub.resetHistory(); }); afterEach(() => { @@ -88,180 +131,314 @@ describe(commands.FOLDER_COPY, () => { }); it('fails validation if the webUrl option is not a valid SharePoint site URL', async () => { - const actual = await command.validate({ options: { webUrl: 'foo', sourceUrl: sourceUrl, targetUrl: targetUrl } }, commandInfo); + const actual = await command.validate({ options: { webUrl: 'foo', sourceUrl: sourceServerRelUrl, targetUrl: destServerRelUrl } }, commandInfo); assert.notStrictEqual(actual, true); }); it('fails validation if sourceId is not a valid guid', async () => { - const actual = await command.validate({ options: { webUrl: webUrl, sourceId: 'invalid', targetUrl: targetUrl } }, commandInfo); + const actual = await command.validate({ options: { webUrl: sourceWebUrl, sourceId: 'invalid', targetUrl: destServerRelUrl } }, commandInfo); assert.notStrictEqual(actual, true); }); it('fails validation if nameConflictBehavior is not valid', async () => { - const actual = await command.validate({ options: { webUrl: webUrl, sourceUrl: sourceUrl, targetUrl: targetUrl, nameConflictBehavior: 'invalid' } }, commandInfo); + const actual = await command.validate({ options: { webUrl: sourceWebUrl, sourceUrl: sourceServerRelUrl, targetUrl: destServerRelUrl, nameConflictBehavior: 'invalid' } }, commandInfo); assert.notStrictEqual(actual, true); }); it('passes validation if the sourceId is a valid GUID', async () => { - const actual = await command.validate({ options: { webUrl: webUrl, sourceId: sourceId, targetUrl: targetUrl } }, commandInfo); + const actual = await command.validate({ options: { webUrl: sourceWebUrl, sourceId: sourceFolderId, targetUrl: destServerRelUrl } }, commandInfo); assert.strictEqual(actual, true); }); it('passes validation if the webUrl option is a valid SharePoint site URL', async () => { - const actual = await command.validate({ options: { webUrl: webUrl, sourceUrl: sourceUrl, targetUrl: targetUrl } }, commandInfo); + const actual = await command.validate({ options: { webUrl: sourceWebUrl, sourceUrl: sourceServerRelUrl, targetUrl: destServerRelUrl } }, commandInfo); assert.strictEqual(actual, true); }); - it('copies a folder correctly when specifying sourceId', async () => { - sinon.stub(request, 'get').callsFake(async opts => { - if (opts.url === `${webUrl}/_api/Web/GetFolderById('${sourceId}')?$select=ServerRelativePath`) { + it('correctly outputs exactly one result when folder is copied when using sourceId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${sourceWebUrl}/_api/Web/GetFolderById('${sourceFolderId}')/ServerRelativePath`) { return { - ServerRelativePath: { - DecodedUrl: sourceUrl - } + DecodedUrl: destAbsoluteTargetUrl + `/${sourceFolderName}` }; } + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + throw 'Invalid request: ' + opts.url; }); await command.action(logger, { options: { - webUrl: webUrl, - sourceId: sourceId, - targetUrl: targetUrl, - verbose: true + verbose: true, + webUrl: sourceWebUrl, + sourceId: sourceFolderId, + targetUrl: destAbsoluteTargetUrl } }); - assert.deepStrictEqual(postStub.lastCall.args[0].data, + assert(loggerLogSpy.calledOnce); + }); + + it('correctly outputs result when folder is copied when using sourceId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${sourceWebUrl}/_api/Web/GetFolderById('${sourceFolderId}')/ServerRelativePath`) { + return { + DecodedUrl: sourceAbsoluteUrl + }; + } + + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + + await command.action(logger, { + options: { + verbose: true, + webUrl: sourceWebUrl, + sourceId: sourceFolderId, + targetUrl: destAbsoluteTargetUrl + } + }); + + assert(loggerLogSpy.calledOnce); + assert.deepStrictEqual(loggerLogSpy.lastCall.args[0], destFolderResponse); + }); + + it('correctly outputs result when folder is copied when using sourceUrl', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + + await command.action(logger, { + options: { + verbose: true, + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl + } + }); + + assert(loggerLogSpy.calledOnce); + assert.deepStrictEqual(loggerLogSpy.lastCall.args[0], destFolderResponse); + }); + + it('correctly copies a folder when using sourceId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${sourceWebUrl}/_api/Web/GetFolderById('${sourceFolderId}')/ServerRelativePath`) { + return { + DecodedUrl: sourceAbsoluteUrl + }; + } + + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + + await command.action(logger, { + options: { + webUrl: sourceWebUrl, + sourceId: sourceFolderId, + targetUrl: destAbsoluteTargetUrl + } + }); + + assert.deepStrictEqual(spoUtilCreateCopyJobStub.lastCall.args, [ + sourceWebUrl, + sourceAbsoluteUrl, + destAbsoluteTargetUrl, { - srcPath: { - DecodedUrl: absoluteSourceUrl - }, - destPath: { - DecodedUrl: absoluteTargetUrl + `/${folderName}` - }, - options: { - KeepBoth: false, - ShouldBypassSharedLocks: false, - ResetAuthorAndCreatedOnCopy: false - } + nameConflictBehavior: CreateFolderCopyJobsNameConflictBehavior.Fail, + operation: 'copy', + newName: undefined } - ); + ]); }); - it('copies a folder correctly when specifying sourceUrl with server relative paths', async () => { + it('correctly copies a folder when using sourceUrl', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + await command.action(logger, { options: { - webUrl: webUrl, - sourceUrl: sourceUrl, - targetUrl: targetUrl + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl, + nameConflictBehavior: 'fail' } }); - assert.deepStrictEqual(postStub.lastCall.args[0].data, + assert.deepStrictEqual(spoUtilCreateCopyJobStub.lastCall.args, [ + sourceWebUrl, + sourceAbsoluteUrl, + destAbsoluteTargetUrl, { - srcPath: { - DecodedUrl: absoluteSourceUrl - }, - destPath: { - DecodedUrl: absoluteTargetUrl + `/${folderName}` - }, - options: { - KeepBoth: false, - ShouldBypassSharedLocks: false, - ResetAuthorAndCreatedOnCopy: false - } + nameConflictBehavior: CreateFolderCopyJobsNameConflictBehavior.Fail, + operation: 'copy', + newName: undefined } - ); + ]); }); - it('copies a folder correctly when specifying sourceUrl with site relative paths', async () => { + it('correctly copies a folder when using site-relative sourceUrl', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + await command.action(logger, { options: { - webUrl: webUrl, - sourceUrl: `/Shared Documents/${folderName}`, - targetUrl: targetUrl, + webUrl: sourceWebUrl, + sourceUrl: sourceSiteRelUrl, + targetUrl: destAbsoluteTargetUrl, nameConflictBehavior: 'fail' } }); - assert.deepStrictEqual(postStub.lastCall.args[0].data, + assert.deepStrictEqual(spoUtilCreateCopyJobStub.lastCall.args, [ + sourceWebUrl, + sourceAbsoluteUrl, + destAbsoluteTargetUrl, { - srcPath: { - DecodedUrl: webUrl + `/Shared Documents/${folderName}` - }, - destPath: { - DecodedUrl: absoluteTargetUrl + `/${folderName}` - }, - options: { - KeepBoth: false, - ShouldBypassSharedLocks: false, - ResetAuthorAndCreatedOnCopy: false - } + nameConflictBehavior: CreateFolderCopyJobsNameConflictBehavior.Fail, + operation: 'copy', + newName: undefined } - ); + ]); }); - it('copies a folder correctly when specifying sourceUrl with absolute paths', async () => { + it('correctly copies a folder when using absolute urls', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + await command.action(logger, { options: { - webUrl: webUrl, - sourceUrl: rootUrl + sourceUrl, - targetUrl: rootUrl + targetUrl + webUrl: sourceWebUrl, + sourceUrl: sourceAbsoluteUrl, + targetUrl: destAbsoluteTargetUrl, + nameConflictBehavior: 'rename' } }); - assert.deepStrictEqual(postStub.lastCall.args[0].data, + assert.deepStrictEqual(spoUtilCreateCopyJobStub.lastCall.args, [ + sourceWebUrl, + sourceAbsoluteUrl, + destAbsoluteTargetUrl, { - srcPath: { - DecodedUrl: absoluteSourceUrl - }, - destPath: { - DecodedUrl: absoluteTargetUrl + `/${folderName}` - }, - options: { - KeepBoth: false, - ShouldBypassSharedLocks: false, - ResetAuthorAndCreatedOnCopy: false - } + nameConflictBehavior: CreateFolderCopyJobsNameConflictBehavior.Rename, + operation: 'copy', + newName: undefined } - ); + ]); }); - it('copies a folder correctly when specifying various options', async () => { + it('correctly copies a folder when using sourceUrl with extra options', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + await command.action(logger, { options: { - webUrl: webUrl, - sourceUrl: sourceUrl, - targetUrl: targetUrl, - newName: 'Reports January', + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl, nameConflictBehavior: 'rename', - resetAuthorAndCreated: true, - bypassSharedLock: true + newName: 'Folder-renamed' } }); - assert.deepStrictEqual(postStub.lastCall.args[0].data, + assert.deepStrictEqual(spoUtilCreateCopyJobStub.lastCall.args, [ + sourceWebUrl, + sourceAbsoluteUrl, + destAbsoluteTargetUrl, { - srcPath: { - DecodedUrl: absoluteSourceUrl - }, - destPath: { - DecodedUrl: absoluteTargetUrl + '/Reports January' - }, - options: { - KeepBoth: true, - ShouldBypassSharedLocks: true, - ResetAuthorAndCreatedOnCopy: true - } + nameConflictBehavior: CreateFolderCopyJobsNameConflictBehavior.Rename, + operation: 'copy', + newName: 'Folder-renamed' + } + ]); + }); + + it('correctly polls for the copy job to finish', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `${destWebUrl}/_api/Web/GetFolderById('${destFolderId}')`) { + return destFolderResponse; + } + + throw 'Invalid request: ' + opts.url; + }); + + await command.action(logger, { + options: { + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl + } + }); + + assert.deepStrictEqual(spoUtilGetCopyJobResultStub.lastCall.args, [ + sourceWebUrl, + copyJobInfo + ]); + }); + + it('outputs no result when skipWait is specified', async () => { + await command.action(logger, { + options: { + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl, + skipWait: true + } + }); + + assert(loggerLogSpy.notCalled); + }); + + it('correctly skips polling when skipWait is specified', async () => { + await command.action(logger, { + options: { + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl, + skipWait: true } - ); + }); + + assert(spoUtilGetCopyJobResultStub.notCalled); }); - it('handles error correctly when copying a folder', async () => { - const error = { + it('correctly handles error when sourceId does not exist', async () => { + sinon.stub(request, 'get').rejects({ error: { 'odata.error': { message: { @@ -270,16 +447,27 @@ describe(commands.FOLDER_COPY, () => { } } } - }; + }); + + await assert.rejects(command.action(logger, { + options: { + webUrl: sourceWebUrl, + sourceId: sourceFolderId, + targetUrl: destAbsoluteTargetUrl + } + }), new CommandError('Folder Not Found.')); + }); - sinon.stub(request, 'get').rejects(error); + it('correctly handles error when getCopyJobResult fails', async () => { + spoUtilGetCopyJobResultStub.restore(); + spoUtilGetCopyJobResultStub = sinon.stub(spo, 'getCopyJobResult').rejects(new Error('Target folder already exists.')); await assert.rejects(command.action(logger, { options: { - webUrl: webUrl, - sourceId: sourceId, - targetUrl: targetUrl + webUrl: sourceWebUrl, + sourceUrl: sourceServerRelUrl, + targetUrl: destAbsoluteTargetUrl } - }), new CommandError(error.error['odata.error'].message.value)); + }), new CommandError('Target folder already exists.')); }); -}); +}); \ No newline at end of file diff --git a/src/m365/spo/commands/folder/folder-copy.ts b/src/m365/spo/commands/folder/folder-copy.ts index c1e082dcb75..9d1b16180e8 100644 --- a/src/m365/spo/commands/folder/folder-copy.ts +++ b/src/m365/spo/commands/folder/folder-copy.ts @@ -1,6 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { CreateFolderCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js'; import { urlUtil } from '../../../../utils/urlUtil.js'; import { validation } from '../../../../utils/validation.js'; import SpoCommand from '../../../base/SpoCommand.js'; @@ -17,8 +18,7 @@ interface Options extends GlobalOptions { targetUrl: string; newName?: string; nameConflictBehavior?: string; - resetAuthorAndCreated?: boolean; - bypassSharedLock?: boolean; + skipWait?: boolean; } class SpoFolderCopyCommand extends SpoCommand { @@ -49,8 +49,7 @@ class SpoFolderCopyCommand extends SpoCommand { sourceId: typeof args.options.sourceId !== 'undefined', newName: typeof args.options.newName !== 'undefined', nameConflictBehavior: typeof args.options.nameConflictBehavior !== 'undefined', - resetAuthorAndCreated: !!args.options.resetAuthorAndCreated, - bypassSharedLock: !!args.options.bypassSharedLock + skipWait: !!args.options.skipWait }); }); } @@ -77,10 +76,7 @@ class SpoFolderCopyCommand extends SpoCommand { autocomplete: this.nameConflictBehaviorOptions }, { - option: '--resetAuthorAndCreated' - }, - { - option: '--bypassSharedLock' + option: '--skipWait' } ); } @@ -112,7 +108,7 @@ class SpoFolderCopyCommand extends SpoCommand { #initTypes(): void { this.types.string.push('webUrl', 'sourceUrl', 'sourceId', 'targetUrl', 'newName', 'nameConflictBehavior'); - this.types.boolean.push('resetAuthorAndCreated', 'bypassSharedLock'); + this.types.boolean.push('skipWait'); } protected getExcludedOptionsWithUrls(): string[] | undefined { @@ -121,51 +117,70 @@ class SpoFolderCopyCommand extends SpoCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { try { - const sourcePath = await this.getSourcePath(logger, args.options); + const sourceServerRelativePath = await this.getSourcePath(logger, args.options); + const sourcePath = this.getAbsoluteUrl(args.options.webUrl, sourceServerRelativePath); + const destinationPath = this.getAbsoluteUrl(args.options.webUrl, args.options.targetUrl); if (this.verbose) { - await logger.logToStderr(`Copying folder ${sourcePath} to ${args.options.targetUrl}...`); + await logger.logToStderr(`Copying folder '${sourcePath}' to '${destinationPath}'...`); } - const absoluteSourcePath = this.getAbsoluteUrl(args.options.webUrl, sourcePath); - let absoluteTargetPath = this.getAbsoluteUrl(args.options.webUrl, args.options.targetUrl) + '/'; + const copyJobResponse = await spo.createFolderCopyJob( + args.options.webUrl, + sourcePath, + destinationPath, + { + nameConflictBehavior: this.getNameConflictBehaviorValue(args.options.nameConflictBehavior), + newName: args.options.newName, + operation: 'copy' + } + ); - if (args.options.newName) { - absoluteTargetPath += args.options.newName; + if (args.options.skipWait) { + return; } - else { - // Keep the original folder name - absoluteTargetPath += sourcePath.substring(sourcePath.lastIndexOf('/') + 1); + + if (this.verbose) { + await logger.logToStderr('Waiting for the copy job to complete...'); + } + + const copyJobResult = await spo.getCopyJobResult(args.options.webUrl, copyJobResponse); + + if (this.verbose) { + await logger.logToStderr('Getting information about the destination folder...'); } + // Get destination folder data + const siteRelativeDestinationFolder = '/' + copyJobResult.TargetObjectSiteRelativeUrl.substring(0, copyJobResult.TargetObjectSiteRelativeUrl.lastIndexOf('/')); + const absoluteWebUrl = destinationPath.substring(0, destinationPath.toLowerCase().lastIndexOf(siteRelativeDestinationFolder.toLowerCase())); + const requestOptions: CliRequestOptions = { - url: `${args.options.webUrl}/_api/SP.MoveCopyUtil.CopyFolderByPath`, + url: `${absoluteWebUrl}/_api/Web/GetFolderById('${copyJobResult.TargetObjectUniqueId}')`, headers: { accept: 'application/json;odata=nometadata' }, - responseType: 'json', - data: { - srcPath: { - DecodedUrl: absoluteSourcePath - }, - destPath: { - DecodedUrl: absoluteTargetPath - }, - options: { - KeepBoth: args.options.nameConflictBehavior === 'rename', - ShouldBypassSharedLocks: !!args.options.bypassSharedLock, - ResetAuthorAndCreatedOnCopy: !!args.options.resetAuthorAndCreated - } - } + responseType: 'json' }; - await request.post(requestOptions); + const destinationFile = await request.get(requestOptions); + await logger.log(destinationFile); } catch (err: any) { this.handleRejectedODataJsonPromise(err); } } + private getNameConflictBehaviorValue(nameConflictBehavior?: string): CreateFolderCopyJobsNameConflictBehavior { + switch (nameConflictBehavior?.toLowerCase()) { + case 'fail': + return CreateFolderCopyJobsNameConflictBehavior.Fail; + case 'rename': + return CreateFolderCopyJobsNameConflictBehavior.Rename; + default: + return CreateFolderCopyJobsNameConflictBehavior.Fail; + } + } + private async getSourcePath(logger: Logger, options: Options): Promise { if (options.sourceUrl) { return urlUtil.getServerRelativePath(options.webUrl, options.sourceUrl); @@ -176,19 +191,20 @@ class SpoFolderCopyCommand extends SpoCommand { } const requestOptions: CliRequestOptions = { - url: `${options.webUrl}/_api/Web/GetFolderById('${options.sourceId}')?$select=ServerRelativePath`, + url: `${options.webUrl}/_api/Web/GetFolderById('${options.sourceId}')/ServerRelativePath`, headers: { accept: 'application/json;odata=nometadata' }, responseType: 'json' }; - const file = await request.get<{ ServerRelativePath: { DecodedUrl: string } }>(requestOptions); - return file.ServerRelativePath.DecodedUrl; + const path = await request.get<{ DecodedUrl: string }>(requestOptions); + return path.DecodedUrl; } private getAbsoluteUrl(webUrl: string, url: string): string { - return url.startsWith('https://') ? url : urlUtil.getAbsoluteUrl(webUrl, url); + const result = url.startsWith('https://') ? url : urlUtil.getAbsoluteUrl(webUrl, url); + return urlUtil.removeTrailingSlashes(result); } } From def156cc2c2a979985a033210a6103e83701eb00 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:55:32 +0200 Subject: [PATCH 04/42] Removes deprecated option from 'entra m365group user remove'. Closes #6357 --- .../entra/m365group/m365group-user-remove.mdx | 7 +- docs/docs/v10-upgrade-guidance.mdx | 71 ++++++------------- .../m365group/m365group-user-remove.spec.ts | 61 +++++----------- .../m365group/m365group-user-remove.ts | 21 ++---- 4 files changed, 45 insertions(+), 115 deletions(-) diff --git a/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx b/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx index 930fc5df09f..a4d6b59989a 100644 --- a/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx @@ -25,14 +25,11 @@ m365 entra m365group user remove [options] `--teamName [teamName]` : The display name of the Microsoft Teams team. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`. -`-n, --userName [userName]` -: (deprecated) User's UPN (user principal name), eg. `johndoe@example.com`.. Specify only one of the following: `userName`, `ids` or `userNames`. - `--ids [ids]` -: Microsoft Entra IDs of users. You can also pass a comma-separated list of IDs. Specify only one of the following `userName`, `ids` or `userNames`. +: Microsoft Entra IDs of users. You can also pass a comma-separated list of IDs. Specify either `ids` or `userNames` but not both. `--userNames [userNames]` -: The user principal names of users. You can also pass a comma-separated list of UPNs. Specify only one of the following `userName`, `ids` or `userNames`. +: The user principal names of users. You can also pass a comma-separated list of UPNs. Specify either `ids` or `userNames` but not both. `-f, --force` : Don't prompt for confirming removing the user from the specified Microsoft 365 Group or Microsoft Teams team. diff --git a/docs/docs/v10-upgrade-guidance.mdx b/docs/docs/v10-upgrade-guidance.mdx index acf2053e701..49bf8f02419 100644 --- a/docs/docs/v10-upgrade-guidance.mdx +++ b/docs/docs/v10-upgrade-guidance.mdx @@ -2,16 +2,6 @@ The v10 of CLI for Microsoft 365 introduces several breaking changes. To help you upgrade to the latest version of CLI for Microsoft 365, we've listed those changes along with any actions you may need to take. -## App - -### Updated option names of 'app permission add' to plural - -We updated the [app permission add](./cmd/app/permission/permission-add.mdx) command. Option were updated to `applicationPermissions` and `delegatedPermissions`. - -#### What action do I need to take? - -Please, check the documentation of the [app permission add](./cmd/app/permission/permission-add.mdx) command to see the updated options and adjust your scripts accordingly. - ## CLI ### Removed command `cli reconsent` @@ -70,50 +60,10 @@ The deprecated Guest value was removed from the `role` option in the [aad m365gr Please, check the documentation of the [aad m365group user list](./cmd/entra/m365group/m365group-user-list.mdx) command to see the updated ``role` option and adjust your scripts accordingly. -#### Aligned options with naming convention - -We updated option naming from `groupDisplayName` to `groupName` and `userPrincipalName` to `userName`. - -**Affected commands:** - -- [entra group member list](./cmd/entra/group/group-member-list.mdx) -- [entra m365group conversation post list](./cmd/entra/m365group/m365group-conversation-post-list.mdx) -- [entra m365group recyclebinitem list](./cmd/entra/m365group/m365group-recyclebinitem-list.mdx) -- [outlook message get](./cmd/outlook/message/message-get.mdx) - #### What action do I need to take? Please, check the documentation of the affected commands to see the updated option naming and adjust your scripts accordingly. -### Aligns options of `entra enterpriseapp` commands - -For this new major version, we've aligned the options of the `entra enterpriseapp` commands as follows: - -[entra enterpriseapp add](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-add) - -| Old option | New option | -|---------------|---------------| -| --appId [appId] | -i, --id [id] | -| --appName [appName] | -n, --displayName [displayName] | - -[entra enterpriseapp get](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-get) - -| Old option | New option | -|---------------|---------------| -| -i, --appId [appId] | -i, --id [id] | -| -n, --appDisplayName [appDisplayName] | -n, --displayName [displayName] | -| --appObjectId [appObjectId] | --objectId [objectId] | - -[entra enterpriseapp list](https://pnp.github.io/cli-microsoft365/cmd/entra/enterpriseapp/enterpriseapp-list) - -| Old option | New option | -|---------------|---------------| -| --displayName [displayName] | -n, --displayName [displayName] | - -#### What action do I need to take? - -Please, check the documentation of the [entra enterpriseapp add](./cmd/entra/enterpriseapp/enterpriseapp-add), [entra enterpriseapp get](./cmd/entra/enterpriseapp/enterpriseapp-get), and [entra enterpriseapp list](./cmd/entra/enterpriseapp/enterpriseapp-list) commands to see the updated options and adjust your scripts accordingly. - ### Removed deprecated option `userName` from the `entra m365group user add` command. The deprecated option `userName` was removed from the command [entra m365group user add](./cmd/entra/m365group/m365group-user-add.mdx). @@ -365,6 +315,27 @@ Replace any of the aliases mentioned above with the corresponding command name. ## General +### Aligned options with naming convention + +In version 10 of the CLI for Microsoft 365, we have made updates to the options for specific commands, aligning with our naming convention. This includes renaming options to ensure consistency and improve the CLI experience. Some other options have been renamed to align with the response output of the commands. These changes aim to make it easier for you to use the CLI. + +We've updated the following commands and options: + +Command|Old option|New option +-------|----------|---------- +[app permission add](./cmd/app/permission/permission-add.mdx) | `applicationPermission`
`delegatedPermission` | `applicationPermissions`
`delegatedPermissions` +[entra enterpriseapp add](./cmd/entra/enterpriseapp/enterpriseapp-add) | `appId`
`appName` | `id`
`displayName` +[entra enterpriseapp get](./cmd/entra/enterpriseapp/enterpriseapp-get) | `appId`
`appDisplayName`
`appObjectId` | `id`
`displayName`
`objectId` +[entra group member list](./cmd/entra/group/group-member-list.mdx) | `groupDisplayName` | `groupName` +[entra m365group conversation post list](./cmd/entra/m365group/m365group-conversation-post-list.mdx) | `groupDisplayName` | `groupName` +[entra m365group recyclebinitem list](./cmd/entra/m365group/m365group-recyclebinitem-list.mdx) | `groupDisplayName` | `groupName` +[entra m365group user remove](./cmd/entra/m365group/m365group-user-remove.mdx) | `userName` | `userNames` +[outlook message get](./cmd/outlook/message/message-get.mdx) | `userPrincipalName` | `userName` + +#### What action do I need to take? + +If you use any of the commands listed above, ensure that you use the new option names. + ### Ensured output for `list` commands We noticed that some of the `list` commands were not returning any output when no items were found. We've updated all `list` commands to return an empty array when no items are found. diff --git a/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts b/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts index 5c9345823fd..fe6b05b7574 100644 --- a/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts @@ -82,21 +82,11 @@ describe(commands.M365GROUP_USER_REMOVE, () => { assert.notStrictEqual(command.description, null); }); - it('fails validation if userName is not a valid upn', async () => { - const actual = await command.validate({ - options: { - groupId: groupOrTeamId, - userName: 'invalid' - } - }, commandInfo); - assert.notStrictEqual(actual, true); - }); - it('fails validation if the groupId is not a valid guid', async () => { const actual = await command.validate({ options: { groupId: 'invalid', - userName: userName + userNames: userName } }, commandInfo); assert.notStrictEqual(actual, true); @@ -106,7 +96,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { const actual = await command.validate({ options: { teamId: 'invalid', - userName: userName + userNames: userName } }, commandInfo); assert.notStrictEqual(actual, true); @@ -132,16 +122,6 @@ describe(commands.M365GROUP_USER_REMOVE, () => { assert.notStrictEqual(actual, true); }); - it('passes validation when a valid teamId and userName are specified', async () => { - const actual = await command.validate({ - options: { - teamId: groupOrTeamId, - userName: userName - } - }, commandInfo); - assert.strictEqual(actual, true); - }); - it('passes validation when a valid teamId and userNames are specified', async () => { const actual = await command.validate({ options: { @@ -164,13 +144,13 @@ describe(commands.M365GROUP_USER_REMOVE, () => { it('prompts before removing the specified user from the specified Microsoft 365 Group when force option not passed', async () => { - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }); assert(promptIssued); }); it('prompts before removing the specified user from the specified Team when force option not passed (debug)', async () => { - await command.action(logger, { options: { debug: true, teamId: "00000000-0000-0000-0000-000000000000", userName: userName } }); + await command.action(logger, { options: { debug: true, teamId: "00000000-0000-0000-0000-000000000000", userNames: userName } }); assert(promptIssued); }); @@ -180,7 +160,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(false); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }); assert(postSpy.notCalled); }); @@ -189,7 +169,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(false); - await command.action(logger, { options: { debug: true, groupId: groupOrTeamId, userName: userName } }); + await command.action(logger, { options: { debug: true, groupId: groupOrTeamId, userNames: userName } }); assert(postSpy.notCalled); }); @@ -213,7 +193,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(true); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }); assert(memberDeleteCallIssued); }); @@ -235,7 +215,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { }); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } }); assert(memberDeleteCallIssued); }); @@ -261,7 +241,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { }); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } }); assert(memberDeleteCallIssued); }); @@ -287,7 +267,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(true); - await command.action(logger, { options: { teamName: groupOrTeamName, userName: userName, verbose: true } }); + await command.action(logger, { options: { teamName: groupOrTeamName, userNames: userName, verbose: true } }); assert(deleteStub.calledTwice); }); @@ -318,7 +298,6 @@ describe(commands.M365GROUP_USER_REMOVE, () => { assert(deleteStub.calledTwice); }); - it('removes the specified members of the specified Microsoft 365 Group specified by groupName', async () => { sinon.stub(entraGroup, 'getGroupIdByDisplayName').withArgs(groupOrTeamName).resolves(groupOrTeamId); @@ -343,11 +322,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { }); it('does not fail if the user is not owner or member of the specified Microsoft 365 Group when prompt confirmed', async () => { - let memberDeleteCallIssued = false; - - sinon.stub(request, 'delete').callsFake(async (opts) => { - memberDeleteCallIssued = true; - + const deleteStub = sinon.stub(request, 'delete').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/${groupOrTeamId}/owners/${userId}/$ref`) { return { "response": { @@ -369,8 +344,8 @@ describe(commands.M365GROUP_USER_REMOVE, () => { }); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } }); - assert(memberDeleteCallIssued); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } }); + assert(deleteStub.calledTwice); }); it('stops removal if an unknown error message is thrown when deleting the owner', async () => { @@ -400,7 +375,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { }); - await command.action(logger, { options: { groupId: groupOrTeamId, userName: userName, force: true } }); + await command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName, force: true } }); assert(memberDeleteCallIssued); }); @@ -425,7 +400,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { ); sinon.stub(cli, 'promptForConfirmation').resolves(true); - await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any), + await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }), new CommandError(errorMessage)); }); @@ -448,7 +423,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(true); - await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any), + await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } } as any), new CommandError('Invalid object identifier')); }); @@ -481,7 +456,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(cli.promptForConfirmation); sinon.stub(cli, 'promptForConfirmation').resolves(true); - await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: userName } } as any), + await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: userName } }), new CommandError('Invalid object identifier')); }); @@ -489,7 +464,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinonUtil.restore(entraGroup.isUnifiedGroup); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(false); - await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userName: 'anne.matthews@contoso.onmicrosoft.com', force: true } } as any), + await assert.rejects(command.action(logger, { options: { groupId: groupOrTeamId, userNames: 'anne.matthews@contoso.onmicrosoft.com', force: true } }), new CommandError(`Specified group with id '${groupOrTeamId}' is not a Microsoft 365 group.`)); }); }); diff --git a/src/m365/entra/commands/m365group/m365group-user-remove.ts b/src/m365/entra/commands/m365group/m365group-user-remove.ts index 380cc76c50e..126ab7dc847 100644 --- a/src/m365/entra/commands/m365group/m365group-user-remove.ts +++ b/src/m365/entra/commands/m365group/m365group-user-remove.ts @@ -18,7 +18,6 @@ interface Options extends GlobalOptions { teamName?: string; groupId?: string; groupName?: string; - userName?: string; ids?: string; userNames?: string; force?: boolean; @@ -51,7 +50,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { groupId: typeof args.options.groupId !== 'undefined', teamName: typeof args.options.teamName !== 'undefined', groupName: typeof args.options.groupName !== 'undefined', - userName: typeof args.options.userName !== 'undefined', ids: typeof args.options.ids !== 'undefined', userNames: typeof args.options.userNames !== 'undefined' }); @@ -72,9 +70,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { { option: '--teamName [teamName]' }, - { - option: '-n, --userName [userName]' - }, { option: '--ids [ids]' }, @@ -112,10 +107,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { } } - if (args.options.userName && !validation.isValidUserPrincipalName(args.options.userName)) { - return `The specified userName '${args.options.userName}' is not a valid user principal name.`; - } - return true; } ); @@ -127,21 +118,17 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { options: ['groupId', 'teamId', 'groupName', 'teamName'] }, { - options: ['userName', 'ids', 'userNames'] + options: ['ids', 'userNames'] } ); } #initTypes(): void { - this.types.string.push('groupId', 'groupName', 'teamId', 'teamName', 'userName', 'ids', 'userNames'); + this.types.string.push('groupId', 'groupName', 'teamId', 'teamName', 'ids', 'userNames'); this.types.boolean.push('force'); } public async commandAction(logger: Logger, args: CommandArgs): Promise { - if (args.options.userName) { - await this.warn(logger, `Option 'userName' is deprecated. Please use 'ids' or 'userNames' instead.`); - } - const removeUser = async (): Promise => { try { const groupId: string = await this.getGroupId(logger, args.options.groupId, args.options.teamId, args.options.groupName, args.options.teamName); @@ -151,7 +138,7 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { throw Error(`Specified group with id '${groupId}' is not a Microsoft 365 group.`); } - const userNames = args.options.userNames || args.options.userName; + const userNames = args.options.userNames; const userIds: string[] = await this.getUserIds(logger, args.options.ids, userNames); await this.removeUsersFromGroup(groupId, userIds, 'owners'); @@ -166,7 +153,7 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { await removeUser(); } else { - const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove ${args.options.userName || args.options.userNames || args.options.ids} from ${args.options.groupId || args.options.groupName || args.options.teamId || args.options.teamName}?` }); + const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove ${args.options.userNames || args.options.ids} from ${args.options.groupId || args.options.groupName || args.options.teamId || args.options.teamName}?` }); if (result) { await removeUser(); From 4bd7faa915404b79779b8a96e41e2c7b48e5acd9 Mon Sep 17 00:00:00 2001 From: Can Arslan <138895927+mc2rcanarslan@users.noreply.github.com> Date: Fri, 18 Oct 2024 10:45:24 -0600 Subject: [PATCH 05/42] Updates 'spo userprofile get' command docs. --- src/m365/spo/commands/userprofile/userprofile-get.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m365/spo/commands/userprofile/userprofile-get.ts b/src/m365/spo/commands/userprofile/userprofile-get.ts index a6ef3fc9e38..1a368028ab9 100644 --- a/src/m365/spo/commands/userprofile/userprofile-get.ts +++ b/src/m365/spo/commands/userprofile/userprofile-get.ts @@ -22,7 +22,7 @@ class SpoUserProfileGetCommand extends SpoCommand { } public get description(): string { - return 'Sets user profile property for a SharePoint user'; + return 'Gets user profile property for a SharePoint user'; } constructor() { From c6297ed51b779a6e935cefeea0222aa2e5d5305e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Thu, 24 Oct 2024 23:57:10 +0200 Subject: [PATCH 06/42] Updates release notes --- docs/docs/about/release-notes.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index c6bb619a544..e1b3b5d2838 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -27,6 +27,7 @@ sidebar_position: 3 - added ability to specify multiple users for command [entra m365group user remove](../cmd/entra/m365group/m365group-user-remove.mdx) command [#6058](https://github.com/pnp/cli-microsoft365/issues/6058) - enhanced [spo list remove](../cmd/spo/list/list-remove.mdx) command with `--recylce` flag [#6270](https://github.com/pnp/cli-microsoft365/issues/6270) - enhanced [spo file remove](../cmd/spo/file/file-remove.mdx) command with `--bypassSharedLock` flag [#6313](https://github.com/pnp/cli-microsoft365/issues/6313) +- updated 'spo userprofile get' command docs. ### ⚠️ Breaking changes @@ -61,6 +62,8 @@ sidebar_position: 3 - removed aad options and aliasses [#5823](https://github.com/pnp/cli-microsoft365/issues/5823), [#5676](https://github.com/pnp/cli-microsoft365/issues/5676) - removed deprecated option `username` from [entra m365group user set](../cmd/entra/m365group/m365group-user-set.mdx) command [#6224](https://github.com/pnp/cli-microsoft365/issues/6224) - updated endpoint for [spo folder move](../cmd/spo/folder/folder-move.mdx) command [#6154](https://github.com/pnp/cli-microsoft365/issues/6154) +- moved [spo folder copy](../cmd/spo/folder/folder-copy.mdx) to new endpoint [#6310](https://github.com/pnp/cli-microsoft365/issues/6310) +- removed deprecated option from 'entra m365group user remove' [#6357](https://github.com/pnp/cli-microsoft365/issues/6357) ## [v9.1.0](https://github.com/pnp/cli-microsoft365/releases/tag/v9.1.0) From bc324dc776061864c12bc9845badf83222231511 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 18 Oct 2024 14:52:17 +0200 Subject: [PATCH 07/42] Adds new command 'spo page publish'. Closes #6420 --- docs/docs/cmd/spo/page/page-publish.mdx | 41 ++++ docs/src/config/sidebars.ts | 5 + src/m365/spo/commands.ts | 1 + .../spo/commands/page/page-publish.spec.ts | 178 ++++++++++++++++++ src/m365/spo/commands/page/page-publish.ts | 69 +++++++ 5 files changed, 294 insertions(+) create mode 100644 docs/docs/cmd/spo/page/page-publish.mdx create mode 100644 src/m365/spo/commands/page/page-publish.spec.ts create mode 100644 src/m365/spo/commands/page/page-publish.ts diff --git a/docs/docs/cmd/spo/page/page-publish.mdx b/docs/docs/cmd/spo/page/page-publish.mdx new file mode 100644 index 00000000000..f2666d33214 --- /dev/null +++ b/docs/docs/cmd/spo/page/page-publish.mdx @@ -0,0 +1,41 @@ +import Global from '/docs/cmd/_global.mdx'; + +# spo page publish + +Publishes a modern page + +## Usage + +```sh +m365 spo page publish [options] +``` + +## Options + +```md definition-list +`-u, --webUrl ` +: URL of the site where the page is located. + +`-n, --name ` +: Name of the page. +``` + + + +## Examples + +Publish a modern page + +```sh +m365 spo page publish --webUrl https://contoso.sharepoint.com/sites/Marketing --name "Style guide.aspx" +``` + +Publish a modern page in a subfolder + +```sh +m365 spo page publish --webUrl https://contoso.sharepoint.com/sites/Marketing --name "/Styles/Guide.aspx" +``` + +## Response + +The command won't return a response on success. diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 858f2189040..7b141310973 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -3166,6 +3166,11 @@ const sidebars: SidebarsConfig = { label: 'page list', id: 'cmd/spo/page/page-list' }, + { + type: 'doc', + label: 'page publish', + id: 'cmd/spo/page/page-publish' + }, { type: 'doc', label: 'page remove', diff --git a/src/m365/spo/commands.ts b/src/m365/spo/commands.ts index e7323a44bb6..990c4988471 100644 --- a/src/m365/spo/commands.ts +++ b/src/m365/spo/commands.ts @@ -203,6 +203,7 @@ export default { PAGE_COPY: `${prefix} page copy`, PAGE_GET: `${prefix} page get`, PAGE_LIST: `${prefix} page list`, + PAGE_PUBLISH: `${prefix} page publish`, PAGE_REMOVE: `${prefix} page remove`, PAGE_SET: `${prefix} page set`, PAGE_CLIENTSIDEWEBPART_ADD: `${prefix} page clientsidewebpart add`, diff --git a/src/m365/spo/commands/page/page-publish.spec.ts b/src/m365/spo/commands/page/page-publish.spec.ts new file mode 100644 index 00000000000..0c6db15f965 --- /dev/null +++ b/src/m365/spo/commands/page/page-publish.spec.ts @@ -0,0 +1,178 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import { z } from 'zod'; +import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './page-publish.js'; +import { urlUtil } from '../../../../utils/urlUtil.js'; +import { formatting } from '../../../../utils/formatting.js'; + +describe(commands.PAGE_PUBLISH, () => { + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + let postStub: sinon.SinonStub; + let commandOptionsSchema: z.ZodTypeAny; + + const webUrl = 'https://contoso.sharepoint.com/sites/Marketing'; + const serverRelativeUrl = urlUtil.getServerRelativeSiteUrl(webUrl); + const pageName = 'HR.aspx'; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + commandInfo = cli.getCommandInfo(command); + commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + + const serverRelativePageUrl = `${serverRelativeUrl}/SitePages/${pageName}`; + postStub = sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === `${webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(serverRelativePageUrl)}')/Publish()`) { + return; + } + + throw 'Invalid request: ' + opts.url; + }); + }); + + afterEach(() => { + sinonUtil.restore([ + request.post + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.PAGE_PUBLISH); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('logs no command output', async () => { + await command.action(logger, + { + options: { + webUrl: webUrl, + name: pageName + } + }); + + assert(loggerLogSpy.notCalled); + }); + + it('correctly publishes page', async () => { + await command.action(logger, + { + options: { + webUrl: webUrl, + name: pageName + } + }); + + assert(postStub.calledOnce); + }); + + it('correctly publishes a page when extension is not specified', async () => { + await command.action(logger, + { + options: { + webUrl: webUrl, + name: pageName.substring(0, pageName.lastIndexOf('.')), + verbose: true + } + }); + + assert(postStub.calledOnce); + }); + + it('correctly publishes a nested page', async () => { + const pageUrl = '/folder1/folder2/' + pageName; + postStub.restore(); + + postStub = sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === `${webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(serverRelativeUrl + '/SitePages' + pageUrl)}')/Publish()`) { + return; + } + + throw 'Invalid request: ' + opts.url; + }); + + await command.action(logger, + { + options: { + webUrl: webUrl, + name: pageUrl + } + }); + + assert(postStub.calledOnce); + }); + + it('correctly handles API error', async () => { + postStub.restore(); + const errorMessage = 'The file /sites/Marketing/SitePages/My-new-page.aspx does not exist.'; + + sinon.stub(request, 'post').rejects({ + error: { + 'odata.error': { + message: { + lang: 'en-US', + value: errorMessage + } + } + } + }); + + await assert.rejects(command.action(logger, { options: { webUrl: webUrl, name: pageName } }), + new CommandError(errorMessage)); + }); + + it('fails validation if webUrl is not a valid SharePoint URL', async () => { + const actual = commandOptionsSchema.safeParse({ webUrl: 'foo' }); + assert.strictEqual(actual.success, false); + }); + + it('passes validation when the webUrl is a valid SharePoint URL and name is specified', async () => { + const actual = commandOptionsSchema.safeParse({ webUrl: webUrl, name: pageName }); + assert.strictEqual(actual.success, true); + }); + + it('passes validation when name has no extension', async () => { + const actual = commandOptionsSchema.safeParse({ webUrl: webUrl, name: 'page' }); + assert.strictEqual(actual.success, true); + }); +}); diff --git a/src/m365/spo/commands/page/page-publish.ts b/src/m365/spo/commands/page/page-publish.ts new file mode 100644 index 00000000000..dcb499524ac --- /dev/null +++ b/src/m365/spo/commands/page/page-publish.ts @@ -0,0 +1,69 @@ +import { z } from 'zod'; +import { zod } from '../../../../utils/zod.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { globalOptionsZod } from '../../../../Command.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; +import { urlUtil } from '../../../../utils/urlUtil.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; + +const options = globalOptionsZod + .extend({ + webUrl: zod.alias('u', z.string() + .refine(url => validation.isValidSharePointUrl(url) === true, url => ({ + message: `'${url}' is not a valid SharePoint Online site URL.` + })) + ), + name: zod.alias('n', z.string()) + }) + .strict(); +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +class SpoPagePublishCommand extends SpoCommand { + public get name(): string { + return commands.PAGE_PUBLISH; + } + + public get description(): string { + return 'Publishes a modern page'; + } + + public get schema(): z.ZodTypeAny { + return options; + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + try { + // Remove leading slashes from the page name (page can be nested in folders) + let pageName: string = urlUtil.removeLeadingSlashes(args.options.name); + if (!pageName.toLowerCase().endsWith('.aspx')) { + pageName += '.aspx'; + } + + if (this.verbose) { + await logger.logToStderr(`Publishing page ${pageName}...`); + } + + const filePath = `${urlUtil.getServerRelativeSiteUrl(args.options.webUrl)}/SitePages/${pageName}`; + const requestOptions: CliRequestOptions = { + url: `${args.options.webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(filePath)}')/Publish()`, + headers: { + accept: 'application/json;odata=nometadata' + } + }; + + await request.post(requestOptions); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new SpoPagePublishCommand(); From 563ff823875f4aef7d5ad1d8f0b89f9724ca9a8c Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Fri, 25 Oct 2024 22:34:32 +0200 Subject: [PATCH 08/42] Updates release notes --- docs/docs/about/release-notes.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index e1b3b5d2838..581d0b66b24 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -10,6 +10,7 @@ sidebar_position: 3 **SharePoint:** +- [spo page publish](../cmd/spo/page/page-publish.mdx) - publishes a modern page [#6420](https://github.com/pnp/cli-microsoft365/issues/6420) - [spo tenant site membership list](../cmd/spo/tenant/tenant-site-membership-list.mdx) - retrieves information about default site groups' membership [#5980](https://github.com/pnp/cli-microsoft365/issues/5980) **SharePoint Premium:** From cb46a5efed8d03434ad6318e8cfa6144f7a44054 Mon Sep 17 00:00:00 2001 From: Shantha Kumar T Date: Sun, 22 Sep 2024 07:39:38 +0530 Subject: [PATCH 09/42] Renames command 'spo site list' to 'spo tenant site list'. Closes #6143 --- docs/docs/about/release-notes.mdx | 4 ++-- .../tenant-site-list.mdx} | 20 ++++++++++++------- docs/docs/v6-upgrade-guidance.mdx | 2 +- docs/docs/v7-upgrade-guidance.mdx | 2 +- docs/src/config/sidebars.ts | 7 ++++++- src/index.spec.ts | 1 + src/m365/spo/commands.ts | 3 ++- .../SPOTenantSitePropertiesEnumerable.ts | 7 +++++++ .../commands/tenant/TenantSiteProperties.ts | 6 ++++++ .../tenant-site-list.spec.ts} | 11 +++++++--- .../tenant-site-list.ts} | 18 ++++++++++------- 11 files changed, 58 insertions(+), 23 deletions(-) rename docs/docs/cmd/spo/{site/site-list.mdx => tenant/tenant-site-list.mdx} (97%) create mode 100644 src/m365/spo/commands/tenant/SPOTenantSitePropertiesEnumerable.ts create mode 100644 src/m365/spo/commands/tenant/TenantSiteProperties.ts rename src/m365/spo/commands/{site/site-list.spec.ts => tenant/tenant-site-list.spec.ts} (99%) rename src/m365/spo/commands/{site/site-list.ts => tenant/tenant-site-list.ts} (92%) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 581d0b66b24..673280c8939 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -3848,7 +3848,7 @@ sidebar_position: 3 - added information about the current user to status commands [#202](https://github.com/pnp/cli-microsoft365/issues/202) - fixed issue with completing input that doesn't match commands [#222](https://github.com/pnp/cli-microsoft365/issues/222) - fixed issue with escaping numeric input [#226](https://github.com/pnp/cli-microsoft365/issues/226) -- changed the [entra oauth2grant list](../cmd/entra/oauth2grant/oauth2grant-list.mdx), [spo app list](../cmd/spo/app/app-list.mdx), [spo customaction list](../cmd/spo/customaction/customaction-list.mdx), [spo site list](../cmd/spo/site/site-list.mdx) commands to list all properties for output type JSON [#232](https://github.com/pnp/cli-microsoft365/issues/232), [#233](https://github.com/pnp/cli-microsoft365/issues/233), [#234](https://github.com/pnp/cli-microsoft365/issues/234), [#235](https://github.com/pnp/cli-microsoft365/issues/235) +- changed the [entra oauth2grant list](../cmd/entra/oauth2grant/oauth2grant-list.mdx), [spo app list](../cmd/spo/app/app-list.mdx), [spo customaction list](../cmd/spo/customaction/customaction-list.mdx), [spo site list](../cmd/spo/tenant/tenant-site-list.mdx) commands to list all properties for output type JSON [#232](https://github.com/pnp/cli-microsoft365/issues/232), [#233](https://github.com/pnp/cli-microsoft365/issues/233), [#234](https://github.com/pnp/cli-microsoft365/issues/234), [#235](https://github.com/pnp/cli-microsoft365/issues/235) - fixed issue with generating clink completion file [#252](https://github.com/pnp/cli-microsoft365/issues/252) - added [user guide](../user-guide/installing-cli.mdx) [#236](https://github.com/pnp/cli-microsoft365/issues/236), [#237](https://github.com/pnp/cli-microsoft365/issues/237), [#238](https://github.com/pnp/cli-microsoft365/issues/238), [#239](https://github.com/pnp/cli-microsoft365/issues/239) @@ -3864,7 +3864,7 @@ sidebar_position: 3 - [spo customaction list](../cmd/spo/customaction/customaction-list.mdx) - lists user custom actions for site or site collection [#19](https://github.com/pnp/cli-microsoft365/issues/19) - [spo site get](../cmd/spo/site/site-get.mdx) - gets information about the specific site collection [#114](https://github.com/pnp/cli-microsoft365/issues/114) -- [spo site list](../cmd/spo/site/site-list.mdx) - lists modern sites of the given type [#115](https://github.com/pnp/cli-microsoft365/issues/115) +- [spo site list](../cmd/spo/tenant/tenant-site-list.mdx) - lists modern sites of the given type [#115](https://github.com/pnp/cli-microsoft365/issues/115) - [spo site add](../cmd/spo/site/site-add.mdx) - creates new modern site [#116](https://github.com/pnp/cli-microsoft365/issues/116) - [spo app remove](../cmd/spo/app/app-remove.mdx) - removes the specified app from the tenant app catalog [#9](https://github.com/pnp/cli-microsoft365/issues/9) - [spo site appcatalog add](../cmd/spo/site/site-appcatalog-add.mdx) - creates a site collection app catalog in the specified site [#63](https://github.com/pnp/cli-microsoft365/issues/63) diff --git a/docs/docs/cmd/spo/site/site-list.mdx b/docs/docs/cmd/spo/tenant/tenant-site-list.mdx similarity index 97% rename from docs/docs/cmd/spo/site/site-list.mdx rename to docs/docs/cmd/spo/tenant/tenant-site-list.mdx index e2831c65ee1..c67f76790aa 100644 --- a/docs/docs/cmd/spo/site/site-list.mdx +++ b/docs/docs/cmd/spo/tenant/tenant-site-list.mdx @@ -2,14 +2,20 @@ import Global from '/docs/cmd/_global.mdx'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# spo site list +# spo tenant site list Lists modern sites of the given type ## Usage ```sh -m365 spo site list [options] +m365 spo tenant site list [options] +``` + +## Alias + +```sh +m365 spo site list ``` ## Options @@ -49,31 +55,31 @@ To use this command you have to have permissions to access the tenant admin site List all sites in the currently connected tenant ```sh -m365 spo site list +m365 spo tenant site list ``` List all group connected team sites in the currently connected tenant ```sh -m365 spo site list --type TeamSite +m365 spo tenant site list --type TeamSite ``` List all communication sites in the currently connected tenant ```sh -m365 spo site list --type CommunicationSite +m365 spo tenant site list --type CommunicationSite ``` List all group connected team sites that contain _project_ in the URL ```sh -m365 spo site list --type TeamSite --filter "Url -like 'project'" +m365 spo tenant site list --type TeamSite --filter "Url -like 'project'" ``` List all sites in the currently connected tenant including OneDrive sites ```sh -m365 spo site list --includeOneDriveSites +m365 spo tenant site list --includeOneDriveSites ``` ## Response diff --git a/docs/docs/v6-upgrade-guidance.mdx b/docs/docs/v6-upgrade-guidance.mdx index bd1e4d8afbf..22c6d4e80e1 100644 --- a/docs/docs/v6-upgrade-guidance.mdx +++ b/docs/docs/v6-upgrade-guidance.mdx @@ -9,7 +9,7 @@ In CLI for Microsoft 365 we had several commands that were originally targeted a Command | Merged with --|-- `spo site classic add` | [spo site add](./cmd/spo/site/site-add.mdx) -`spo site classic list` | [spo site list](./cmd/spo/site/site-list.mdx) +`spo site classic list` | [spo site list](./cmd/spo/tenant/tenant-site-list.mdx) `spo site classic set` | [spo site set](./cmd/spo/site/site-set.mdx) To fix a resulting issue with the `spo site list` command, the default value of the `type` option of that command has been removed. diff --git a/docs/docs/v7-upgrade-guidance.mdx b/docs/docs/v7-upgrade-guidance.mdx index c534e802736..7ceb7f36255 100644 --- a/docs/docs/v7-upgrade-guidance.mdx +++ b/docs/docs/v7-upgrade-guidance.mdx @@ -128,7 +128,7 @@ Update your scripts to use the new options. Verify that your scripts work as int ### Removed `deleted` flag from `spo site list` command -In the previous versions of CLI for Microsoft 365, you could get deleted sites from your SharePoint tenant using the command [spo site list](./cmd/spo/site/site-list.mdx) by providing the flag `--deleted`. In the latest release, we have removed this option from the command. To get all deleted sites in your tenant, you can now use the command [spo tenant recyclebinitem list](./cmd/spo/tenant/tenant-recyclebinitem-list.mdx). +In the previous versions of CLI for Microsoft 365, you could get deleted sites from your SharePoint tenant using the command [spo site list](./cmd/spo/tenant/tenant-site-list.mdx) by providing the flag `--deleted`. In the latest release, we have removed this option from the command. To get all deleted sites in your tenant, you can now use the command [spo tenant recyclebinitem list](./cmd/spo/tenant/tenant-recyclebinitem-list.mdx). #### What action do I need to take? diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 7b141310973..575597b1260 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -3409,7 +3409,7 @@ const sidebars: SidebarsConfig = { { type: 'doc', label: 'site list', - id: 'cmd/spo/site/site-list' + id: 'cmd/spo/tenant/tenant-site-list' }, { type: 'doc', @@ -3757,6 +3757,11 @@ const sidebars: SidebarsConfig = { label: 'tenant site archive', id: 'cmd/spo/tenant/tenant-site-archive' }, + { + type: 'doc', + label: 'tenant site list', + id: 'cmd/spo/tenant/tenant-site-list' + }, { type: 'doc', label: 'tenant site rename', diff --git a/src/index.spec.ts b/src/index.spec.ts index e85381a1405..1606309b0ad 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -83,6 +83,7 @@ describe('Lazy loading commands', () => { 'search externalconnection remove', 'search externalconnection schema add', 'spo folder rename', + 'spo site list', 'spo page template remove', 'spo sp grant add', 'spo sp grant list', diff --git a/src/m365/spo/commands.ts b/src/m365/spo/commands.ts index 990c4988471..e6b18408499 100644 --- a/src/m365/spo/commands.ts +++ b/src/m365/spo/commands.ts @@ -323,8 +323,9 @@ export default { TENANT_SETTINGS_LIST: `${prefix} tenant settings list`, TENANT_SETTINGS_SET: `${prefix} tenant settings set`, TENANT_SITE_ARCHIVE: `${prefix} tenant site archive`, - TENANT_SITE_MEMBERSHIP_LIST: `${prefix} tenant site membership list`, + TENANT_SITE_LIST: `${prefix} tenant site list`, TENANT_SITE_RENAME: `${prefix} tenant site rename`, + TENANT_SITE_MEMBERSHIP_LIST: `${prefix} tenant site membership list`, TENANT_SITE_UNARCHIVE: `${prefix} tenant site unarchive`, TERM_ADD: `${prefix} term add`, TERM_GET: `${prefix} term get`, diff --git a/src/m365/spo/commands/tenant/SPOTenantSitePropertiesEnumerable.ts b/src/m365/spo/commands/tenant/SPOTenantSitePropertiesEnumerable.ts new file mode 100644 index 00000000000..b8539a605d5 --- /dev/null +++ b/src/m365/spo/commands/tenant/SPOTenantSitePropertiesEnumerable.ts @@ -0,0 +1,7 @@ +import { TenantSiteProperties } from './TenantSiteProperties.js'; + +export interface SPOTenantSitePropertiesEnumerable { + _Child_Items_: TenantSiteProperties[]; + NextStartIndex: number; + NextStartIndexFromSharePoint: string; +} \ No newline at end of file diff --git a/src/m365/spo/commands/tenant/TenantSiteProperties.ts b/src/m365/spo/commands/tenant/TenantSiteProperties.ts new file mode 100644 index 00000000000..4838dde9d8d --- /dev/null +++ b/src/m365/spo/commands/tenant/TenantSiteProperties.ts @@ -0,0 +1,6 @@ +export interface TenantSiteProperties { + Status: string; + Title: string; + Url: string; + SiteId?: string; +} \ No newline at end of file diff --git a/src/m365/spo/commands/site/site-list.spec.ts b/src/m365/spo/commands/tenant/tenant-site-list.spec.ts similarity index 99% rename from src/m365/spo/commands/site/site-list.spec.ts rename to src/m365/spo/commands/tenant/tenant-site-list.spec.ts index 56f833d9d5d..f0b5c2d0a93 100644 --- a/src/m365/spo/commands/site/site-list.spec.ts +++ b/src/m365/spo/commands/tenant/tenant-site-list.spec.ts @@ -13,9 +13,9 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import { spo } from '../../../../utils/spo.js'; import commands from '../../commands.js'; -import command from './site-list.js'; +import command from './tenant-site-list.js'; -describe(commands.SITE_LIST, () => { +describe(commands.TENANT_SITE_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; @@ -61,7 +61,7 @@ describe(commands.SITE_LIST, () => { }); it('has correct name', () => { - assert.strictEqual(command.name, commands.SITE_LIST); + assert.strictEqual(command.name, commands.TENANT_SITE_LIST); }); it('has a description', () => { @@ -599,4 +599,9 @@ describe(commands.SITE_LIST, () => { await assert.rejects(command.action(logger, { options: { debug: true, filter: "Url like 'ctest'" } } as any), new CommandError('An error has occurred')); }); + + it('defines alias', () => { + const alias = command.alias(); + assert.notStrictEqual(typeof alias, 'undefined'); + }); }); diff --git a/src/m365/spo/commands/site/site-list.ts b/src/m365/spo/commands/tenant/tenant-site-list.ts similarity index 92% rename from src/m365/spo/commands/site/site-list.ts rename to src/m365/spo/commands/tenant/tenant-site-list.ts index 3b92d454aca..35619afaeea 100644 --- a/src/m365/spo/commands/site/site-list.ts +++ b/src/m365/spo/commands/tenant/tenant-site-list.ts @@ -6,8 +6,8 @@ import { formatting } from '../../../../utils/formatting.js'; import { ClientSvcResponse, ClientSvcResponseContents, FormDigestInfo, spo } from '../../../../utils/spo.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -import { SiteProperties } from './SiteProperties.js'; -import { SPOSitePropertiesEnumerable } from './SPOSitePropertiesEnumerable.js'; +import { TenantSiteProperties } from './TenantSiteProperties.js'; +import { SPOTenantSitePropertiesEnumerable } from './SPOTenantSitePropertiesEnumerable.js'; interface CommandArgs { options: Options; @@ -20,11 +20,11 @@ interface Options extends GlobalOptions { includeOneDriveSites?: boolean; } -class SpoSiteListCommand extends SpoCommand { - private allSites?: SiteProperties[]; +class SpoTenantSiteListCommand extends SpoCommand { + private allSites?: TenantSiteProperties[]; public get name(): string { - return commands.SITE_LIST; + return commands.TENANT_SITE_LIST; } public get description(): string { @@ -95,6 +95,10 @@ class SpoSiteListCommand extends SpoCommand { ); } + public alias(): string[] | undefined { + return [commands.SITE_LIST]; + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { const webTemplate: string = this.getWebTemplateId(args.options); const includeOneDriveSites: boolean = args.options.includeOneDriveSites || false; @@ -137,7 +141,7 @@ class SpoSiteListCommand extends SpoCommand { throw responseContent.ErrorInfo.ErrorMessage; } else { - const sites: SPOSitePropertiesEnumerable = json[json.length - 1]; + const sites: SPOTenantSitePropertiesEnumerable = json[json.length - 1]; this.allSites!.push(...sites._Child_Items_); if (sites.NextStartIndexFromSharePoint) { @@ -169,4 +173,4 @@ class SpoSiteListCommand extends SpoCommand { } } -export default new SpoSiteListCommand(); \ No newline at end of file +export default new SpoTenantSiteListCommand(); \ No newline at end of file From 8fde5f409e7073da555d1bf1d1257b700552a18e Mon Sep 17 00:00:00 2001 From: Jwaegebaert <38426621+Jwaegebaert@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:21:33 +0200 Subject: [PATCH 10/42] Adds command 'viva engage community user list'. Closes #6295 --- .../engage/engage-community-user-list.mdx | 123 ++++++++ docs/src/config/sidebars.ts | 5 + src/m365/viva/commands.ts | 1 + src/m365/viva/commands/engage/Community.ts | 7 +- .../engage/engage-community-user-list.spec.ts | 278 ++++++++++++++++++ .../engage/engage-community-user-list.ts | 127 ++++++++ src/utils/vivaEngage.spec.ts | 192 ++++++++++++ src/utils/vivaEngage.ts | 73 +++++ 8 files changed, 803 insertions(+), 3 deletions(-) create mode 100644 docs/docs/cmd/viva/engage/engage-community-user-list.mdx create mode 100644 src/m365/viva/commands/engage/engage-community-user-list.spec.ts create mode 100644 src/m365/viva/commands/engage/engage-community-user-list.ts create mode 100644 src/utils/vivaEngage.spec.ts create mode 100644 src/utils/vivaEngage.ts diff --git a/docs/docs/cmd/viva/engage/engage-community-user-list.mdx b/docs/docs/cmd/viva/engage/engage-community-user-list.mdx new file mode 100644 index 00000000000..689d26a048d --- /dev/null +++ b/docs/docs/cmd/viva/engage/engage-community-user-list.mdx @@ -0,0 +1,123 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# viva engage community user list + +Lists all users within a specified Microsoft 365 Viva Engage community + +## Usage + +```sh +m365 viva engage community user list [options] +``` + +## Options + +```md definition-list +`--communityId [communityId]` +: The ID of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`. + +`-n, --communityDisplayName [communityDisplayName]` +: The display name of the Viva Engage community. Specify `communityId`, `communityDisplayName` or `entraGroupId`. + +`--entraGroupId [entraGroupId]` +: The ID of the Microsoft 365 group. Specify `communityId`, `communityDisplayName` or `entraGroupId`. + +`-r, --role [role]` +: Filter the results to only users with the given role: `Admin`, `Member`. +``` + + + +## Examples + +List all users from a community specified by ID. + +```sh +m365 viva engage community user list --communityId eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9 +``` + +List all admins from a community specified by display name. + +```sh +m365 viva engage community user list --communityDisplayName "All company" --role Admin +``` + +List all members from a community specified by group ID. + +```sh +m365 viva engage community user list --entraGroupId b6c35b51-ebca-445c-885a-63a67d24cb53 --role Member +``` + +## Response + +### Standard response + + + + + ```json + [ + { + "id": "da634de7-d23c-4419-ab83-fcd395b4ebd0", + "businessPhones": [ + "123-555-1215" + ], + "displayName": "Anton Johansen", + "givenName": "Anton", + "jobTitle": "IT Manager", + "mail": null, + "mobilePhone": "123-555-6645", + "officeLocation": "123455", + "preferredLanguage": null, + "surname": "Johansen", + "userPrincipalName": "Anton.Johansen@contoso.onmicrosoft.com", + "roles": [ + "Admin" + ] + } + ] + ``` + + + + + ```text + id displayName userPrincipalName roles + ------------------------------------ ---------------- ----------------------------------------- ------ + da634de7-d23c-4419-ab83-fcd395b4ebd0 Anton Johansen Anton.Johansen@contoso.onmicrosoft.com Admin + ``` + + + + + ```csv + id,displayName,givenName,jobTitle,mail,mobilePhone,officeLocation,preferredLanguage,surname,userPrincipalName + da634de7-d23c-4419-ab83-fcd395b4ebd0,Anton Johansen,Anton,IT Manager,,123-555-6645,123455,,Johansen,Anton.Johansen@contoso.onmicrosoft.com + ``` + + + + + ```md + # viva engage community user list --entraGroupId "b6c35b51-ebca-445c-885a-63a67d24cb53" + + Date: 19/9/2024 + + ## Anton Johansen (da634de7-d23c-4419-ab83-fcd395b4ebd0) + + Property | Value + ---------|------- + id | da634de7-d23c-4419-ab83-fcd395b4ebd0 + displayName | Anton Johansen + givenName | Anton + jobTitle | IT Manager + mobilePhone | 123-555-6645 + officeLocation | 123455 + surname | Johansen + userPrincipalName | Anton.Johansen@contoso.onmicrosoft.com + ``` + + + diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 575597b1260..f829ed47fad 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -4508,6 +4508,11 @@ const sidebars: SidebarsConfig = { label: 'engage community list', id: 'cmd/viva/engage/engage-community-list' }, + { + type: 'doc', + label: 'engage community user list', + id: 'cmd/viva/engage/engage-community-user-list' + }, { type: 'doc', label: 'engage group list', diff --git a/src/m365/viva/commands.ts b/src/m365/viva/commands.ts index f2732749839..4c431736913 100644 --- a/src/m365/viva/commands.ts +++ b/src/m365/viva/commands.ts @@ -5,6 +5,7 @@ export default { ENGAGE_COMMUNITY_ADD: `${prefix} engage community add`, ENGAGE_COMMUNITY_GET: `${prefix} engage community get`, ENGAGE_COMMUNITY_LIST: `${prefix} engage community list`, + ENGAGE_COMMUNITY_USER_LIST: `${prefix} engage community user list`, ENGAGE_GROUP_LIST: `${prefix} engage group list`, ENGAGE_GROUP_USER_ADD: `${prefix} engage group user add`, ENGAGE_GROUP_USER_REMOVE: `${prefix} engage group user remove`, diff --git a/src/m365/viva/commands/engage/Community.ts b/src/m365/viva/commands/engage/Community.ts index ed5df596493..bde8d4838fc 100644 --- a/src/m365/viva/commands/engage/Community.ts +++ b/src/m365/viva/commands/engage/Community.ts @@ -1,6 +1,7 @@ export interface Community { - id: string; - displayName: string; + id?: string; + displayName?: string; description?: string; - privacy: string; + privacy?: string; + groupId?: string; } \ No newline at end of file diff --git a/src/m365/viva/commands/engage/engage-community-user-list.spec.ts b/src/m365/viva/commands/engage/engage-community-user-list.spec.ts new file mode 100644 index 00000000000..2d768c2c090 --- /dev/null +++ b/src/m365/viva/commands/engage/engage-community-user-list.spec.ts @@ -0,0 +1,278 @@ + +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './engage-community-user-list.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { z } from 'zod'; +import { cli } from '../../../../cli/cli.js'; +import { vivaEngage } from '../../../../utils/vivaEngage.js'; + +describe(commands.ENGAGE_COMMUNITY_USER_LIST, () => { + const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9'; + const communityDisplayName = 'All company'; + const entraGroupId = 'b6c35b51-ebca-445c-885a-63a67d24cb53'; + const membersAPIResult = [ + { + "id": "1deb8814-8130-451d-8fcb-849dc7ed47e5", + "businessPhones": [ + "123-555-1215" + ], + "displayName": "Samu Tolonen", + "givenName": "Samu", + "jobTitle": "IT Manager", + "mail": null, + "mobilePhone": "123-555-6645", + "officeLocation": "123455", + "preferredLanguage": null, + "surname": "Tolonen", + "userPrincipalName": "Samu.Tolonen@contoso.onmicrosoft.com" + } + ]; + const membersResult = [{ ...membersAPIResult[0], roles: ["Member"] }]; + const adminsAPIResult = [ + { + "id": "da634de7-d23c-4419-ab83-fcd395b4ebd0", + "businessPhones": [ + "123-555-1215" + ], + "displayName": "Anton Johansen", + "givenName": "Anton", + "jobTitle": "IT Manager", + "mail": null, + "mobilePhone": "123-555-6645", + "officeLocation": "123455", + "preferredLanguage": null, + "surname": "Johansen", + "userPrincipalName": "Anton.Johansen@contoso.onmicrosoft.com" + } + ]; + const adminsResult = [{ ...adminsAPIResult[0], roles: ["Admin"] }]; + const community = { + id: communityId, + displayName: communityDisplayName, + privacy: 'Public', + groupId: entraGroupId + }; + + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + let commandOptionsSchema: z.ZodTypeAny; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + commandInfo = cli.getCommandInfo(command); + commandOptionsSchema = commandInfo.command.getSchemaToParse()!; + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get, + vivaEngage.getCommunityById + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.ENGAGE_COMMUNITY_USER_LIST); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('fails validation if entraGroupId is not a valid GUID', () => { + const actual = commandOptionsSchema.safeParse({ + entraGroupId: 'invalid' + }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if communityId, communityDisplayName or entraGroupId are not specified', () => { + const actual = commandOptionsSchema.safeParse({}); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if communityId, communityDisplayName and entraGroupId are specified', () => { + const actual = commandOptionsSchema.safeParse({ + communityId: communityId, + communityDisplayName: communityDisplayName, + entraGroupId: entraGroupId + }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if incorrect role value is specified', () => { + const actual = commandOptionsSchema.safeParse({ + communityId: communityId, + role: 'invalid' + }); + assert.notStrictEqual(actual.success, true); + }); + + it('passes validation if communityId is specified', () => { + const actual = commandOptionsSchema.safeParse({ + communityId: communityId + }); + assert.strictEqual(actual.success, true); + }); + + it('passes validation if entraGroupId is specified with a proper GUID', () => { + const actual = commandOptionsSchema.safeParse({ + entraGroupId: entraGroupId + }); + assert.strictEqual(actual.success, true); + }); + + it('passes validation if communityDisplayName is specified', () => { + const actual = commandOptionsSchema.safeParse({ + communityDisplayName: communityDisplayName + }); + assert.strictEqual(actual.success, true); + }); + + it('passes validation if role is specified with a proper value', () => { + const actual = commandOptionsSchema.safeParse({ + communityId: communityId, + role: 'Admin' + }); + assert.strictEqual(actual.success, true); + }); + + it('correctly gets the list of users in the community by entraGroupId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners`) { + return { value: adminsAPIResult }; + } + + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members`) { + return { value: membersAPIResult }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { entraGroupId: entraGroupId, verbose: true } }); + assert(loggerLogSpy.calledWith([...adminsResult, ...membersResult])); + }); + + it('correctly gets the list of users in the community by communityId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners`) { + return { value: adminsAPIResult }; + } + + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members`) { + return { value: membersAPIResult }; + } + + throw 'Invalid request'; + }); + + sinon.stub(vivaEngage, 'getCommunityById').resolves(community); + + await command.action(logger, { options: { communityId: communityId, verbose: true } }); + assert(loggerLogSpy.calledWith([...adminsResult, ...membersResult])); + }); + + it('correctly gets the list of users in the community by communityName', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners`) { + return { value: adminsAPIResult }; + } + + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members`) { + return { value: membersAPIResult }; + } + + throw 'Invalid request'; + }); + + sinon.stub(vivaEngage, 'getCommunityByDisplayName').resolves(community); + + await command.action(logger, { options: { communityDisplayName: communityDisplayName, verbose: true } }); + assert(loggerLogSpy.calledWith([...adminsResult, ...membersResult])); + }); + + it('correctly gets the list of members in the community by entraGroupId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners`) { + return { value: adminsAPIResult }; + } + + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members`) { + return { value: membersAPIResult }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { entraGroupId: entraGroupId, role: 'Member' } }); + assert(loggerLogSpy.calledWith(membersResult)); + }); + + it('correctly gets the list of admins in the community by communityId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners`) { + return { value: adminsAPIResult }; + } + + if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members`) { + return { value: membersAPIResult }; + } + + throw 'Invalid request'; + }); + + sinon.stub(vivaEngage, 'getCommunityById').resolves(community); + + await command.action(logger, { options: { communityId: communityId, role: 'Admin' } }); + assert(loggerLogSpy.calledWith(adminsResult)); + }); + + it('correctly handles error', async () => { + const errorMessage = 'Bad request.'; + sinon.stub(request, 'get').rejects({ + error: { + message: errorMessage + } + }); + + await assert.rejects(command.action(logger, { options: { id: 'invalid', verbose: true } }), + new CommandError(errorMessage)); + }); +}); \ No newline at end of file diff --git a/src/m365/viva/commands/engage/engage-community-user-list.ts b/src/m365/viva/commands/engage/engage-community-user-list.ts new file mode 100644 index 00000000000..4c7e3dfb8a0 --- /dev/null +++ b/src/m365/viva/commands/engage/engage-community-user-list.ts @@ -0,0 +1,127 @@ +import { z } from 'zod'; +import { Logger } from '../../../../cli/Logger.js'; +import { globalOptionsZod } from '../../../../Command.js'; +import { zod } from '../../../../utils/zod.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; +import { validation } from '../../../../utils/validation.js'; +import { vivaEngage } from '../../../../utils/vivaEngage.js'; +import { CliRequestOptions } from '../../../../request.js'; +import { User } from '@microsoft/microsoft-graph-types'; +import { odata } from '../../../../utils/odata.js'; + +const options = globalOptionsZod + .extend({ + communityId: z.string().optional(), + communityDisplayName: zod.alias('n', z.string().optional()), + entraGroupId: z.string() + .refine(name => validation.isValidGuid(name), name => ({ + message: `'${name}' is not a valid GUID.` + })).optional(), + role: zod.alias('r', z.enum(['Admin', 'Member']).optional()) + }) + .strict(); +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +interface ExtendedUser extends User { + roles: string[]; +} + +class VivaEngageCommunityUserListCommand extends GraphCommand { + + public get name(): string { + return commands.ENGAGE_COMMUNITY_USER_LIST; + } + + public get description(): string { + return 'Lists all users within a specified Microsoft 365 Viva Engage community'; + } + + public get schema(): z.ZodTypeAny { + return options; + } + + public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { + return schema + .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, { + message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.' + }) + .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, { + message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.' + }); + } + + public defaultProperties(): string[] | undefined { + return ['id', 'displayName', 'userPrincipalName', 'roles']; + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + try { + if (this.verbose) { + await logger.logToStderr('Getting list of users in community...'); + } + + let entraGroupId = args.options.entraGroupId; + + if (args.options.communityDisplayName) { + const community = await vivaEngage.getCommunityByDisplayName(args.options.communityDisplayName, ['groupId']); + entraGroupId = community.groupId; + } + + if (args.options.communityId) { + const community = await vivaEngage.getCommunityById(args.options.communityId, ['groupId']); + entraGroupId = community.groupId; + } + + const requestOptions: CliRequestOptions = { + url: `${this.resource}/v1.0/groups/${entraGroupId}/members`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + const members = await odata.getAllItems(requestOptions); + + requestOptions.url = `${this.resource}/v1.0/groups/${entraGroupId}/owners`; + const owners = await odata.getAllItems(requestOptions); + + const extendedMembers: ExtendedUser[] = members.map(m => { + return { + ...m, + roles: ['Member'] + }; + }); + + const extendedOwners: ExtendedUser[] = owners.map(o => { + return { + ...o, + roles: ['Admin'] + }; + }); + + let users: ExtendedUser[] = []; + if (args.options.role) { + if (args.options.role === 'Member') { + users = users.concat(extendedMembers); + } + if (args.options.role === 'Admin') { + users = users.concat(extendedOwners); + } + } + else { + users = extendedOwners.concat(extendedMembers); + } + + await logger.log(users); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new VivaEngageCommunityUserListCommand(); \ No newline at end of file diff --git a/src/utils/vivaEngage.spec.ts b/src/utils/vivaEngage.spec.ts new file mode 100644 index 00000000000..8df9f11a0d5 --- /dev/null +++ b/src/utils/vivaEngage.spec.ts @@ -0,0 +1,192 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import { cli } from '../cli/cli.js'; +import request from '../request.js'; +import { sinonUtil } from './sinonUtil.js'; +import { vivaEngage } from './vivaEngage.js'; +import { formatting } from './formatting.js'; +import { settingsNames } from '../settingsNames.js'; + +describe('utils/vivaEngage', () => { + const displayName = 'All Company'; + const invalidDisplayName = 'All Compayn'; + const entraGroupId = '0bed8b86-5026-4a93-ac7d-56750cc099f1'; + const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9'; + const communityResponse = { + "id": "eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9", + "description": "This is the default group for everyone in the network", + "displayName": "All Company", + "privacy": "Public", + "groupId": "0bed8b86-5026-4a93-ac7d-56750cc099f1" + }; + const anotherCommunityResponse = { + "id": "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw", + "description": "Test only", + "displayName": "All Company", + "privacy": "Private", + "groupId": "0bed8b86-5026-4a93-ac7d-56750cc099f1" + }; + + afterEach(() => { + sinonUtil.restore([ + request.get, + cli.getSettingWithDefaultValue, + cli.handleMultipleResultsFound + ]); + }); + + it('correctly get single community id by name using getCommunityByDisplayName', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) { + return { + value: [ + { + id: communityId + } + ] + }; + } + + return 'Invalid Request'; + }); + + const actual = await vivaEngage.getCommunityByDisplayName(displayName, ['id']); + assert.deepStrictEqual(actual, { id: communityId }); + }); + + it('handles selecting single community when multiple communities with the specified name found using getCommunityByDisplayName and cli is set to prompt', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) { + return { + value: [ + { id: communityId }, + { id: "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw" } + ] + }; + } + + return 'Invalid Request'; + }); + + sinon.stub(cli, 'handleMultipleResultsFound').resolves({ id: communityId }); + + const actual = await vivaEngage.getCommunityByDisplayName(displayName, ['id']); + assert.deepStrictEqual(actual, { id: 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9' }); + }); + + it('throws error message when no community was found using getCommunityByDisplayName', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(invalidDisplayName)}'&$select=id`) { + return { value: [] }; + } + + throw 'Invalid Request'; + }); + + await assert.rejects(vivaEngage.getCommunityByDisplayName(invalidDisplayName, ['id'])), Error(`The specified Viva Engage community '${invalidDisplayName}' does not exist.`); + }); + + it('throws error message when multiple communities were found using getCommunityByDisplayName', async () => { + sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { + if (settingName === settingsNames.prompt) { + return false; + } + + return defaultValue; + }); + + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) { + return { + value: [ + { id: communityId }, + { id: "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw" } + ] + }; + } + + return 'Invalid Request'; + }); + + await assert.rejects(vivaEngage.getCommunityByDisplayName(displayName, ['id']), + Error(`Multiple Viva Engage communities with name '${displayName}' found. Found: ${communityResponse.id}, ${anotherCommunityResponse.id}.`)); + }); + + it('correctly get single community by group id using getCommunityByEntraGroupId', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === 'https://graph.microsoft.com/v1.0/employeeExperience/communities?$select=id,groupId') { + return { + value: [ + { + id: communityId, + groupId: entraGroupId + } + ] + }; + } + + return 'Invalid Request'; + }); + + const actual = await vivaEngage.getCommunityByEntraGroupId(entraGroupId, ['id']); + assert.deepStrictEqual(actual, { id: communityId, groupId: entraGroupId }); + }); + + it('correctly get single community by group id using getCommunityByEntraGroupId and multiple properties', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === 'https://graph.microsoft.com/v1.0/employeeExperience/communities?$select=id,groupId,displayName') { + return { + value: [ + { + id: communityId, + groupId: entraGroupId, + displayName: displayName + } + ] + }; + } + + return 'Invalid Request'; + }); + + const actual = await vivaEngage.getCommunityByEntraGroupId(entraGroupId, ['id', 'groupId', 'displayName']); + assert.deepStrictEqual(actual, { id: communityId, groupId: entraGroupId, displayName: displayName }); + }); + + it('throws error message when no community was found using getCommunityByEntraGroupId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/employeeExperience/communities?$select=id,groupId') { + return { value: [] }; + } + + throw 'Invalid Request'; + }); + + await assert.rejects(vivaEngage.getCommunityByEntraGroupId(entraGroupId, ['id'])), Error(`The Microsoft Entra group with id '${entraGroupId}' is not associated with any Viva Engage community.`); + }); + + it('correctly gets Entra group ID by community ID using getEntraGroupIdByCommunityId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}?$select=groupId`) { + return { groupId: entraGroupId }; + } + + throw 'Invalid Request'; + }); + + const actual = await vivaEngage.getCommunityById(communityId, ['groupId']); + assert.deepStrictEqual(actual, { groupId: '0bed8b86-5026-4a93-ac7d-56750cc099f1' }); + }); + + it('throws error message when no Entra group ID was found using getCommunityById', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}?$select=groupId`) { + return null; + } + + throw 'Invalid Request'; + }); + + await assert.rejects(vivaEngage.getCommunityById(communityId, ['groupId'])), Error(`The specified Viva Engage community with ID '${communityId}' does not exist.`); + }); +}); \ No newline at end of file diff --git a/src/utils/vivaEngage.ts b/src/utils/vivaEngage.ts new file mode 100644 index 00000000000..40ba84ddc2d --- /dev/null +++ b/src/utils/vivaEngage.ts @@ -0,0 +1,73 @@ +import { cli } from '../cli/cli.js'; +import { Community } from '../m365/viva/commands/engage/Community.js'; +import request, { CliRequestOptions } from '../request.js'; +import { formatting } from './formatting.js'; +import { odata } from './odata.js'; + +export const vivaEngage = { + /** + * Get Viva Engage group ID by community ID. + * @param communityId The ID of the Viva Engage community. + * @returns The ID of the Viva Engage group. + * @returns The Viva Engage community. + */ + async getCommunityById(communityId: string, selectProperties: string[]): Promise { + const requestOptions: CliRequestOptions = { + url: `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}?$select=${selectProperties.join(',')}`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + + const community = await request.get(requestOptions); + + if (!community) { + throw `The specified Viva Engage community with ID '${communityId}' does not exist.`; + } + + return community; + }, + + /** + * Get Viva Engage community by display name. + * @param displayName Community display name. + * @param selectProperties Properties to select. + * @returns The Viva Engage community. + */ + async getCommunityByDisplayName(displayName: string, selectProperties: string[]): Promise { + const communities = await odata.getAllItems(`https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=${selectProperties.join(',')}`); + + if (communities.length === 0) { + throw `The specified Viva Engage community '${displayName}' does not exist.`; + } + + if (communities.length > 1) { + const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', communities); + const selectedCommunity = await cli.handleMultipleResultsFound(`Multiple Viva Engage communities with name '${displayName}' found.`, resultAsKeyValuePair); + return selectedCommunity; + } + + return communities[0]; + }, + + /** + * Get Viva Engage community by Microsoft Entra group ID. + * Note: The Graph API doesn't support filtering by groupId, so we need to retrieve all communities and filter them in memory. + * @param entraGroupId The ID of the Microsoft Entra group. + * @param selectProperties Properties to select. + * @returns The Viva Engage community. + */ + async getCommunityByEntraGroupId(entraGroupId: string, selectProperties: string[]): Promise { + const properties = selectProperties.includes('groupId') ? selectProperties : [...selectProperties, 'groupId']; + const communities = await odata.getAllItems(`https://graph.microsoft.com/v1.0/employeeExperience/communities?$select=${properties.join(',')}`); + + const filteredCommunity = communities.find(c => c.groupId === entraGroupId); + + if (!filteredCommunity) { + throw `The Microsoft Entra group with id '${entraGroupId}' is not associated with any Viva Engage community.`; + } + + return filteredCommunity; + } +}; \ No newline at end of file From 7668a3b1f13dca06940b8980e678ad1e6735eb3e Mon Sep 17 00:00:00 2001 From: Saurabh Date: Fri, 3 May 2024 11:26:16 +0200 Subject: [PATCH 11/42] Adds command 'spo folder sharinglink set'. Closes #5964 --- .../cmd/spo/folder/folder-sharinglink-set.mdx | 116 ++++++++++++ docs/src/config/sidebars.ts | 5 + src/m365/spo/commands.ts | 1 + .../folder/folder-sharinglink-set.spec.ts | 176 ++++++++++++++++++ .../commands/folder/folder-sharinglink-set.ts | 128 +++++++++++++ 5 files changed, 426 insertions(+) create mode 100644 docs/docs/cmd/spo/folder/folder-sharinglink-set.mdx create mode 100644 src/m365/spo/commands/folder/folder-sharinglink-set.spec.ts create mode 100644 src/m365/spo/commands/folder/folder-sharinglink-set.ts diff --git a/docs/docs/cmd/spo/folder/folder-sharinglink-set.mdx b/docs/docs/cmd/spo/folder/folder-sharinglink-set.mdx new file mode 100644 index 00000000000..581ce627da9 --- /dev/null +++ b/docs/docs/cmd/spo/folder/folder-sharinglink-set.mdx @@ -0,0 +1,116 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spo folder sharinglink set + +Updates a sharing link of a folder + +## Usage + +```sh +m365 spo folder sharinglink set [options] +``` + +## Options + +```md definition-list +`-u, --webUrl ` +: The URL of the site where the folder is located. + +`--folderUrl [folderUrl]` +: The server- or site-relative decoded URL of the folder. Specify either `folderUrl` or `folderId` but not both. + +`--folderId [folderId]` +: The unique ID (GUID) of the folder. Specify either `folderUrl` or `folderId` but not both. + +`--id ` +: The sharing link ID. + +`--expirationDateTime ` +: The date and time to set the expiration. This should be defined as a valid ISO 8601 string. This option only works for anonymous links. +``` + + + +## Examples + +Updates the expiration datetime of an anonymous sharing link for a folder specific by folder ID. + +```sh +m365 spo folder sharinglink set --webUrl https://contoso.sharepoint.com/sites/demo --folderId daebb04b-a773-4baa-b1d1-3625418e3234 --id 7c9f97c9-1bda-433c-9364-bb83e81771ee --expirationDateTime '2022-11-30T00:00:00Z' +``` + +Updates the expiration datetime of an anonymous sharing link for a folder specific by folder URL. + +```sh +m365 spo folder sharinglink set --webUrl https://contoso.sharepoint.com/sites/demo --folderUrl /sites/demo/shared%20documents/Folder --id 7c9f97c9-1bda-433c-9364-bb83e81771ee --expirationDateTime '2022-11-30T00:00:00Z' +``` + +## Response + + + + + ```json + { + "id": "bd1481e9-958b-4c1a-a33c-fb021f4ed444", + "roles": [ + "write" + ], + "shareId": "u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc", + "expirationDateTime": "2024-05-05T16:57:00Z", + "hasPassword": false, + "grantedToIdentitiesV2": [], + "grantedToIdentities": [], + "link": { + "scope": "anonymous", + "type": "edit", + "webUrl": "https://contoso.sharepoint.com/:f:/g/Ep-ZFPqvbElBsgYraF2A4mcBfV3P7qM4xfUUrQdwgIye4g", + "preventsDownload": false + } + } + ``` + + + + + ```text + expirationDateTime : 2024-05-05T16:57:00Z + grantedToIdentities : [] + grantedToIdentitiesV2: [] + hasPassword : false + id : bd1481e9-958b-4c1a-a33c-fb021f4ed444 + link : {"scope":"anonymous","type":"edit","webUrl":"https://contoso.sharepoint.com/:f:/g/Ep-ZFPqvbElBsgYraF2A4mcBfV3P7qM4xfUUrQdwgIye4g","preventsDownload":false} + roles : ["write"] + shareId : u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc + ``` + + + + + ```csv + id,shareId,expirationDateTime,hasPassword + bd1481e9-958b-4c1a-a33c-fb021f4ed444,u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc,2024-05-05T16:57:00Z, + ``` + + + + + ```md + # spo folder sharinglink set --webUrl "https://contoso.sharepoint.com" --folderUrl "/shared documents/f1" --id "bd1481e9-958b-4c1a-a33c-fb021f4ed444" --expirationDateTime "2024-05-05T16:57:00.000Z" + + Date: 03/05/2024 + + ## bd1481e9-958b-4c1a-a33c-fb021f4ed444 + + Property | Value + ---------|------- + id | bd1481e9-958b-4c1a-a33c-fb021f4ed444 + shareId | u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc + expirationDateTime | 2024-05-05T16:57:00Z + hasPassword | false + ``` + + + diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index f829ed47fad..7dc3dcc2239 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -2632,6 +2632,11 @@ const sidebars: SidebarsConfig = { type: 'doc', label: 'folder sharinglink remove', id: 'cmd/spo/folder/folder-sharinglink-remove' + }, + { + type: 'doc', + label: 'folder sharinglink set', + id: 'cmd/spo/folder/folder-sharinglink-set' } ] }, diff --git a/src/m365/spo/commands.ts b/src/m365/spo/commands.ts index e6b18408499..9ad1de6b0f2 100644 --- a/src/m365/spo/commands.ts +++ b/src/m365/spo/commands.ts @@ -105,6 +105,7 @@ export default { FOLDER_SHARINGLINK_GET: `${prefix} folder sharinglink get`, FOLDER_SHARINGLINK_LIST: `${prefix} folder sharinglink list`, FOLDER_SHARINGLINK_REMOVE: `${prefix} folder sharinglink remove`, + FOLDER_SHARINGLINK_SET: `${prefix} folder sharinglink set`, GET: `${prefix} get`, GROUP_ADD: `${prefix} group add`, GROUP_GET: `${prefix} group get`, diff --git a/src/m365/spo/commands/folder/folder-sharinglink-set.spec.ts b/src/m365/spo/commands/folder/folder-sharinglink-set.spec.ts new file mode 100644 index 00000000000..22e449f4413 --- /dev/null +++ b/src/m365/spo/commands/folder/folder-sharinglink-set.spec.ts @@ -0,0 +1,176 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandError } from '../../../../Command.js'; +import { telemetry } from '../../../../telemetry.js'; +import { Logger } from '../../../../cli/Logger.js'; +import request from '../../../../request.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import commands from '../../commands.js'; +import command from './folder-sharinglink-set.js'; +import { spo } from '../../../../utils/spo.js'; +import { drive } from '../../../../utils/drive.js'; +import { Drive } from '@microsoft/microsoft-graph-types'; + +describe(commands.FOLDER_SHARINGLINK_SET, () => { + let log: any[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + + const webUrl = 'https://contoso.sharepoint.com/sites/project-x'; + const folderId = 'f09c4efe-b8c0-4e89-a166-03418661b89b'; + const folderUrl = '/sites/project-x/shared documents/folder1'; + const siteId = '0f9b8f4f-0e8e-4630-bb0a-501442db9b64'; + const driveId = '013TMHP6UOOSLON57HT5GLKEU7R5UGWZVK'; + const itemId = 'b!T4-bD44OMEa7ClAUQtubZID9tc40pGJKpguycvELod_Gx-lo4ZQiRJ7vylonTufG'; + const id = 'ef1cddaa-b74a-4aae-8a7a-5c16b4da67f2'; + + const graphResponse = { + "id": "2a021f54-90a2-4016-b3b3-5f34d2e7d932", + "roles": [ + "read" + ], + "hasPassword": false, + "grantedToIdentitiesV2": [], + "grantedToIdentities": [], + "link": { + "scope": "anonymous", + "type": "view", + "webUrl": "https://contoso.sharepoint.com/:b:/s/pnpcoresdktestgroup/EY50lub3559MtRKfj2hrZqoBWnHOpGIcgi4gzw9XiWYJ-A", + "preventsDownload": false + } + }; + + const driveDetails: Drive = { + id: driveId, + webUrl: `${webUrl}/Shared%20Documents` + }; + + const getStubs: any = (options: any) => { + sinon.stub(spo, 'getFolderServerRelativeUrl').resolves(options.folderUrl); + sinon.stub(spo, 'getSiteId').resolves(options.siteId); + sinon.stub(drive, 'getDriveByUrl').resolves(options.drive); + sinon.stub(drive, 'getDriveItemId').resolves(options.itemId); + }; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + commandInfo = cli.getCommandInfo(command); + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get, + request.patch, + spo.getSiteId, + spo.getFolderServerRelativeUrl, + drive.getDriveByUrl, + drive.getDriveItemId + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.FOLDER_SHARINGLINK_SET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('fails validation if the webUrl option is not a valid SharePoint site URL', async () => { + const actual = await command.validate({ options: { webUrl: 'foo', folderId: folderId, id: id } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if the folderId option is not a valid GUID', async () => { + const actual = await command.validate({ options: { webUrl: webUrl, folderId: 'invalid', id: id } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if the expirationDateTime option is not a valid date', async () => { + const actual = await command.validate({ options: { webUrl: 'https://contoso.sharepoint.com', folderId: folderId, expirationDateTime: 'invalid date', id: id } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('passes validation if options are valid', async () => { + const actual = await command.validate({ options: { webUrl: 'https://contoso.sharepoint.com', folderId: folderId, expirationDateTime: '2024-05-05T16:57:00.000Z', id: id } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('updates a sharing link to a folder specified by the id', async () => { + getStubs({ folderUrl: folderUrl, siteId: siteId, drive: driveDetails, itemId: itemId }); + + sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/permissions/${id}`) { + return graphResponse; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { webUrl: webUrl, folderId: folderId, expirationDateTime: '2024-05-05T16:57:00.000Z', id: id, verbose: true } } as any); + assert(loggerLogSpy.calledWith(graphResponse)); + }); + + it('updates a sharing link to a folder specified by the URL', async () => { + getStubs({ folderUrl: folderUrl, siteId: siteId, drive: driveDetails, itemId: itemId }); + + sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/permissions/${id}`) { + return graphResponse; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { webUrl: webUrl, folderUrl: folderUrl, expirationDateTime: '2024-05-05T16:57:00.000Z', id: id, verbose: true } } as any); + assert(loggerLogSpy.calledWith(graphResponse)); + }); + + it('throws error when drive not found by url', async () => { + sinon.stub(spo, 'getFolderServerRelativeUrl').resolves(folderUrl); + sinon.stub(spo, 'getSiteId').resolves(siteId); + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/sites/${siteId}/drives?$select=webUrl,id`) { + return { + value: [] + }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { options: { webUrl: webUrl, folderUrl: folderUrl, expirationDateTime: '2024-05-05T16:57:00.000Z', id: id, verbose: true } } as any), + new CommandError(`Drive 'https://contoso.sharepoint.com/sites/project-x/shared%20documents/folder1' not found`)); + }); +}); \ No newline at end of file diff --git a/src/m365/spo/commands/folder/folder-sharinglink-set.ts b/src/m365/spo/commands/folder/folder-sharinglink-set.ts new file mode 100644 index 00000000000..993845ae9fc --- /dev/null +++ b/src/m365/spo/commands/folder/folder-sharinglink-set.ts @@ -0,0 +1,128 @@ +import { Logger } from '../../../../cli/Logger.js'; +import GlobalOptions from '../../../../GlobalOptions.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { spo } from '../../../../utils/spo.js'; +import { urlUtil } from '../../../../utils/urlUtil.js'; +import { drive } from '../../../../utils/drive.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import { Drive } from '@microsoft/microsoft-graph-types'; +import commands from '../../commands.js'; + +interface CommandArgs { + options: Options; +} + +interface Options extends GlobalOptions { + webUrl: string; + folderUrl?: string; + folderId?: string; + id: string; + expirationDateTime?: string; +} + +class SpoFolderSharingLinkSetCommand extends SpoCommand { + public get name(): string { + return commands.FOLDER_SHARINGLINK_SET; + } + + public get description(): string { + return 'Updates a specific sharing link to a folder'; + } + + constructor() { + super(); + + this.#initTelemetry(); + this.#initOptions(); + this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); + } + + #initTelemetry(): void { + this.telemetry.push((args: CommandArgs) => { + Object.assign(this.telemetryProperties, { + folderUrl: typeof args.options.folderUrl !== 'undefined', + folderId: typeof args.options.folderId !== 'undefined', + expirationDateTime: typeof args.options.expirationDateTime !== 'undefined' + }); + }); + } + + #initOptions(): void { + this.options.unshift( + { option: '-u, --webUrl ' }, + { option: '--folderUrl [folderUrl]' }, + { option: '--folderId [folderId]' }, + { option: '-i, --id ' }, + { option: '--expirationDateTime [expirationDateTime]' } + ); + } + + #initValidators(): void { + this.validators.push( + async (args: CommandArgs) => { + const isValidSharePointUrl: boolean | string = validation.isValidSharePointUrl(args.options.webUrl); + if (isValidSharePointUrl !== true) { + return isValidSharePointUrl; + } + + if (args.options.folderId && !validation.isValidGuid(args.options.folderId)) { + return `${args.options.folderId} is not a valid GUID`; + } + + if (args.options.expirationDateTime && !validation.isValidISODateTime(args.options.expirationDateTime)) { + return `${args.options.expirationDateTime} is not a valid ISO date string.`; + } + + return true; + } + ); + } + + #initOptionSets(): void { + this.optionSets.push({ options: ['folderId', 'folderUrl'] }); + } + + #initTypes(): void { + this.types.string.push('webUrl', 'folderId', 'folderUrl', 'id', 'expirationDateTime'); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (this.verbose) { + await logger.logToStderr(`Updating sharing link to a folder ${args.options.folderId || args.options.folderUrl}...`); + } + + try { + const relFolderUrl: string = await spo.getFolderServerRelativeUrl(args.options.webUrl, args.options.folderUrl, args.options.folderId, logger, args.options.verbose); + const absoluteFolderUrl: string = urlUtil.getAbsoluteUrl(args.options.webUrl, relFolderUrl); + const folderUrl: URL = new URL(absoluteFolderUrl); + + const siteId: string = await spo.getSiteId(args.options.webUrl); + const driveDetails: Drive = await drive.getDriveByUrl(siteId, folderUrl, logger, args.options.verbose); + const itemId: string = await drive.getDriveItemId(driveDetails, folderUrl, logger, args.options.verbose); + + const requestOptions: CliRequestOptions = { + url: `https://graph.microsoft.com/v1.0/drives/${driveDetails.id}/items/${itemId}/permissions/${args.options.id}`, + headers: { + accept: 'application/json;odata.metadata=none', + 'content-type': 'application/json' + }, + responseType: 'json', + data: { + expirationDateTime: args.options.expirationDateTime + } + }; + + const sharingLink = await request.patch(requestOptions); + + await logger.log(sharingLink); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new SpoFolderSharingLinkSetCommand(); \ No newline at end of file From f991f0c0c8a1a6a63d04a17a7823b9ead8a3f0f5 Mon Sep 17 00:00:00 2001 From: Mathijs Verbeeck Date: Sat, 26 Oct 2024 15:42:58 +0200 Subject: [PATCH 12/42] Updates release notes --- docs/docs/about/release-notes.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 673280c8939..1896e313663 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -12,11 +12,16 @@ sidebar_position: 3 - [spo page publish](../cmd/spo/page/page-publish.mdx) - publishes a modern page [#6420](https://github.com/pnp/cli-microsoft365/issues/6420) - [spo tenant site membership list](../cmd/spo/tenant/tenant-site-membership-list.mdx) - retrieves information about default site groups' membership [#5980](https://github.com/pnp/cli-microsoft365/issues/5980) +- [spo folder sharinglink set](../cmd/spo/folder/folder-sharinglink-set.mdx) - updates a specific sharing link of a folder [#5964](https://github.com/pnp/cli-microsoft365/issues/5964) **SharePoint Premium:** - [spp model list](../cmd/spp/model/model-list.mdx) - retrieves the list of unstructured document processing models [#6103](https://github.com/pnp/cli-microsoft365/issues/6103) +**Viva:** + +- [viva engage community user list](../cmd/viva/engage/engage-community-user-list.mdx) - lists all users within a specified Microsoft 365 Viva Engage community [#6295](https://github.com/pnp/cli-microsoft365/issues/6295) + ### Changes - extended [login](../cmd/login.mdx) command with `--ensure` option [#5217](https://github.com/pnp/cli-microsoft365/issues/5217) @@ -29,6 +34,7 @@ sidebar_position: 3 - enhanced [spo list remove](../cmd/spo/list/list-remove.mdx) command with `--recylce` flag [#6270](https://github.com/pnp/cli-microsoft365/issues/6270) - enhanced [spo file remove](../cmd/spo/file/file-remove.mdx) command with `--bypassSharedLock` flag [#6313](https://github.com/pnp/cli-microsoft365/issues/6313) - updated 'spo userprofile get' command docs. +- adds alias for [spo tenant site list](../cmd/spo/tenant/tenant-site-list.mdx) command [#6143](https://github.com/pnp/cli-microsoft365/issues/6143) ### ⚠️ Breaking changes From 74c7a94b9ab6b8d3f21cdfe7db071e9c7dc1d69c Mon Sep 17 00:00:00 2001 From: Mathijs Verbeeck Date: Fri, 20 Sep 2024 11:16:11 +0200 Subject: [PATCH 13/42] Updates 'teams cache remove' command to support the new client. Closes #5998 --- docs/docs/cmd/teams/cache/cache-remove.mdx | 11 +- docs/docs/v10-upgrade-guidance.mdx | 12 ++ .../teams/commands/cache/cache-remove.spec.ts | 150 ++++++++++++++++-- src/m365/teams/commands/cache/cache-remove.ts | 113 +++++++++---- 4 files changed, 240 insertions(+), 46 deletions(-) diff --git a/docs/docs/cmd/teams/cache/cache-remove.mdx b/docs/docs/cmd/teams/cache/cache-remove.mdx index e9d5f0a39fb..b37fb31e224 100644 --- a/docs/docs/cmd/teams/cache/cache-remove.mdx +++ b/docs/docs/cmd/teams/cache/cache-remove.mdx @@ -13,6 +13,9 @@ m365 teams cache remove [options] ## Options ```md definition-list +`-c, --client` +: Teams client to target. Possible values are: `new` or `classic`. Default value is `new`. + `-f, --force` : Don't prompt for confirmation ``` @@ -43,12 +46,18 @@ The command works only on Windows and macOS. If you run it on a different operat ## Examples -Removes the Microsoft Teams client cache +Removes the Microsoft Teams client cache for the new client. ```sh m365 teams cache remove ``` +Removes the Microsoft Teams client cache for the classic client. + +```sh +m365 teams cache remove --client classic +``` + ## Response The command won't return a response on success. diff --git a/docs/docs/v10-upgrade-guidance.mdx b/docs/docs/v10-upgrade-guidance.mdx index 49bf8f02419..a4e11b59118 100644 --- a/docs/docs/v10-upgrade-guidance.mdx +++ b/docs/docs/v10-upgrade-guidance.mdx @@ -271,6 +271,18 @@ Please update your scripts not to use the `overwrite` option. ## Teams +### Enhanced `teams cache remove` command + +We enhanced the [teams cache remove](./cmd/teams/cache/cache-remove.mdx) command to support the removal of the cache from the new Teams client. We also made it so that by default, the command will remove the cache for the `new` client. + +To still support the old client, we have added the option `--client`, in which you can specify `classic` as value to remove the cache of the old Teams client. + +#### What action do I need to take? + +Add the `--client` option to your scripts to specify which client's cache you want to remove. Otherwise, upon the release of v10, it will attempt to remove the cache from the new client. + +## Teams + ### Removes duplicate property from 'teams tab list' command. For the [teams tab list](./cmd/teams/tab/tab-list.mdx) command we removed the `teamsAppTabId` from the command output as it was a duplicate of the `teamsApp/id` property. diff --git a/src/m365/teams/commands/cache/cache-remove.spec.ts b/src/m365/teams/commands/cache/cache-remove.spec.ts index 84fa5c0ec85..bb4726db0af 100644 --- a/src/m365/teams/commands/cache/cache-remove.spec.ts +++ b/src/m365/teams/commands/cache/cache-remove.spec.ts @@ -12,6 +12,7 @@ import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; import command from './cache-remove.js'; +import os, { homedir } from 'os'; describe(commands.CACHE_REMOVE, () => { const processOutput = `ProcessId @@ -20,6 +21,7 @@ describe(commands.CACHE_REMOVE, () => { 11352`; let log: string[]; let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; let commandInfo: CommandInfo; before(() => { @@ -45,6 +47,7 @@ describe(commands.CACHE_REMOVE, () => { log.push(msg); } }; + loggerLogSpy = sinon.spy(logger, 'log'); sinon.stub(cli, 'promptForConfirmation').resolves(true); }); @@ -84,6 +87,16 @@ describe(commands.CACHE_REMOVE, () => { assert(confirmationStub.calledOnce); }); + it('fails validation if client is not a valid client option', async () => { + sinon.stub(process, 'platform').value('win32'); + const actual = await command.validate({ + options: { + client: 'invalid' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + it('fails validation if called from docker container.', async () => { sinon.stub(process, 'platform').value('win32'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': 'docker' }); @@ -114,7 +127,7 @@ describe(commands.CACHE_REMOVE, () => { assert.strictEqual(actual, true); }); - it('fails to remove teams cache when exec fails randomly when killing teams.exe process', async () => { + it('fails to remove teams cache when exec fails randomly when killing teams.exe process using classic client', async () => { sinon.stub(process, 'platform').value('win32'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); sinon.stub(fs, 'existsSync').returns(true); @@ -125,10 +138,10 @@ describe(commands.CACHE_REMOVE, () => { } throw 'Invalid request'; }); - await assert.rejects(command.action(logger, { options: { force: true } } as any), new CommandError('random error')); + await assert.rejects(command.action(logger, { options: { client: 'classic', force: true } } as any), new CommandError('random error')); }); - it('fails to remove teams cache when exec fails randomly when removing cache folder', async () => { + it('fails to remove teams cache when exec fails randomly when removing cache folder using classic client', async () => { sinon.stub(process, 'platform').value('win32'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '', APPDATA: 'C:\\Users\\Administrator\\AppData\\Roaming' }); sinon.stub(process, 'kill' as any).returns(null); @@ -143,10 +156,41 @@ describe(commands.CACHE_REMOVE, () => { } throw 'Invalid request'; }); - await assert.rejects(command.action(logger, { options: { force: true } } as any), new CommandError('random error')); + await assert.rejects(command.action(logger, { options: { client: 'classic', force: true } } as any), new CommandError('random error')); + }); + + it('shows error message when exec fails when removing the teams cache folder on mac os', async () => { + const deleteError = { + code: 1, + killed: false, + signal: null, + cmd: 'rm -r "/Users/John/Library/Group Containers/UBF8T346G9.com.microsoft.teams"', + stdout: '', + stderr: 'rm: /Users/John/Library/Group Containers/UBF8T346G9.com.microsoft.teams: Operation not permitted\\n' + }; + + sinon.stub(process, 'platform').value('darwin'); + sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); + sinon.stub(process, 'kill' as any).returns(null); + sinon.stub(fs, 'existsSync').returns(true); + + sinon.stub(command, 'exec' as any).callsFake(async (opts) => { + if (opts === `ps ax | grep MacOS/MSTeams -m 1 | grep -v grep | awk '{ print $1 }'`) { + return {}; + } + if (opts === `rm -r "${homedir}/Library/Group Containers/UBF8T346G9.com.microsoft.teams"`) { + return; + } + if (opts === `rm -r "${homedir}/Library/Containers/com.microsoft.teams2"`) { + throw deleteError; + } + throw 'Invalid request'; + }); + await command.action(logger, { options: { force: true } } as any); + assert(loggerLogSpy.calledWith('Deleting the folder failed. Please have a look at the following URL to delete the folders manually: https://answers.microsoft.com/en-us/msteams/forum/all/clearing-cache-on-microsoft-teams/35876f6b-eb1a-4b77-bed1-02ce3277091f')); }); - it('removes Teams cache from macOs platform without prompting.', async () => { + it('removes Teams cache from macOs platform without prompting using classic client', async () => { sinon.stub(process, 'platform').value('darwin'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); sinon.stub(command, 'exec' as any).returns({ stdout: '' }); @@ -156,13 +200,14 @@ describe(commands.CACHE_REMOVE, () => { await command.action(logger, { options: { force: true, - verbose: true + verbose: true, + client: 'classic' } }); assert(true); }); - it('removes teams cache when teams is currently not active', async () => { + it('removes teams cache when teams is currently not active using the classic client', async () => { sinon.stub(process, 'platform').value('win32'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '', APPDATA: 'C:\\Users\\Administrator\\AppData\\Roaming' }); sinon.stub(process, 'kill' as any).returns(null); @@ -180,13 +225,14 @@ describe(commands.CACHE_REMOVE, () => { await command.action(logger, { options: { force: true, - verbose: true + verbose: true, + client: 'classic' } }); assert(true); }); - it('removes Teams cache from win32 platform without prompting.', async () => { + it('removes Teams cache from win32 platform without prompting using the classic client', async () => { sinon.stub(process, 'platform').value('win32'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '', APPDATA: 'C:\\Users\\Administrator\\AppData\\Roaming' }); sinon.stub(process, 'kill' as any).returns(null); @@ -200,6 +246,30 @@ describe(commands.CACHE_REMOVE, () => { throw 'Invalid request'; }); sinon.stub(fs, 'existsSync').returns(true); + await command.action(logger, { + options: { + force: true, + verbose: true, + client: 'classic' + } + }); + assert(true); + }); + + it('removes Teams cache from win32 platform without prompting using the new client', async () => { + sinon.stub(process, 'platform').value('win32'); + sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '', APPDATA: 'C:\\Users\\Administrator\\AppData\\Roaming', LOCALAPPDATA: 'C:\\Users\\Administrator\\AppData\\Local' }); + sinon.stub(process, 'kill' as any).returns(null); + sinon.stub(command, 'exec' as any).callsFake(async (opts) => { + if (opts === 'wmic process where caption="ms-teams.exe" get ProcessId') { + return { stdout: processOutput }; + } + if (opts === 'rmdir /s /q "C:\\Users\\Administrator\\AppData\\Local\\Packages\\MSTeams_8wekyb3d8bbwe\\LocalCache\\Microsoft\\MSTeams"') { + return; + } + throw 'Invalid request'; + }); + sinon.stub(fs, 'existsSync').returns(true); await command.action(logger, { options: { force: true, @@ -209,7 +279,7 @@ describe(commands.CACHE_REMOVE, () => { assert(true); }); - it('removes Teams cache from darwin platform with prompting.', async () => { + it('removes Teams cache from darwin platform with prompting using the classic client', async () => { sinon.stub(process, 'platform').value('darwin'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); sinon.stub(command, 'exec' as any).returns({ stdout: 'pid' }); @@ -218,12 +288,70 @@ describe(commands.CACHE_REMOVE, () => { await command.action(logger, { options: { - debug: true + debug: true, + client: 'classic' } }); assert(true); }); + it('removes Teams cache from darwin platform with prompting', async () => { + sinon.stub(process, 'platform').value('darwin'); + sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); + sinon.stub(command, 'exec' as any).returns({ stdout: '1111' }); + sinon.stub(process, 'kill' as any).returns(null); + sinon.stub(fs, 'existsSync').returns(true); + + await command.action(logger, { + options: { + debug: true, + client: 'new' + } + }); + assert(true); + }); + + it('removes teams cache when teams is currently not running on macOS', async () => { + sinon.stub(process, 'platform').value('darwin'); + sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); + sinon.stub(process, 'kill' as any).returns(null); + sinon.stub(command, 'exec' as any).callsFake(async (opts) => { + if (opts === `ps ax | grep MacOS/MSTeams -m 1 | grep -v grep | awk '{ print $1 }'`) { + return {}; + } + if (opts === `rm -r "${os.homedir()}/Library/Group Containers/UBF8T346G9.com.microsoft.teams"`) { + return; + } + if (opts === `rm -r "${os.homedir()}/Library/Containers/com.microsoft.teams2"`) { + return; + } + throw 'Invalid request'; + }); + sinon.stub(fs, 'existsSync').returns(true); + + await command.action(logger, { + options: { + force: true, + verbose: true, + client: 'new' + } + }); + assert(true); + }); + + + it('aborts cache clearing when no cache folder is found using the classic client', async () => { + sinon.stub(process, 'platform').value('darwin'); + sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); + sinon.stub(fs, 'existsSync').returns(false); + await command.action(logger, { + options: { + verbose: true, + client: 'classic' + } + }); + }); + it('aborts cache clearing when no cache folder is found', async () => { sinon.stub(process, 'platform').value('darwin'); sinon.stub(process, 'env').value({ 'CLIMICROSOFT365_ENV': '' }); diff --git a/src/m365/teams/commands/cache/cache-remove.ts b/src/m365/teams/commands/cache/cache-remove.ts index 8b8db97eb23..d96a002edc1 100644 --- a/src/m365/teams/commands/cache/cache-remove.ts +++ b/src/m365/teams/commands/cache/cache-remove.ts @@ -14,6 +14,7 @@ interface CommandArgs { } interface Options extends GlobalOptions { + client?: string; force?: boolean; } @@ -22,6 +23,8 @@ interface Win32Process { } class TeamsCacheRemoveCommand extends AnonymousCommand { + private static readonly allowedClients: string[] = ['new', 'classic']; + public get name(): string { return commands.CACHE_REMOVE; } @@ -41,6 +44,7 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { #initTelemetry(): void { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { + client: args.options.client, force: !!args.options.force }); }); @@ -48,6 +52,10 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { #initOptions(): void { this.options.unshift( + { + option: '-c, --client', + autocomplete: TeamsCacheRemoveCommand.allowedClients + }, { option: '-f, --force' } @@ -56,7 +64,11 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { #initValidators(): void { this.validators.push( - async () => { + async (args: CommandArgs) => { + if (args.options.client && !TeamsCacheRemoveCommand.allowedClients.includes(args.options.client.toLowerCase())) { + return `'${args.options.client}' is not a valid value for option 'client'. Allowed values are ${TeamsCacheRemoveCommand.allowedClients.join(', ')}`; + } + if (process.env.CLIMICROSOFT365_ENV === 'docker') { return 'Because you\'re running CLI for Microsoft 365 in a Docker container, we can\'t clear the cache on your host. Instead run this command on your host using "npx ..."'; } @@ -73,7 +85,7 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { try { if (args.options.force) { - await this.clearTeamsCache(logger); + await this.clearTeamsCache(args.options.client?.toLowerCase() || 'new', logger); } else { await logger.logToStderr('This command will execute the following steps.'); @@ -83,7 +95,7 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { const result = await cli.promptForConfirmation({ message: `Are you sure you want to clear your Microsoft Teams cache?` }); if (result) { - await this.clearTeamsCache(logger); + await this.clearTeamsCache(args.options.client?.toLowerCase() || 'new', logger); } } } @@ -92,13 +104,20 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { } } - private async clearTeamsCache(logger: Logger): Promise { - const filePath = await this.getTeamsCacheFolderPath(logger); - const folderExists = await this.checkIfCacheFolderExists(filePath, logger); + private async clearTeamsCache(client: string, logger: Logger): Promise { + const filePaths = await this.getTeamsCacheFolderPaths(client, logger); + + let folderExists = true; + for (const filePath of filePaths) { + const exists = await this.checkIfCacheFolderExists(filePath, logger); + if (!exists) { + folderExists = false; + } + } if (folderExists) { - await this.killRunningProcess(logger); - await this.removeCacheFiles(filePath, logger); + await this.killRunningProcess(client, logger); + await this.removeCacheFiles(filePaths, logger); await logger.logToStderr('Teams cache cleared!'); } else { @@ -107,24 +126,34 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { } - private async getTeamsCacheFolderPath(logger: Logger): Promise { + private async getTeamsCacheFolderPaths(client: string, logger: Logger): Promise { const platform = process.platform; if (this.verbose) { await logger.logToStderr(`Getting path of Teams cache folder for platform ${platform}...`); } - - let filePath = ''; + const filePaths: string[] = []; switch (platform) { case 'win32': - filePath = `${process.env.APPDATA}\\Microsoft\\Teams`; + if (client === 'classic') { + filePaths.push(`${process.env.APPDATA}\\Microsoft\\Teams`); + } + else { + filePaths.push(`${process.env.LOCALAPPDATA}\\Packages\\MSTeams_8wekyb3d8bbwe\\LocalCache\\Microsoft\\MSTeams`); + } break; case 'darwin': - filePath = `${homedir}/Library/Application Support/Microsoft/Teams`; + if (client === 'classic') { + filePaths.push(`${homedir}/Library/Application Support/Microsoft/Teams`); + } + else { + filePaths.push(`${homedir}/Library/Group Containers/UBF8T346G9.com.microsoft.teams`); + filePaths.push(`${homedir}/Library/Containers/com.microsoft.teams2`); + } break; } - return filePath; + return filePaths; } private async checkIfCacheFolderExists(filePath: string, logger: Logger): Promise { @@ -135,7 +164,7 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { return fs.existsSync(filePath); } - private async killRunningProcess(logger: Logger): Promise { + private async killRunningProcess(client: string, logger: Logger): Promise { if (this.verbose) { await logger.logToStderr('Stopping Teams client...'); } @@ -145,10 +174,21 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { switch (platform) { case 'win32': - cmd = 'wmic process where caption="Teams.exe" get ProcessId'; + if (client === 'classic') { + cmd = 'wmic process where caption="Teams.exe" get ProcessId'; + } + else { + cmd = 'wmic process where caption="ms-teams.exe" get ProcessId'; + } break; case 'darwin': - cmd = `ps ax | grep MacOS/Teams -m 1 | grep -v grep | awk '{ print $1 }'`; + if (client === 'classic') { + cmd = `ps ax | grep MacOS/Teams -m 1 | grep -v grep | awk '{ print $1 }'`; + } + else { + cmd = `ps ax | grep MacOS/MSTeams -m 1 | grep -v grep | awk '{ print $1 }'`; + } + break; } @@ -158,42 +198,47 @@ class TeamsCacheRemoveCommand extends AnonymousCommand { const cmdOutput = await this.exec(cmd); - if (platform === 'darwin') { + if (platform === 'darwin' && cmdOutput.stdout) { process.kill(parseInt(cmdOutput.stdout)); } else if (platform === 'win32') { const processJson: Win32Process[] = formatting.parseCsvToJson(cmdOutput.stdout); - processJson.filter(proc => proc.ProcessId).map((proc: Win32Process) => { + for (const proc of processJson) { process.kill(proc.ProcessId); - }); + } } if (this.verbose) { await logger.logToStderr('Teams client closed'); } } - private async removeCacheFiles(filePath: string, logger: Logger): Promise { + private async removeCacheFiles(filePaths: string[], logger: Logger): Promise { if (this.verbose) { await logger.logToStderr('Removing Teams cache files...'); } const platform = process.platform; - let cmd = ''; + const baseCmd = platform === 'win32' ? 'rmdir /s /q ' : 'rm -r '; - switch (platform) { - case 'win32': - cmd = `rmdir /s /q "${filePath}"`; - break; - case 'darwin': - cmd = `rm -r "${filePath}"`; - break; - } + for (const filePath of filePaths) { + const cmd = `${baseCmd}"${filePath}"`; - if (this.debug) { - await logger.logToStderr(cmd); - } + if (this.debug) { + await logger.logToStderr(cmd); + } - await this.exec(cmd); + try { + await this.exec(cmd); + } + catch (err: any) { + if (err?.stderr?.includes('Operation not permitted')) { + await logger.log('Deleting the folder failed. Please have a look at the following URL to delete the folders manually: https://answers.microsoft.com/en-us/msteams/forum/all/clearing-cache-on-microsoft-teams/35876f6b-eb1a-4b77-bed1-02ce3277091f'); + } + else { + throw err; + } + } + } } private exec = util.promisify(child_process.exec); From 7e0914e04afb36ae82de4cb3707409bbb6d171c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Wed, 30 Oct 2024 23:11:55 +0100 Subject: [PATCH 14/42] Updates release notes --- docs/docs/about/release-notes.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 1896e313663..6e539414122 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -71,6 +71,7 @@ sidebar_position: 3 - updated endpoint for [spo folder move](../cmd/spo/folder/folder-move.mdx) command [#6154](https://github.com/pnp/cli-microsoft365/issues/6154) - moved [spo folder copy](../cmd/spo/folder/folder-copy.mdx) to new endpoint [#6310](https://github.com/pnp/cli-microsoft365/issues/6310) - removed deprecated option from 'entra m365group user remove' [#6357](https://github.com/pnp/cli-microsoft365/issues/6357) +- updated `teams cache remove` command to support the new client [#5998](https://github.com/pnp/cli-microsoft365/issues/5998) ## [v9.1.0](https://github.com/pnp/cli-microsoft365/releases/tag/v9.1.0) From 9f71bd6892d43daa17757737040e87771a1ea9b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Thu, 31 Oct 2024 12:10:20 +0100 Subject: [PATCH 15/42] Updates version to v10.1.0 --- docs/docs/about/release-notes.mdx | 2 +- npm-shrinkwrap.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 6e539414122..dcb25623714 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -4,7 +4,7 @@ sidebar_position: 3 # Release notes -## v10.0.0 (beta) +## [v10.0.0](https://github.com/pnp/cli-microsoft365/releases/tag/v10.0.0) ### New commands diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 6768b6439ee..63d467c61c0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,12 +1,12 @@ { "name": "@pnp/cli-microsoft365", - "version": "10.0.0", + "version": "10.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pnp/cli-microsoft365", - "version": "10.0.0", + "version": "10.1.0", "license": "MIT", "dependencies": { "@azure/msal-common": "14.15", diff --git a/package.json b/package.json index 7133ed14288..b5cdf8ccd70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pnp/cli-microsoft365", - "version": "10.0.0", + "version": "10.1.0", "description": "Manage Microsoft 365 and SharePoint Framework projects on any platform", "license": "MIT", "main": "./dist/api.js", From 15d55a110b446e9941698156543a8be0958325d6 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Sun, 6 Oct 2024 17:57:10 +0000 Subject: [PATCH 16/42] Adds command 'm365 spe container list'. Closes #6082 --- .../docs/cmd/spe/container/container-list.mdx | 97 ++++++++++ docs/src/config/sidebars.ts | 9 + src/m365/spe/ContainerProperties.ts | 6 + src/m365/spe/ContainerTypeProperties.ts | 14 -- src/m365/spe/commands.ts | 1 + .../commands/container/container-list.spec.ts | 182 ++++++++++++++++++ .../spe/commands/container/container-list.ts | 118 ++++++++++++ .../containertype/containertype-list.spec.ts | 142 ++++---------- .../containertype/containertype-list.ts | 30 +-- src/utils/spo.spec.ts | 84 ++++++++ src/utils/spo.ts | 38 ++++ 11 files changed, 576 insertions(+), 145 deletions(-) create mode 100644 docs/docs/cmd/spe/container/container-list.mdx create mode 100644 src/m365/spe/ContainerProperties.ts delete mode 100644 src/m365/spe/ContainerTypeProperties.ts create mode 100644 src/m365/spe/commands/container/container-list.spec.ts create mode 100644 src/m365/spe/commands/container/container-list.ts diff --git a/docs/docs/cmd/spe/container/container-list.mdx b/docs/docs/cmd/spe/container/container-list.mdx new file mode 100644 index 00000000000..247e2534228 --- /dev/null +++ b/docs/docs/cmd/spe/container/container-list.mdx @@ -0,0 +1,97 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spe container list + +Lists containers of a specific Container Type + +## Usage + +```sh +m365 spe container list [options] +``` + +## Options + +```md definition-list +`--containerTypeId [containerTypeId]` +: The Container Type Id of the container instance. Use either `containerTypeId` or `containerTypeName` but not both. + +`--containerTypeName [containerTypeName]` +: The Container Type name of the container instance. Use either `containerTypeId` or `containerTypeName` but not both. +``` + + + +## Examples + +List containers of a specific type by id. + +```sh +m365 spe container list --containerTypeId "91710488-5756-407f-9046-fbe5f0b4de73" +``` + +List containers of a specific type by name. + +```sh +m365 spe container list --containerTypeName "trial container" +``` + +## Response + + + + + ```json + [ + { + "id": "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z", + "displayName": "My File Storage Container", + "containerTypeId": "e2756c4d-fa33-4452-9c36-2325686e1082", + "createdDateTime": "2021-11-24T15:41:52.347Z" + } + ] + ``` + + + + + ```text + id displayName containerTypeId createdDateTime + ------------------------------------------------------------------ ------------------------- ------------------------------------ ------------------------ + b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z My File Storage Container e2756c4d-fa33-4452-9c36-2325686e1082 2021-11-24T15:41:52.347Z + ``` + + + + + ```csv + id,displayName,containerTypeId,createdDateTime + b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z,My File Storage Container,e2756c4d-fa33-4452-9c36-2325686e1082,2021-11-24T15:41:52.347Z + ``` + + + + + ```md + # spe container list + + Date: 10/06/2024 + + ## My File Storage Container + + Property | Value + ---------|------- + id | b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z + displayName | My File Storage Container + containerTypeId | e2756c4d-fa33-4452-9c36-2325686e1082 + createdDateTime | 2021-11-24T15:41:52.347Z + ``` + + + + +## More information + +In SharePoint Embedded, all files and documents are stored in Containers. The calling app should be the owning app of the container type. diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 7dc3dcc2239..10256eb8164 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1969,6 +1969,15 @@ const sidebars: SidebarsConfig = { } ] }, + { + container: [ + { + type: 'doc', + label: 'container list', + id: 'cmd/spe/container/container-list' + } + ] + }, { containertype: [ { diff --git a/src/m365/spe/ContainerProperties.ts b/src/m365/spe/ContainerProperties.ts new file mode 100644 index 00000000000..b7738cad160 --- /dev/null +++ b/src/m365/spe/ContainerProperties.ts @@ -0,0 +1,6 @@ +export interface ContainerProperties { + id: string; + displayName: string; + containerTypeId: string; + createdDateTime: string; +} \ No newline at end of file diff --git a/src/m365/spe/ContainerTypeProperties.ts b/src/m365/spe/ContainerTypeProperties.ts deleted file mode 100644 index 5b077ef73b7..00000000000 --- a/src/m365/spe/ContainerTypeProperties.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ContainerTypeProperties { - _ObjectType_?: string; - AzureSubscriptionId: string; - ContainerTypeId: string; - CreationDate: string; - DisplayName: string; - ExpiryDate: string; - IsBillingProfileRequired: boolean; - OwningAppId: string; - OwningTenantId: string; - Region?: string; - ResourceGroup?: string; - SPContainerTypeBillingClassification: string; -} \ No newline at end of file diff --git a/src/m365/spe/commands.ts b/src/m365/spe/commands.ts index f013be34c0b..c0d74cf01f4 100644 --- a/src/m365/spe/commands.ts +++ b/src/m365/spe/commands.ts @@ -2,6 +2,7 @@ const prefix: string = 'spe'; export default { CONTAINER_GET: `${prefix} container get`, + CONTAINER_LIST: `${prefix} container list`, CONTAINERTYPE_ADD: `${prefix} containertype add`, CONTAINERTYPE_LIST: `${prefix} containertype list` }; \ No newline at end of file diff --git a/src/m365/spe/commands/container/container-list.spec.ts b/src/m365/spe/commands/container/container-list.spec.ts new file mode 100644 index 00000000000..a5900faf2fa --- /dev/null +++ b/src/m365/spe/commands/container/container-list.spec.ts @@ -0,0 +1,182 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { Logger } from '../../../../cli/Logger.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './container-list.js'; +import { spo } from '../../../../utils/spo.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { cli } from '../../../../cli/cli.js'; + +describe(commands.CONTAINER_LIST, () => { + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + + const adminUrl = 'https://contoso-admin.sharepoint.com'; + const containersList = [{ + "id": "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z", + "displayName": "My File Storage Container", + "containerTypeId": "e2756c4d-fa33-4452-9c36-2325686e1082", + "createdDateTime": "2021-11-24T15:41:52.347Z" + }, + { + "id": "b!NdyMBAJ1FEWHB2hEx0DND2dYRB9gz4JOl4rzl7-DuyPG3Fidzm5TTKkyZW2beare", + "displayName": "Trial Container", + "containerTypeId": "e2756c4d-fa33-4452-9c36-2325686e1082", + "createdDateTime": "2021-11-24T15:41:52.347Z" + }]; + + const containerTypedata = [{ + "AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)", + "ContainerTypeId": "/Guid(e2756c4d-fa33-4452-9c36-2325686e1082)", + "CreationDate": "3/11/2024 2:38:56 PM", + "DisplayName": "standard container", + "ExpiryDate": "3/11/2028 2:38:56 PM", + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)", + "OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)", + "Region": "West Europe", + "ResourceGroup": "Standard group", + "SPContainerTypeBillingClassification": "Standard" + }, + { + "AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)", + "ContainerTypeId": "/Guid(e2756c4d-fa33-4452-9c36-2325686e1082)", + "CreationDate": "3/11/2024 2:38:56 PM", + "DisplayName": "trial container", + "ExpiryDate": "3/11/2028 2:38:56 PM", + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)", + "OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)", + "Region": "West Europe", + "ResourceGroup": "Standard group", + "SPContainerTypeBillingClassification": "Standard" + }]; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + sinon.stub(spo, 'getSpoAdminUrl').resolves(adminUrl); + sinon.stub(spo, 'ensureFormDigest').resolves({ FormDigestValue: 'abc', FormDigestTimeoutSeconds: 1800, FormDigestExpiresAt: new Date(), WebFullUrl: 'https://contoso.sharepoint.com' }); + auth.connection.active = true; + auth.connection.spoUrl = 'https://contoso.sharepoint.com'; + commandInfo = cli.getCommandInfo(command); + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get, + request.post, + spo.getSpoAdminUrl, + spo.getAllContainerTypes + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + auth.connection.spoUrl = undefined; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.CONTAINER_LIST); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('defines correct properties for the default output', () => { + assert.deepStrictEqual(command.defaultProperties(), ['id', 'displayName', 'containerTypeId', 'createdDateTime']); + }); + + it('fails validation if the containerTypeId is not a valid guid', async () => { + const actual = await command.validate({ options: { containerTypeId: 'abc' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('passes validation if valid containerTypeId is specified', async () => { + const actual = await command.validate({ options: { containerTypeId: "e2756c4d-fa33-4452-9c36-2325686e1082" } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('retrieves list of container type by id', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq e2756c4d-fa33-4452-9c36-2325686e1082') { + return { "value": containersList }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { containerTypeId: "e2756c4d-fa33-4452-9c36-2325686e1082", debug: true } }); + assert(loggerLogSpy.calledWith(containersList)); + }); + + it('retrieves list of container type by name', async () => { + sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypedata); + + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq e2756c4d-fa33-4452-9c36-2325686e1082') { + return { "value": containersList }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { containerTypeName: "standard container", debug: true } }); + assert(loggerLogSpy.calledWith(containersList)); + }); + + it('throws an error when service principal is not found', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq e2756c4d-fa33-4452-9c36-2325686e1086') { + return []; + } + + throw 'Invalid request'; + }); + + sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypedata); + + await assert.rejects(command.action(logger, { options: { containerTypeName: "nonexisting container", debug: true } }), + new CommandError(`Container type with name nonexisting container not found`)); + }); + + it('correctly handles error when retrieving containers', async () => { + const error = 'An error has occurred'; + sinon.stub(spo, 'getAllContainerTypes').rejects(new Error(error)); + + await assert.rejects(command.action(logger, { + options: { + debug: true + } + }), new CommandError('An error has occurred')); + }); +}); \ No newline at end of file diff --git a/src/m365/spe/commands/container/container-list.ts b/src/m365/spe/commands/container/container-list.ts new file mode 100644 index 00000000000..3149436ab36 --- /dev/null +++ b/src/m365/spe/commands/container/container-list.ts @@ -0,0 +1,118 @@ +import { Logger } from '../../../../cli/Logger.js'; +import GlobalOptions from '../../../../GlobalOptions.js'; +import { formatting } from '../../../../utils/formatting.js'; +import { odata } from '../../../../utils/odata.js'; +import { validation } from '../../../../utils/validation.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; +import { ContainerProperties } from '../../ContainerProperties.js'; +import { ContainerTypeProperties, spo } from '../../../../utils/spo.js'; + +interface CommandArgs { + options: Options; +} + +export interface Options extends GlobalOptions { + containerTypeId?: string; + containerTypeName?: string; +} + +class SpeContainerListCommand extends GraphCommand { + public get name(): string { + return commands.CONTAINER_LIST; + } + + public get description(): string { + return 'Lists all Container Types'; + } + + public defaultProperties(): string[] | undefined { + return ['id', 'displayName', 'containerTypeId', 'createdDateTime']; + } + + constructor() { + super(); + + this.#initTelemetry(); + this.#initOptions(); + this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); + } + + #initTelemetry(): void { + this.telemetry.push((args: CommandArgs) => { + Object.assign(this.telemetryProperties, { + containerTypeId: typeof args.options.containerTypeId !== 'undefined', + containerTypeName: typeof args.options.containerTypeName !== 'undefined' + }); + }); + } + + #initOptions(): void { + this.options.unshift( + { + option: '--containerTypeId [containerTypeId]' + }, + { + option: '--containerTypeName [containerTypeName]' + } + ); + } + + #initValidators(): void { + this.validators.push( + async (args: CommandArgs) => { + if (args.options.containerTypeId && !validation.isValidGuid(args.options.containerTypeId as string)) { + return `${args.options.containerTypeId} is not a valid GUID`; + } + + return true; + } + ); + } + + #initOptionSets(): void { + this.optionSets.push({ options: ['containerTypeId', 'containerTypeName'] }); + } + + #initTypes(): void { + this.types.string.push('containerTypeId', 'containerTypeName'); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + try { + if (this.verbose) { + await logger.logToStderr(`Retrieving list of Containers...`); + } + + const containerTypeId = await this.getContainerTypeId(logger, args.options); + const allContainers = await odata.getAllItems(`${this.resource}/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq ${formatting.encodeQueryParameter(containerTypeId)}`); + await logger.log(allContainers); + } + catch (err: any) { + this.handleRejectedPromise(err); + } + } + + private async getContainerTypeId(logger: Logger, options: Options): Promise { + if (options.containerTypeId) { + return options.containerTypeId; + } + + const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug); + const containerTypes: ContainerTypeProperties[] = await spo.getAllContainerTypes(spoAdminUrl, logger, this.debug); + + // Get id of the container type by name + const containerType: ContainerTypeProperties | undefined = containerTypes.find(c => c.DisplayName === options.containerTypeName); + if (!containerType) { + throw new Error(`Container type with name ${options.containerTypeName} not found`); + } + + // The value is returned as "/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/". We need to extract the GUID from it. + const containerTypeValue = containerType.ContainerTypeId.toString(); + return containerTypeValue.substring(containerTypeValue.indexOf('(') + 1, containerTypeValue.lastIndexOf(')')); + } +} + +export default new SpeContainerListCommand(); \ No newline at end of file diff --git a/src/m365/spe/commands/containertype/containertype-list.spec.ts b/src/m365/spe/commands/containertype/containertype-list.spec.ts index 1d161c62fe7..cb21ddc29ed 100644 --- a/src/m365/spe/commands/containertype/containertype-list.spec.ts +++ b/src/m365/spe/commands/containertype/containertype-list.spec.ts @@ -11,42 +11,38 @@ import commands from '../../commands.js'; import command from './containertype-list.js'; import { spo } from '../../../../utils/spo.js'; import { CommandError } from '../../../../Command.js'; -import config from '../../../../config.js'; + +const containerTypedata = [{ + "AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)", + "ContainerTypeId": "/Guid(c33cfee5-c9b6-0a2a-02ee-060693a57f37)", + "CreationDate": "3/11/2024 2:38:56 PM", + "DisplayName": "standard container", + "ExpiryDate": "3/11/2028 2:38:56 PM", + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)", + "OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)", + "Region": "West Europe", + "ResourceGroup": "Standard group", + "SPContainerTypeBillingClassification": "Standard" +}, +{ + "AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)", + "ContainerTypeId": "/Guid(c33cfee5-c9b6-0a2a-02ee-060693a57f37)", + "CreationDate": "3/11/2024 2:38:56 PM", + "DisplayName": "trial container", + "ExpiryDate": "3/11/2028 2:38:56 PM", + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)", + "OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)", + "Region": "West Europe", + "ResourceGroup": "Standard group", + "SPContainerTypeBillingClassification": "Standard" +}]; describe(commands.CONTAINERTYPE_LIST, () => { let log: string[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; - const containerTypedata = [{ - "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", - "ApplicationRedirectUrl": null, - "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", - "ContainerTypeId": "/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/", - "CreationDate": null, - "DisplayName": "test1", - "ExpiryDate": null, - "IsBillingProfileRequired": true, - "OwningAppId": "/Guid(df4085cc-9a38-4255-badc-5c5225610475)/", - "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", - "Region": null, - "ResourceGroup": null, - "SPContainerTypeBillingClassification": 0 - }, - { - "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", - "ApplicationRedirectUrl": null, - "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", - "ContainerTypeId": "/Guid(880ab3bd-5b68-01d4-3744-01a7656cf2ba)/", - "CreationDate": null, - "DisplayName": "test1", - "ExpiryDate": null, - "IsBillingProfileRequired": true, - "OwningAppId": "/Guid(50785fde-3082-47ac-a36d-06282ac5c7da)/", - "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", - "Region": null, - "ResourceGroup": null, - "SPContainerTypeBillingClassification": 0 - }]; before(() => { sinon.stub(auth, 'restoreAuth').resolves(); @@ -76,7 +72,8 @@ describe(commands.CONTAINERTYPE_LIST, () => { afterEach(() => { sinonUtil.restore([ - request.post + request.post, + spo.getAllContainerTypes ]); }); @@ -98,81 +95,20 @@ describe(commands.CONTAINERTYPE_LIST, () => { assert.deepStrictEqual(command.defaultProperties(), ['ContainerTypeId', 'DisplayName', 'OwningAppId']); }); - it('correctly handles random API error', async () => { - sinon.stub(request, 'post').rejects(new Error('An error has occurred')); - - await assert.rejects(command.action(logger, { options: { debug: true } } as any), new CommandError("An error has occurred")); - }); - - it('retrieves list of Container Type', async () => { - sinon.stub(request, 'post').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { - if (opts.headers && - opts.headers['X-RequestDigest'] && - opts.headers['X-RequestDigest'] === 'abc' && - opts.data === `1`) { - return JSON.stringify([ - { - "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" - }, 46, { - "IsNull": false - }, 47, containerTypedata - ]); - } - } - - throw 'Invalid request'; - }); - - await command.action(logger, { options: {} }); - assert(loggerLogSpy.calledWith(containerTypedata)); - }); - - it('retrieves list of Container Type (debug)', async () => { - sinon.stub(request, 'post').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { - if (opts.headers && - opts.headers['X-RequestDigest'] && - opts.headers['X-RequestDigest'] === 'abc' && - opts.data === `1`) { - return JSON.stringify([ - { - "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" - }, 46, { - "IsNull": false - }, 47, containerTypedata - ]); - } - } - - throw 'Invalid request'; - }); - + it('retrieves list of container type', async () => { + sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypedata); await command.action(logger, { options: { debug: true } }); assert(loggerLogSpy.calledWith(containerTypedata)); }); - it('correctly handles error when retrieving Container Types', async () => { - sinon.stub(request, 'post').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { - if (opts.headers && - opts.headers['X-RequestDigest'] && - opts.headers['X-RequestDigest'] === 'abc') { + it('correctly handles error when retrieving container types', async () => { + const error = 'An error has occurred'; + sinon.stub(spo, 'getAllContainerTypes').rejects(new Error(error)); - return JSON.stringify([ - { - "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.7324.1200", "ErrorInfo": { - "ErrorMessage": "An error has occurred.", "ErrorValue": null, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d", "ErrorCode": -1, "ErrorTypeName": "SPException" - }, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d" - } - ]); - } + await assert.rejects(command.action(logger, { + options: { + debug: true } - - throw 'Invalid request'; - }); - - await assert.rejects(command.action(logger, { options: { debug: true } } as any), - new CommandError("An error has occurred.")); + }), new CommandError('An error has occurred')); }); -}); +}); \ No newline at end of file diff --git a/src/m365/spe/commands/containertype/containertype-list.ts b/src/m365/spe/commands/containertype/containertype-list.ts index b55527b9a9e..97e0f573a20 100644 --- a/src/m365/spe/commands/containertype/containertype-list.ts +++ b/src/m365/spe/commands/containertype/containertype-list.ts @@ -1,10 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; -import config from '../../../../config.js'; -import request, { CliRequestOptions } from '../../../../request.js'; -import { ClientSvcResponse, ClientSvcResponseContents, FormDigestInfo, spo } from '../../../../utils/spo.js'; import SpoCommand from '../../../base/SpoCommand.js'; import commands from '../../commands.js'; -import { ContainerTypeProperties } from '../../ContainerTypeProperties.js'; +import { ContainerTypeProperties, spo } from '../../../../utils/spo.js'; class SpeContainertypeListCommand extends SpoCommand { @@ -28,36 +25,13 @@ class SpeContainertypeListCommand extends SpoCommand { await logger.logToStderr(`Retrieving list of Container types...`); } - const allContainerTypes = await this.getAllContainerTypes(spoAdminUrl, logger); + const allContainerTypes: ContainerTypeProperties[] = await spo.getAllContainerTypes(spoAdminUrl, logger, this.debug); await logger.log(allContainerTypes); } catch (err: any) { this.handleRejectedPromise(err); } } - - private async getAllContainerTypes(spoAdminUrl: string, logger: Logger): Promise { - const formDigestInfo: FormDigestInfo = await spo.ensureFormDigest(spoAdminUrl, logger, undefined, this.debug); - - const requestOptions: CliRequestOptions = { - url: `${spoAdminUrl}/_vti_bin/client.svc/ProcessQuery`, - headers: { - 'X-RequestDigest': formDigestInfo.FormDigestValue - }, - data: `1` - }; - - const res: string = await request.post(requestOptions); - const json: ClientSvcResponse = JSON.parse(res); - const response: ClientSvcResponseContents = json[0]; - - if (response.ErrorInfo) { - throw response.ErrorInfo.ErrorMessage; - } - - const containerTypes: ContainerTypeProperties[] = json[json.length - 1]; - return containerTypes; - } } export default new SpeContainertypeListCommand(); \ No newline at end of file diff --git a/src/utils/spo.spec.ts b/src/utils/spo.spec.ts index f9b7b914885..9be48b98cf2 100644 --- a/src/utils/spo.spec.ts +++ b/src/utils/spo.spec.ts @@ -88,6 +88,37 @@ const copyJobInfo = { ] }; +const containerTypedata = [{ + "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", + "ApplicationRedirectUrl": null, + "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "ContainerTypeId": "/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/", + "CreationDate": null, + "DisplayName": "test1", + "ExpiryDate": null, + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(df4085cc-9a38-4255-badc-5c5225610475)/", + "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "Region": null, + "ResourceGroup": null, + "SPContainerTypeBillingClassification": 0 +}, +{ + "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", + "ApplicationRedirectUrl": null, + "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "ContainerTypeId": "/Guid(880ab3bd-5b68-01d4-3744-01a7656cf2ba)/", + "CreationDate": null, + "DisplayName": "test1", + "ExpiryDate": null, + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(50785fde-3082-47ac-a36d-06282ac5c7da)/", + "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "Region": null, + "ResourceGroup": null, + "SPContainerTypeBillingClassification": 0 +}]; + describe('utils/spo', () => { let logger: Logger; let log: string[]; @@ -3157,4 +3188,57 @@ describe('utils/spo', () => { assert.deepStrictEqual(postStub.firstCall.args[0].data, { url: 'https://contoso.sharepoint.com/sites/sales', includeDetail: true }); }); + + it('retrieves list of Container Type', async () => { + sinon.stub(spo, 'getSpoAdminUrl').resolves('https://contoso-admin.sharepoint.com'); + sinon.stub(spo, 'ensureFormDigest').resolves({ FormDigestValue: 'abc', FormDigestTimeoutSeconds: 1800, FormDigestExpiresAt: new Date(), WebFullUrl: 'https://contoso.sharepoint.com' }); + + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" + }, 46, { + "IsNull": false + }, 47, containerTypedata + ]); + } + } + + throw 'Invalid request'; + }); + + const containerTypeList = await spo.getAllContainerTypes('https://contoso-admin.sharepoint.com', logger, true); + assert.deepEqual(containerTypeList, containerTypedata); + }); + + it('correctly throws error when retrieving container types', async () => { + sinon.stub(spo, 'getSpoAdminUrl').resolves('https://contoso-admin.sharepoint.com'); + sinon.stub(spo, 'ensureFormDigest').resolves({ FormDigestValue: 'abc', FormDigestTimeoutSeconds: 1800, FormDigestExpiresAt: new Date(), WebFullUrl: 'https://contoso.sharepoint.com' }); + + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc') { + + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.7324.1200", "ErrorInfo": { + "ErrorMessage": "An error has occurred", "ErrorValue": null, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d", "ErrorCode": -1, "ErrorTypeName": "SPException" + }, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d" + } + ]); + } + } + + throw 'Invalid request'; + }); + + await assert.rejects(spo.getAllContainerTypes('https://contoso-admin.sharepoint.com', logger, true), 'An error occured'); + }); }); \ No newline at end of file diff --git a/src/utils/spo.ts b/src/utils/spo.ts index 9256d3cce4e..d88e4f486ca 100644 --- a/src/utils/spo.ts +++ b/src/utils/spo.ts @@ -259,6 +259,21 @@ interface TenantSiteProperties { WebsCount: number; } +export interface ContainerTypeProperties { + _ObjectType_?: string; + AzureSubscriptionId: string; + ContainerTypeId: string; + CreationDate: string; + DisplayName: string; + ExpiryDate: string; + IsBillingProfileRequired: boolean; + OwningAppId: string; + OwningTenantId: string; + Region?: string; + ResourceGroup?: string; + SPContainerTypeBillingClassification: string; +} + export const spo = { async getRequestDigest(siteUrl: string): Promise { const requestOptions: CliRequestOptions = { @@ -293,6 +308,29 @@ export const spo = { return context; }, + async getAllContainerTypes(spoAdminUrl: string, logger: Logger, verbose: boolean): Promise { + const formDigestInfo: FormDigestInfo = await spo.ensureFormDigest(spoAdminUrl, logger, undefined, verbose); + + const requestOptions: CliRequestOptions = { + url: `${spoAdminUrl}/_vti_bin/client.svc/ProcessQuery`, + headers: { + 'X-RequestDigest': formDigestInfo.FormDigestValue + }, + data: `1` + }; + + const res: string = await request.post(requestOptions); + const json: ClientSvcResponse = JSON.parse(res); + const response: ClientSvcResponseContents = json[0]; + + if (response.ErrorInfo) { + throw new Error(response.ErrorInfo.ErrorMessage); + } + + const containerTypes: ContainerTypeProperties[] = json[json.length - 1]; + return containerTypes; + }, + async waitUntilFinished({ operationId, siteUrl, logger, currentContext, debug, verbose }: { operationId: string, siteUrl: string, logger: Logger, currentContext: FormDigestInfo, debug: boolean, verbose: boolean }): Promise { const resFormDigest = await spo.ensureFormDigest(siteUrl, logger, currentContext, debug); currentContext = resFormDigest; From 52f2eaa73e6f48e83bba6054406cbab60ba6710a Mon Sep 17 00:00:00 2001 From: reshmee011 Date: Thu, 8 Aug 2024 11:25:04 +0000 Subject: [PATCH 17/42] Adds command 'spe containertype get'. Closes #5991 --- .../spe/containertype/containertype-get.mdx | 114 ++++++++ docs/src/config/sidebars.ts | 5 + src/m365/spe/commands.ts | 1 + .../containertype/containertype-get.spec.ts | 243 ++++++++++++++++++ .../containertype/containertype-get.ts | 139 ++++++++++ 5 files changed, 502 insertions(+) create mode 100644 docs/docs/cmd/spe/containertype/containertype-get.mdx create mode 100644 src/m365/spe/commands/containertype/containertype-get.spec.ts create mode 100644 src/m365/spe/commands/containertype/containertype-get.ts diff --git a/docs/docs/cmd/spe/containertype/containertype-get.mdx b/docs/docs/cmd/spe/containertype/containertype-get.mdx new file mode 100644 index 00000000000..bc9febc16f4 --- /dev/null +++ b/docs/docs/cmd/spe/containertype/containertype-get.mdx @@ -0,0 +1,114 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spe containertype get + +Get a specific Container Type + +## Usage + +```sh +m365 containertype get [options] +``` + +## Options + +```md definition-list +`-i, --id [id]` +: The Id of the Container Type. Specify either id or name but not both. + +`-n, --name [name]` +: The Container Type name. Specify either id or name but not both. +``` + + + +## Examples + +Gets Container Type by id + +```sh +m365 spe containertype get --id '4ec4aefd-4fa3-0e4a-20c3-6e68389e7138' +``` + +Gets Container Type by name + +```sh +m365 spe containertype get --name 'test container' +``` + +## Response + + + + + ```json + { + "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", + "ApplicationRedirectUrl": null, + "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "ContainerTypeId": "/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/", + "CreationDate": null, + "DisplayName": "test1", + "ExpiryDate": null, + "IsBillingProfileRequired": true, + "OwningAppId": "/Guid(df4085cc-9a38-4255-badc-5c5225610475)/", + "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "Region": null, + "ResourceGroup": null, + "SPContainerTypeBillingClassification": 0 + } + ``` + + + + + ```text +ContainerTypeId DisplayName OwningAppId +-------------------------------------------- --------------------------------------------- -------------------------------------------- +/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/ test1 /Guid(df4085cc-9a38-4255-badc-5c5225610475)/ + ``` + + + + + ```csv + _ObjectType_,ApplicationRedirectUrl,AzureSubscriptionId,ContainerTypeId,CreationDate,DisplayName,ExpiryDate,IsBillingProfileRequired,OwningAppId,OwningTenantId,Region,ResourceGroup,SPContainerTypeBillingClassification + Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties,,/Guid(00000000-0000-0000-0000-000000000000)/,/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/,,test1,,1,/Guid(df4085cc-9a38-4255-badc-5c5225610475)/,/Guid(00000000-0000-0000-0000-000000000000)/,,,0 + ``` + + + + + ```md + # spe containertype list + + Date: 5/11/2024 + + ## test1 + + Property | Value + ---------|------- + \_ObjectType\_ | Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties + AzureSubscriptionId | /Guid(00000000-0000-0000-0000-000000000000)/ + ContainerTypeId | /Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/ + DisplayName | test1 + IsBillingProfileRequired | true + OwningAppId | /Guid(df4085cc-9a38-4255-badc-5c5225610475)/ + OwningTenantId | /Guid(00000000-0000-0000-0000-000000000000)/ + SPContainerTypeBillingClassification | 0 + ``` + + + + +## More information + +In SharePoint Embedded, all files and documents are stored in Containers, and each Container is identified by a Container Type. + +Container Type is a property stamped on every Container instance. Each Container Type is owned by one Application, and each Application can own only one Container Type. + +The primary function of a Container Type is to manage the application workload that can access the Containers. Container Type defines the access permissions an Application has towards all Containers of that type, including create, read, write, delete containers; manage container permissions, etc. + +More information about SharePoint Embedded and the limits can be found at the following location: [SharePoint Embedded](https://learn.microsoft.com/en-us/sharepoint/dev/embedded/concepts/app-concepts/containertypes#sharepoint-embedded-trial-container-types) diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 10256eb8164..2ac88fa5dec 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1985,6 +1985,11 @@ const sidebars: SidebarsConfig = { label: 'containertype add', id: 'cmd/spe/containertype/containertype-add' }, + { + type: 'doc', + label: 'containertype get', + id: 'cmd/spe/containertype/containertype-get' + }, { type: 'doc', label: 'containertype list', diff --git a/src/m365/spe/commands.ts b/src/m365/spe/commands.ts index c0d74cf01f4..f870b41b2b6 100644 --- a/src/m365/spe/commands.ts +++ b/src/m365/spe/commands.ts @@ -4,5 +4,6 @@ export default { CONTAINER_GET: `${prefix} container get`, CONTAINER_LIST: `${prefix} container list`, CONTAINERTYPE_ADD: `${prefix} containertype add`, + CONTAINERTYPE_GET: `${prefix} containertype get`, CONTAINERTYPE_LIST: `${prefix} containertype list` }; \ No newline at end of file diff --git a/src/m365/spe/commands/containertype/containertype-get.spec.ts b/src/m365/spe/commands/containertype/containertype-get.spec.ts new file mode 100644 index 00000000000..a9e958079eb --- /dev/null +++ b/src/m365/spe/commands/containertype/containertype-get.spec.ts @@ -0,0 +1,243 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { CommandInfo } from "../../../../cli/CommandInfo.js"; +import { Logger } from '../../../../cli/Logger.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import { cli } from '../../../../cli/cli.js'; +import commands from '../../commands.js'; +import command from './containertype-get.js'; +import { spo } from '../../../../utils/spo.js'; +import { CommandError } from '../../../../Command.js'; +import config from '../../../../config.js'; + +describe(commands.CONTAINERTYPE_GET, () => { + let log: string[]; + let logger: Logger; + let loggerLogSpy: sinon.SinonSpy; + let commandInfo: CommandInfo; + const containerTypeId = '3ec7c59d-ef31-0752-1ab5-5c343a5e8557'; + const containerTypeName = 'SharePoint Embedded Free Trial Container Type'; + const containerTypedata = { + "_ObjectType_": "Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties", + "ApplicationRedirectUrl": "", + "AzureSubscriptionId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "ContainerTypeId": "/Guid(3ec7c59d-ef31-0752-1ab5-5c343a5e8557)/", + "CreationDate": "5/9/2024", + "DisplayName": "SharePoint Embedded Free Trial Container Type", + "ExpiryDate": "6/8/2024", + "IsBillingProfileRequired": false, + "OwningAppId": "/Guid(e6b529fe-84db-412a-8e9c-5bbb75f7b037)/", + "OwningTenantId": "/Guid(00000000-0000-0000-0000-000000000000)/", + "Region": null, + "ResourceGroup": null, + "SPContainerTypeBillingClassification": 1 + }; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + sinon.stub(spo, 'ensureFormDigest').resolves({ FormDigestValue: 'abc', FormDigestTimeoutSeconds: 1800, FormDigestExpiresAt: new Date(), WebFullUrl: 'https://contoso.sharepoint.com' }); + auth.connection.active = true; + auth.connection.spoUrl = 'https://contoso.sharepoint.com'; + commandInfo = cli.getCommandInfo(command); + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + loggerLogSpy = sinon.spy(logger, 'log'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.post + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + auth.connection.spoUrl = undefined; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.CONTAINERTYPE_GET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('correctly handles random API error', async () => { + sinon.stub(request, 'post').rejects(new Error('An error has occurred')); + + await assert.rejects(command.action(logger, { options: { debug: true } } as any), new CommandError("An error has occurred")); + }); + + it('fails validation if the id is not a valid GUID', async () => { + const actual = await command.validate({ options: { id: '123' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('passes validation if the id is a valid GUID', async () => { + const actual = await command.validate({ options: { id: containerTypeId } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if required options specified (displayName)', async () => { + const actual = await command.validate({ options: { name: "test container" } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('retrieves container type by ID', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `{${containerTypeId}}1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.25117.12004", "ErrorInfo": null, "TraceCorrelationId": "df0a44a1-c013-9000-9064-f786729ad6a5" + } + , 49, { + "IsNull": false + }, 50, containerTypedata + ]); + } + } + + throw "Invalid request"; + }); + + await command.action(logger, { options: { id: containerTypeId } }); + assert(loggerLogSpy.calledWith(containerTypedata)); + }); + + it('retrieves container type by ID (debug)', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `{${containerTypeId}}1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.25117.12004", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" + }, 49, { + "IsNull": false + }, 50, containerTypedata + ]); + } + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { id: containerTypeId, debug: true } }); + assert(loggerLogSpy.calledWith(containerTypedata)); + }); + + it('correctly handles error when retrieving container type by ID', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc') { + + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.7324.1200", "ErrorInfo": { + "ErrorMessage": "An error has occurred.", "ErrorValue": null, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d", "ErrorCode": -1, "ErrorTypeName": "SPException" + }, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d" + } + ]); + } + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { options: { id: containerTypeId, verbose: true } } as any), + new CommandError("An error has occurred.")); + }); + + it('retrieves the container type by name successfully', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" + }, 46, { + "IsNull": false + }, 47, [containerTypedata] + ]); + } + } + + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `{${containerTypeId}}1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.25117.12004", "ErrorInfo": null, "TraceCorrelationId": "df0a44a1-c013-9000-9064-f786729ad6a5" + } + , 49, { + "IsNull": false + }, 50, containerTypedata + ]); + } + } + throw "Invalid request"; + }); + + await command.action(logger, { options: { name: containerTypeName } }); + assert(loggerLogSpy.calledWith(containerTypedata)); + }); + + it('correctly handles container type not found', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/_vti_bin/client.svc/ProcessQuery`) > -1) { + if (opts.headers && + opts.headers['X-RequestDigest'] && + opts.headers['X-RequestDigest'] === 'abc' && + opts.data === `1`) { + return JSON.stringify([ + { + "SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73" + }, 46, { + "IsNull": false + }, 47, [] + ]); + } + } + throw "Invalid request"; + }); + + await assert.rejects(command.action(logger, { options: { name: 'test' } } as any), new CommandError("Container type with name test not found")); + }); + +}); diff --git a/src/m365/spe/commands/containertype/containertype-get.ts b/src/m365/spe/commands/containertype/containertype-get.ts new file mode 100644 index 00000000000..c0addd80ae2 --- /dev/null +++ b/src/m365/spe/commands/containertype/containertype-get.ts @@ -0,0 +1,139 @@ +import { Logger } from '../../../../cli/Logger.js'; +import config from '../../../../config.js'; +import GlobalOptions from '../../../../GlobalOptions.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { ClientSvcResponse, ClientSvcResponseContents, FormDigestInfo, ContainerTypeProperties, spo } from '../../../../utils/spo.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; + +interface CommandArgs { + options: Options; +} + +interface Options extends GlobalOptions { + id?: string; + name?: string; +} + +class SpeContainertypeGetCommand extends SpoCommand { + public get name(): string { + return commands.CONTAINERTYPE_GET; + } + + public get description(): string { + return 'Get a Container Type'; + } + + constructor() { + super(); + this.#initTelemetry(); + this.#initOptions(); + this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); + } + + #initTelemetry(): void { + this.telemetry.push((args: CommandArgs) => { + Object.assign(this.telemetryProperties, { + id: typeof args.options.id !== 'undefined', + name: typeof args.options.name !== 'undefined' + }); + }); + } + + #initOptions(): void { + this.options.unshift( + { + option: '-i, --id [id]' + }, + { + option: '-n, --name [name]' + } + ); + } + + #initOptionSets(): void { + this.optionSets.push( + { + options: ['id', 'name'] + } + ); + } + + #initValidators(): void { + this.validators.push( + async (args: CommandArgs) => { + if (args.options.id && !validation.isValidGuid(args.options.id as string)) { + return `${args.options.id} is not a valid GUID`; + } + + return true; + } + ); + } + + #initTypes(): void { + this.types.string.push('id', 'name'); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + try { + const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug); + + if (this.verbose) { + await logger.logToStderr(`Getting the Container type...`); + } + + const containerTypeId = await this.getContainerTypeId(args.options, spoAdminUrl, logger); + const allContainerTypes = await this.getContainerTypeById(containerTypeId, spoAdminUrl, logger); + await logger.log(allContainerTypes); + } + catch (err: any) { + this.handleRejectedPromise(err); + } + } + + private async getContainerTypeById(containerTypeId: string, spoAdminUrl: string, logger: Logger): Promise { + const formDigestInfo: FormDigestInfo = await spo.ensureFormDigest(spoAdminUrl, logger, undefined, this.debug); + + const requestOptions: CliRequestOptions = { + url: `${spoAdminUrl}/_vti_bin/client.svc/ProcessQuery`, + headers: { + 'X-RequestDigest': formDigestInfo.FormDigestValue + }, + data: `{${containerTypeId}}1` + }; + + const res: string = await request.post(requestOptions); + const json: ClientSvcResponse = JSON.parse(res); + const response: ClientSvcResponseContents = json[0]; + + if (response.ErrorInfo) { + throw response.ErrorInfo.ErrorMessage; + } + + const containerTypes: ContainerTypeProperties[] = json[json.length - 1]; + return containerTypes; + } + + private async getContainerTypeId(options: Options, spoAdminUrl: string, logger: Logger): Promise { + if (options.id) { + return options.id; + } + + const containerTypes: ContainerTypeProperties[] = await spo.getAllContainerTypes(spoAdminUrl, logger, this.debug); + + // Get id of the container type by name + const containerType: ContainerTypeProperties | undefined = containerTypes.find(c => c.DisplayName === options.name); + if (!containerType) { + throw new Error(`Container type with name ${options.name} not found`); + } + + const match = containerType.ContainerTypeId.match(/\/Guid\(([^)]+)\)\//); + return match![1]; + } +} + +export default new SpeContainertypeGetCommand(); \ No newline at end of file From 0d10ddc31b6c0ca8cb5a6ef7c06448d3ee5e54e4 Mon Sep 17 00:00:00 2001 From: waldekmastykarz Date: Tue, 5 Nov 2024 12:10:56 +0100 Subject: [PATCH 18/42] Fixes using env var for login command. Closes #6469 --- src/m365/commands/login.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/m365/commands/login.ts b/src/m365/commands/login.ts index e07168eafd8..ad60b239b8e 100644 --- a/src/m365/commands/login.ts +++ b/src/m365/commands/login.ts @@ -50,8 +50,9 @@ class LoginCommand extends Command { public getRefinedSchema(schema: typeof options): z.ZodEffects | undefined { return schema - .refine(options => typeof options.appId !== 'undefined' || cli.getConfig().get(settingsNames.clientId), { - message: `appId is required. TIP: use the "m365 setup" command to configure the default appId` + .refine(options => typeof options.appId !== 'undefined' || cli.getClientId(), { + message: `appId is required. TIP: use the "m365 setup" command to configure the default appId`, + path: ['appId'] }) .refine(options => options.authType !== 'password' || options.userName, { message: 'Username is required when using password authentication', From f32e5f11b82dc598ddc6461c4987c8a32a419d3d Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Fri, 1 Nov 2024 20:50:43 +0100 Subject: [PATCH 19/42] Updates v10 upgrade guidance. Closes #6363 --- docs/docs/v10-upgrade-guidance.mdx | 274 +++++++++-------------------- 1 file changed, 82 insertions(+), 192 deletions(-) diff --git a/docs/docs/v10-upgrade-guidance.mdx b/docs/docs/v10-upgrade-guidance.mdx index a4e11b59118..2d3bc5ffa92 100644 --- a/docs/docs/v10-upgrade-guidance.mdx +++ b/docs/docs/v10-upgrade-guidance.mdx @@ -1,217 +1,129 @@ # v10 Upgrade Guidance -The v10 of CLI for Microsoft 365 introduces several breaking changes. To help you upgrade to the latest version of CLI for Microsoft 365, we've listed those changes along with any actions you may need to take. +The v10 release of CLI for Microsoft 365 introduces several breaking changes. To assist you in upgrading to the latest version, we’ve outlined the changes and the actions you may need to take. ## CLI ### Removed command `cli reconsent` -The `cli reconsent` command was removed. This command was removed due to the removal of the PnP Management Shell service application. +Due to the removal of the PnP Management Shell app registration, the cli reconsent command had no use anymore. Therefore, we have removed this command from the CLI. -## CLI configuration +### Removed `showSpinner` setting -### Removed setting `showSpinner` - -We removed the `showSpinner` config variable from the CLI configuration. Due to several issues, the spinner has been permanently removed. +Persistent issues with the spinner led to its removal from the CLI a few months ago. However, the `showSpinner` setting remained in the CLI configuration. In this major release, we have removed the `showSpinner` setting entirely. #### What action do I need to take? -Remove all entries in your script where you use the `showSpinner` setting. - -## CLI Doctor +Remove all instances in your scripts where you reference the `showSpinner` setting. ### Updated output of `cli doctor` command -For this new major version, we've adjusted the output of the `cli doctor` command to align the renaming of Azure AD to Microsoft Entra ID. - -The output is updated with `cliAadAppId` as `cliEntraAppId` and `cliAadAppTenant` as `cliEntraAppTenant`. - -#### What action do I need to take? - -Please, check the documentation of the [cli doctor](./cmd/cli/cli-doctor.mdx) command to see the updated output and adjust your scripts accordingly. - -## CLI environment variables +In this major release, we have updated the [cli doctor](./cmd/cli/cli-doctor.mdx) command output to reflect the renaming of Azure AD to Microsoft Entra ID. -### Removed deprecated CLI environment variables +The following properties have been renamed in the result: -For this new major version, we've removed the deprecated `AAD` CLI environment variables. We have renamed `CLIMICROSOFT365_AADAPPID` variable to `CLIMICROSOFT365_ENTRAAPPID`. +- `cliAadAppId` to `cliEntraAppId` +- `cliAadAppTenant` to `cliEntraAppTenant` #### What action do I need to take? -Please, check the user guidance [Log in to Microsoft 365](./user-guide/connecting-microsoft-365.mdx) command to see the updated environment variables to use. - -## CLI Status +Review the documentation of the `cli doctor` command to see the new output structure and adjust your scripts accordingly. -### Removed deprecated CLI environment variables +### Removed deprecated environment variables -We've updated the [m365 status](./cmd/status.mdx) command output. Previously the command would return `Logged out from Microsoft 365` now it will return `Logged out`. +In the aftermath of renaming all Azure AD-related commands and options to Microsoft Entra ID, we have removed the deprecated environment variable `CLIMICROSOFT365_AADAPPID` that was used to store the Microsoft Entra app ID. #### What action do I need to take? -Please, update your scripts to reflect the new output of the [m365 status](./cmd/status.mdx) command. - -## Entra ID +Instead of using `CLIMICROSOFT365_AADAPPID`, use `CLIMICROSOFT365_ENTRAAPPID` to store the Microsoft Entra app ID. -### Removed deprecated Guest value from 'aad m365group user list' command. +### Updated output of `status` command -The deprecated Guest value was removed from the `role` option in the [aad m365group user list](./cmd/entra/m365group/m365group-user-list.mdx) command. +The command output of [status](./cmd/status.mdx) has been updated. Previously, the command returned `Logged out from Microsoft 365`, now it will simply return `Logged out`. #### What action do I need to take? -Please, check the documentation of the [aad m365group user list](./cmd/entra/m365group/m365group-user-list.mdx) command to see the updated ``role` option and adjust your scripts accordingly. +Please, update your scripts to reflect the new output of the [status](./cmd/status.mdx) command. -#### What action do I need to take? - -Please, check the documentation of the affected commands to see the updated option naming and adjust your scripts accordingly. +## Entra ID -### Removed deprecated option `userName` from the `entra m365group user add` command. +### Removed deprecated `guest` value from `entra m365group user list` command. -The deprecated option `userName` was removed from the command [entra m365group user add](./cmd/entra/m365group/m365group-user-add.mdx). +In the In the entra m365group user list command, it was previously possible to use the `role` option with the value `Guest`. Since this did not yield the expected output, we have removed this option. #### What action do I need to take? -In your scripts, replace every occurrence of the deprecated option `userName` with `userNames`. +Remove all --role guest instances from your scripts. To list guest users of a group, use [entra m365group user list](./cmd/entra/m365group/m365group-user-list.mdx) in conjunction with [entra user get](./cmd/entra/user/user-get.mdx) to determine if a user is a guest. ### Enhanced `entra m365group set` command -We've enhanced the [entra m365group set](./cmd/entra/m365group/m365group-set.mdx) command so that a group that we wish to update can also be retrieved by it's displayName. Before, this was only able by the group ID. We have replaced the original `displayName` option by `newDisplayName`. Secondly we've updated the way the command handles updating users. Ownes/members that are not specified, will now be removed from the group. To add users without removing old users, the command [entra m365group user add](./cmd/entra/m365group/m365group-user-add) should be used. We've also added extra options to specify owners and members by UserName or Id. - -#### What action do I need to take? - -Make sure that if you are currently updating groups using the `displayName` option, you update your scripts to use the `newDisplayName` option instead. Also make sure that your script uses the new option names for updating Owners and Members, and that it does include all users that should be a member or owner of the group. - -### Renamed `entra group user` commands to `entra group member` - -We have renamed the `entra group user` commands to `entra group member` to better reflect the command's purpose,. The following commands were renamed: -- [entra group member add](./cmd/entra/group/group-member-add.mdx) -- [entra group member list](./cmd/entra/group/group-member-list.mdx) -- [entra group member set](./cmd/entra/group/group-member-set.mdx) +We've enhanced the [entra m365group set](./cmd/entra/m365group/m365group-set.mdx) command to allow retrieving a group by its `displayName`. Previously, groups could only be retrieved by their ID. We have replaced the original `displayName` option with `newDisplayName`. Additionally, owners/members not specified in the command will now be **removed** from the group. To add members/owners without affecting existing ones, use [entra m365group user add](./cmd/entra/m365group/m365group-user-add). #### What action do I need to take? -Please update your scripts to use the new command names. +Ensure your scripts use `newDisplayName` when updating groups by `displayName`. Also, ensure that all intended group members and owners are specified, as any unspecified members or owners will be removed. -### Removed `aad` options and aliasses to `aad` commands. +### Removed `aad` options and aliasses -As part of the renaming of Azure AD to Microsoft Entra ID, we have removed the `aad` options and aliases. From now on only `entra` commands are available. +As part of renaming Azure AD to Microsoft Entra ID, all references to `aad` in command and option names have been replaced with `entra`. For example, `aadAppId` is now `entraAppId`. #### What action do I need to take? -Please, check if your scripts use any of the `aad` commands and if so update it to `entra` commands. - -### Removed deprecated option `username` from `entra m365group user set` command. - -The deprecated option `username` was removed from the [entra m365group user set](./cmd/entra/m365group/m365group-user-set.mdx) command. - -#### What action do I need to take? - -Please, check the documentation of the [entra m365group user set](./cmd/entra/m365group/m365group-user-set.mdx) command to see the updated `username` option and adjust your scripts accordingly. +Update any `aad` references in your scripts to `entra` for compatibility. ## SharePoint ### Updated `spo site appcatalog remove` options -We updated the [spo site appcatalog remove](./cmd/spo/site/site-appcatalog-remove.mdx) command. This command is a destructive operation that removes a site collection app catalog. Previously, it did not prompt for confirmation before executing this action, which could potentially lead to unintended data loss. To address this, we have added a confirmation prompt by default. - -Because of this rework, we have added a new option. - -**New options:** - -Option | Description ---- | --- -`-f, --force` | Don't prompt for confirmation. +The [spo site appcatalog remove](./cmd/spo/site/site-appcatalog-remove.mdx) command now includes a confirmation prompt to prevent unintentional deletions. To bypass this prompt, use the `--force` flag. #### What action do I need to take? -Update your scripts with the following: - -- Add the option `--force` if you want to bypass the confirmation prompt and proceed with the removal operation automatically. +Add `--force` to your scripts to bypass the confirmation prompt, if desired. -### Removed the deprecated `wait` option and output from the `spo tenant recyclebinitem restore` command +### Removed deprecated `wait` option from `spo site remove` and `spo tenant recyclebinitem restore` -For this new major version, we've removed the deprecated `wait` option from the `spo tenant recyclebinitem restore` command. The command now won't return a response on success. - -#### What action do I need to take? +The deprecated `wait` option has been removed from the [spo site remove](./cmd/spo/site/site-remove.mdx) and [spo tenant recyclebinitem restore](./cmd/spo/tenant/tenant-recyclebinitem-restore.mdx) commands. These commands no longer return a response on success. -- Please, check the documentation of the [spo tenant recyclebinitem restore](./cmd/spo/tenant/tenant-recyclebinitem-restore.mdx) command to see the updated output and adjust your scripts accordingly. -- In the `spo tenant recyclebinitem restore` command, remove deprecated `wait` option from your scripts. +**What action do I need to take?** -### Removed the deprecated `wait` option and output from the `spo site remove` command +Remove the `--wait` option and adjust scripts to not expect a response from these commands. -For this new major version, we've removed the deprecated `wait` option from the `spo site remove` command. - -#### What action do I need to take? - -In the [spo site remove](./cmd/spo/site/site-remove.mdx) command, remove deprecated `wait` option from your scripts. - -### Removes duplicate property from 'spo list list' command. +### Removes duplicate property from `spo list list` command. In the [spo list list](./cmd/spo/list/list-list.mdx) we removed the `Url` from the command output as it was a duplicate of the `RootFolder/ServerRelativeUrl` property. #### What action do I need to take? -Please update your scripts not to use the `Url` property from the command output. - -### Updated command `spo file copy` - -Because of some limitations of the current [spo file copy](./cmd/spo/file/file-copy.mdx) command, we have decided to move it to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command input and output have changed. - -**Command options:** - -Unfortunately, we had to drop the `--resetAuthorAndCreated` options as it's no longer supported by the new endpoint. In return, we were able to add two new options: -- `--ignoreVersionHistory`: Only copy the most recent version of the file and ignore the version history. -- `--skipWait`: Don't wait for the copy operation to complete. - -**Command output:** - -In the past versions of CLI for Microsoft 365, the command had no output. When using option `--nameConflictBehavior rename`, it's hard for the user to know what the actual name of the copied file is. With the new endpoint, the command now returns the file information about the destination file, providing you with all the info you need to execute other commands on this file. - -#### What action do I need to take? - -When using the [spo file copy](./cmd/spo/file/file-copy.mdx) command, please use the new command input. This means that you'll have to remove option `--resetAuthorAndCreated` from your scripts and automation tools. - -### Updated command `spo file move` - -Just like `spo file copy`, we have decided to move the [spo file move](./cmd/spo/file/file-move.mdx) command to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command input and output have changed. - -**Command options:** - -Unfortunately, we had to drop the `--retainEditorAndModified` options as it's no longer supported by the new endpoint. In return, we were able to add two new options: -- `--includeItemPermissions`: Ensure that item-level permissions are preserved during the move. -- `--skipWait`: Don't wait for the copy operation to complete. - -**Command output:** - -In the past versions of CLI for Microsoft 365, the command had no output. When using option `--nameConflictBehavior rename`, it's hard for the user to know what the actual name of the copied file is. With the new endpoint, the command now returns the file information about the destination file, providing you with all the info you need to execute other commands on this file. - -#### What action do I need to take? - -When using the [spo file move](./cmd/spo/file/file-move.mdx) command, please use the new command input. This means that you'll have to remove option `--retainEditorAndModified` from your scripts and automation tools. - -### Updated command `spo folder move` and `spo folder copy` - -Because of some limitations of the current commands [spo folder move](./cmd/spo/folder/folder-move.mdx) and [spo folder copy](./cmd/spo/folder/folder-copy.mdx), we have decided to move it to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command's input and output have changed. +Please update your scripts not to use the `Url` property from the command output, instead use `RootFolder/ServerRelativeUrl`. -**Command options:** +### Updated commands for file and folder operations -Unfortunately, we had to drop the `--retainEditorAndModified`, `--resetAuthorAndCreated` and `--bypassSharedLock` options as it's no longer supported by the new endpoint. In return, we were able to add a new option: -- `--skipWait`: Don't wait for the move operation to complete. +Due to limitations with the current endpoints for file and folder operations, the commands [spo file copy](./cmd/spo/file/file-copy.mdx), [spo file move](./cmd/spo/file/file-move.mdx), [spo folder copy](./cmd/spo/folder/folder-copy.mdx), and [spo file move](./cmd/spo/file/file-move.mdx) have been updated to use new endpoints. This change improves the commands' functionality and reliability. Consequently, the command inputs and outputs have been updated. -**Command output:** +Several options previously available are no longer supported with the new commands: -In the past versions of CLI for Microsoft 365, the command had no output. When using option `--nameConflictBehavior rename`, it's hard for the user to know what the actual name of the moved folder is. With the new endpoint, the command now returns the folder information about the destination folder, providing you with all the info you need to execute other commands on this folder. +* `spo file copy`: Removed `--resetAuthorAndCreated` +* `spo file move`: Removed `--retainEditorAndModified` +* `spo folder copy`: Removed `--resetAuthorAndCreated` and `--bypassSharedLock` +* `spo folder move`: Removed `--retainEditorAndModified` and `--bypassSharedLock` -#### What action do I need to take? - -When using these commands, please use the new command input. This means that you'll have to remove options `--retainEditorAndModified`, `--resetAuthorAndCreated` and `--bypassSharedLock` from your scripts and automation tools. +In return, we have added new options to the commands: -### Removed `spo folder rename` alias +* `spo file copy`: + * `--ignoreVersionHistory`: Only copy the most recent version of the file and ignore the version history. + * `--skipWait`: Don't wait for the move operation to complete. +* `spo file move`: + * `--includeItemPermissions`: Ensure that item-level permissions are preserved during the move. + * `--skipWait`: Don't wait for the move operation to complete. +* `spo folder copy` and `spo folder move`: + * `--skipWait`: Don't wait for the move operation to complete. -The `spo folder rename` command was removed and replaced by the [spo folder set](./cmd/spo/folder/folder-set.mdx) command. +Previously, these commands provided no output, making it difficult to track the final name of copied or moved items, especially when using the `--nameConflictBehavior rename` option. With the new endpoints, each command now returns information about the destination file or folder, allowing you to seamlessly execute further commands on the moved or copied items. #### What action do I need to take? -Please, update your scripts to use the `spo folder set` command instead of `spo folder rename`. +To continue using these commands, update your scripts and automation tools to reflect the new command inputs. Remove any unsupported options (`--resetAuthorAndCreated`, `--retainEditorAndModified`, and `--bypassSharedLock` where applicable) to ensure compatibility with the updated commands. ### Updated output of `spo applicationcustomizer get` command @@ -219,45 +131,15 @@ In the previous version of CLI, the command output had a `Rights` property that #### What action do I need to take? -Please, check the documentation of the [spo applicationcustomizer get](./cmd/spo/applicationcustomizer/applicationcustomizer-get.mdx) command to see the updated output and adjust your scripts accordingly. - -### Updated `spo contenttype field remove` options - -We made some slight changes to the options of command [spo contenttype field remove](./cmd/spo/contenttype/contenttype-field-remove.mdx) command. The following changes were made: - -- Removed the short option for `contentTypeId`. -- Renamed the option `fieldLinkId` to `id` and added the short option `i`. - -#### What actions do I need to take? - -Please update your scripts to make sure that you use the new name/short options. - -### Updated `spo contenttype field set` options - -We updated the [spo contenttype field set](./cmd/spo/contenttype/contenttype-field-set.mdx) command. We made the following changes: - -- Removed the short option for `contentTypeId` -- Add the short option `i` for `id` - -#### What actions do I need to take? - -Please update your scripts to make sure that you use the correct options. +Please, check the documentation of the [spo applicationcustomizer get](./cmd/spo/applicationcustomizer/applicationcustomizer-get.mdx) command to see the updated output and adjust your scripts accordingly. If you are using the `Rights` property, there is no change you should make. ### Updated `spo sitescript get` output -We updated the output from the [`spo sitescript get`](./cmd/spo/sitescript/sitescript-get.mdx) command. The `Content` property is now a JSON object instead of a stringified JSON object, so it's easier to interact with the sitescript data, even when --content is not specified. +We updated the output from the [spo sitescript get](./cmd/spo/sitescript/sitescript-get.mdx) command. The `Content` property is now a JSON object instead of a stringified JSON object, so it's easier to interact with the sitescript data, even when `--content` is not specified. #### What action do I need to take? -Please update your scripts to reflect the new output of the [`spo sitescript get`](./cmd/spo/sitescript/sitescript-get.mdx) command. - -### Renamed `spo site rename` to `spo tenant site rename` - -The `spo site rename` command was renamed to [`spo tenant site rename`](./cmd/spo/tenant/tenant-site-rename.mdx) to better reflect the command's purpose as this is an action that only an administrator can do. - -#### What action do I need to take? - -Please update your scripts to use the new command name. +Adjust your scripts to expect a valid JSON object instead of a stringified object in the `Content` property of the output. ## SharePoint Framework @@ -267,21 +149,19 @@ Overwriting the SPFx package should be the default behavior of the continuous de #### What action do I need to take? -Please update your scripts not to use the `overwrite` option. +Please update your scripts not to use the `--overwrite` option. ## Teams ### Enhanced `teams cache remove` command -We enhanced the [teams cache remove](./cmd/teams/cache/cache-remove.mdx) command to support the removal of the cache from the new Teams client. We also made it so that by default, the command will remove the cache for the `new` client. +We enhanced the [teams cache remove](./cmd/teams/cache/cache-remove.mdx) command to support the removal of the cache from the new Teams client. The breaking change we introduced is that it now targets new Teams clients by default. To still support the old client, we have added the option `--client`, in which you can specify `classic` as value to remove the cache of the old Teams client. #### What action do I need to take? -Add the `--client` option to your scripts to specify which client's cache you want to remove. Otherwise, upon the release of v10, it will attempt to remove the cache from the new client. - -## Teams +Add the `--client` option to your scripts to specify which client's cache you want to remove. Otherwise, it will attempt to remove the cache from the new client. ### Removes duplicate property from 'teams tab list' command. @@ -289,16 +169,21 @@ For the [teams tab list](./cmd/teams/tab/tab-list.mdx) command we removed the `t #### What action do I need to take? -Please update your scripts not to use the `teamsAppTabId` property from the command output. +Please update your scripts not to use the `teamsAppTabId` property from the command output. Instead, use the `teamsApp/id` property. -## Yammer (Viva Engage) +## General -### Removal of deprecated Yammer aliases +### Aligned command names -We have removed all deprecated `Yammer` command aliases. These aliases were introduced in the previous version to prevent the introduction of breaking changes. Since Yammer has been renamed to Viva Engage a while ago, the following aliases were removed: +Some command names have been updated to reflect the latest Microsoft 365 terminology. Additionally, command improvements sometimes lead to renaming for better functionality alignment. -Alias | Command ---|-- +v9 command | v10 command +--- | --- +`entra group user add` | [entra group member add](./cmd/entra/group/group-member-add.mdx) +`entra group user list` | [entra group member list](./cmd/entra/group/group-member-list.mdx) +`entra group user set` | [entra group member set](./cmd/entra/group/group-member-set.mdx) +`spo folder rename` | [spo folder set](./cmd/spo/folder/folder-set.mdx) +`spo site rename` | [spo tenant site rename](./cmd/spo/tenant/tenant-site-rename.mdx) `yammer search` | [viva engage search](./cmd/viva/engage/engage-search.mdx) `yammer group list` | [viva engage group list](./cmd/viva/engage/engage-group-list.mdx) `yammer group user add` | [viva engage group user add](./cmd/viva/engage/engage-group-user-add.mdx) @@ -323,26 +208,30 @@ Alias | Command #### What action do I need to take? -Replace any of the aliases mentioned above with the corresponding command name. The functionality of the command hasn't changed. - -## General +Update your scripts to use the new command names listed. -### Aligned options with naming convention +### Aligned naming options -In version 10 of the CLI for Microsoft 365, we have made updates to the options for specific commands, aligning with our naming convention. This includes renaming options to ensure consistency and improve the CLI experience. Some other options have been renamed to align with the response output of the commands. These changes aim to make it easier for you to use the CLI. +Just like with command names, we also have to rename options to reflect the changes in the product. We also try to make the options more descriptive and clearer to make it easier for you to use the CLI. +These changes aim to make it easier for you to use the CLI. -We've updated the following commands and options: +We've updated the following options: -Command|Old option|New option --------|----------|---------- +Command | Old option | New option +------- | ---------- | ---------- [app permission add](./cmd/app/permission/permission-add.mdx) | `applicationPermission`
`delegatedPermission` | `applicationPermissions`
`delegatedPermissions` [entra enterpriseapp add](./cmd/entra/enterpriseapp/enterpriseapp-add) | `appId`
`appName` | `id`
`displayName` [entra enterpriseapp get](./cmd/entra/enterpriseapp/enterpriseapp-get) | `appId`
`appDisplayName`
`appObjectId` | `id`
`displayName`
`objectId` [entra group member list](./cmd/entra/group/group-member-list.mdx) | `groupDisplayName` | `groupName` +[entra m365group set](./cmd/entra/m365group/m365group-set.mdx) | `displayName` | `newDisplayName` [entra m365group conversation post list](./cmd/entra/m365group/m365group-conversation-post-list.mdx) | `groupDisplayName` | `groupName` [entra m365group recyclebinitem list](./cmd/entra/m365group/m365group-recyclebinitem-list.mdx) | `groupDisplayName` | `groupName` +[entra m365group user add](./cmd/entra/m365group/m365group-user-add.mdx) | `userName` | `userNames` [entra m365group user remove](./cmd/entra/m365group/m365group-user-remove.mdx) | `userName` | `userNames` +[entra m365group user set](./cmd/entra/m365group/m365group-user-set.mdx) | `userName` | `userNames` [outlook message get](./cmd/outlook/message/message-get.mdx) | `userPrincipalName` | `userName` +[spo contenttype field remove](./cmd/spo/contenttype/contenttype-field-remove.mdx) | `i`
`fieldLinkId` | `contentTypeId`
`id` +[spo contenttype field set](./cmd/spo/contenttype/contenttype-field-set.mdx) | `i` | `contentTypeId` #### What action do I need to take? @@ -353,6 +242,7 @@ If you use any of the commands listed above, ensure that you use the new option We noticed that some of the `list` commands were not returning any output when no items were found. We've updated all `list` commands to return an empty array when no items are found. The commands impacted by this change are: + - [entra oauth2grant list](./cmd/entra/oauth2grant/oauth2grant-list.mdx) - [flow environment list](./cmd/flow/environment/environment-list.mdx) - [pa app list](./cmd/pa/app/app-list.mdx) @@ -372,4 +262,4 @@ The commands impacted by this change are: #### What action do I need to take? -Update your scripts using these commands to handle the empty array output when there are no items found. +Update your scripts to expect an empty array when no items are found. From fc5c2f76189ff283523132a26d061f854ec1ec15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Wed, 6 Nov 2024 00:04:30 +0100 Subject: [PATCH 20/42] Updates release notes --- docs/docs/about/release-notes.mdx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index dcb25623714..fe40cebebe0 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -4,6 +4,20 @@ sidebar_position: 3 # Release notes +## v10.1.0 (beta) + +### New commands + +**SharePoint Embedded:** + +- [spe container list](../cmd/spe/container/container-list.mdx) - lists containers of a specific Container Type [#6082](https://github.com/pnp/cli-microsoft365/issues/6082) +- [spe containertype get](../cmd/spe/containertype/containertype-get.mdx) - retrieves a specific Container Type [#5991](https://github.com/pnp/cli-microsoft365/issues/5991) + +### Changes + +- fixed using env var for login command [#6469](https://github.com/pnp/cli-microsoft365/issues/6469) +- updated v10 upgrade guidance [#6363](https://github.com/pnp/cli-microsoft365/issues/6363) + ## [v10.0.0](https://github.com/pnp/cli-microsoft365/releases/tag/v10.0.0) ### New commands From c94236fc6dafee5a6eab3f2842e2b1b279f856ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Wed, 6 Nov 2024 22:24:17 +0100 Subject: [PATCH 21/42] Fixes missing 'sp' alias in 'enterpriseapp' commands. Closes #6476 --- .../entra/commands/enterpriseapp/enterpriseapp-add.spec.ts | 5 +++++ src/m365/entra/commands/enterpriseapp/enterpriseapp-add.ts | 4 ++++ .../entra/commands/enterpriseapp/enterpriseapp-get.spec.ts | 5 +++++ src/m365/entra/commands/enterpriseapp/enterpriseapp-get.ts | 4 ++++ .../entra/commands/enterpriseapp/enterpriseapp-list.spec.ts | 5 +++++ src/m365/entra/commands/enterpriseapp/enterpriseapp-list.ts | 4 ++++ 6 files changed, 27 insertions(+) diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.spec.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.spec.ts index 009558afa22..119d1bd07a3 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.spec.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.spec.ts @@ -68,6 +68,11 @@ describe(commands.ENTERPRISEAPP_ADD, () => { assert.notStrictEqual(command.description, null); }); + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.SP_ADD]); + }); + it('fails validation if neither the id, displayName, nor objectId option specified', async () => { sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { if (settingName === settingsNames.prompt) { diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.ts index f732ea1a64a..db3f5bf5138 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-add.ts @@ -26,6 +26,10 @@ class EntraEnterpriseAppAddCommand extends GraphCommand { return 'Creates an enterprise application (or service principal) for a registered Entra app'; } + public alias(): string[] | undefined { + return [commands.SP_ADD]; + } + constructor() { super(); diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.spec.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.spec.ts index a95f812c6d5..5b083ef7415 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.spec.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.spec.ts @@ -82,6 +82,11 @@ describe(commands.ENTERPRISEAPP_GET, () => { assert.notStrictEqual(command.description, null); }); + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.SP_GET]); + }); + it('retrieves information about the specified enterprise application using its display name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { if ((opts.url as string).indexOf(`/v1.0/servicePrincipals?$filter=displayName eq `) > -1) { diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.ts index 0fe94b35b4a..48798595b9a 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-get.ts @@ -26,6 +26,10 @@ class EntraEnterpriseAppGetCommand extends GraphCommand { return 'Gets information about an Enterprise Application'; } + public alias(): string[] | undefined { + return [commands.SP_GET]; + } + constructor() { super(); diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.spec.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.spec.ts index 5e9355420f9..ea073a19515 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.spec.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.spec.ts @@ -134,6 +134,11 @@ describe(commands.ENTERPRISEAPP_LIST, () => { assert.notStrictEqual(command.description, null); }); + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.SP_LIST]); + }); + it('defines correct properties for the default output', () => { assert.deepStrictEqual(command.defaultProperties(), ['appId', 'displayName', 'tag']); }); diff --git a/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.ts b/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.ts index 903518dfe78..3995679b25f 100644 --- a/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.ts +++ b/src/m365/entra/commands/enterpriseapp/enterpriseapp-list.ts @@ -26,6 +26,10 @@ class EntraEnterpriseAppListCommand extends GraphCommand { return 'Lists the enterprise applications (or service principals) in Entra ID'; } + public alias(): string[] | undefined { + return [commands.SP_LIST]; + } + constructor() { super(); From f447fafe43e256d93c6d9ce8094b10c62654850a Mon Sep 17 00:00:00 2001 From: Martin Lingstuyl Date: Fri, 8 Nov 2024 11:19:51 +0100 Subject: [PATCH 22/42] Updates release notes --- docs/docs/about/release-notes.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index fe40cebebe0..90fea8bacf2 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -17,6 +17,7 @@ sidebar_position: 3 - fixed using env var for login command [#6469](https://github.com/pnp/cli-microsoft365/issues/6469) - updated v10 upgrade guidance [#6363](https://github.com/pnp/cli-microsoft365/issues/6363) +- fixed missing 'sp' aliases in some 'enterpriseapp' commands. [#6476](https://github.com/pnp/cli-microsoft365/issues/6476) ## [v10.0.0](https://github.com/pnp/cli-microsoft365/releases/tag/v10.0.0) From 59ae248e0555fcbd6fec774e721106207bdcbe56 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Fri, 9 Aug 2024 09:10:08 +0200 Subject: [PATCH 23/42] Updates entra m365group commands to accept displayName option. Closes #6147 --- .../m365group/m365group-conversation-list.mdx | 15 ++- .../cmd/entra/m365group/m365group-get.mdx | 13 +- .../cmd/entra/m365group/m365group-remove.mdx | 11 +- .../cmd/entra/m365group/m365group-renew.mdx | 13 +- .../cmd/entra/m365group/m365group-teamify.mdx | 15 ++- .../entra/m365group/m365group-user-add.mdx | 4 +- .../entra/m365group/m365group-user-list.mdx | 6 + .../entra/m365group/m365group-user-remove.mdx | 2 +- .../entra/m365group/m365group-user-set.mdx | 6 +- .../m365group-conversation-list.spec.ts | 22 +++- .../m365group/m365group-conversation-list.ts | 36 +++++- .../commands/m365group/m365group-get.spec.ts | 92 +++++++++++++- .../entra/commands/m365group/m365group-get.ts | 35 ++++-- .../m365group/m365group-remove.spec.ts | 17 ++- .../commands/m365group/m365group-remove.ts | 37 ++++-- .../m365group/m365group-renew.spec.ts | 26 +++- .../commands/m365group/m365group-renew.ts | 33 +++-- .../m365group/m365group-teamify.spec.ts | 118 +++++++++++++++++- .../commands/m365group/m365group-teamify.ts | 38 +++--- .../m365group/m365group-user-remove.spec.ts | 3 +- .../m365group/m365group-user-remove.ts | 4 + src/utils/entraGroup.spec.ts | 75 +++++++++++ src/utils/entraGroup.ts | 22 ++++ 23 files changed, 554 insertions(+), 89 deletions(-) diff --git a/docs/docs/cmd/entra/m365group/m365group-conversation-list.mdx b/docs/docs/cmd/entra/m365group/m365group-conversation-list.mdx index 1cf4dbcdde7..a9b0cfe1627 100644 --- a/docs/docs/cmd/entra/m365group/m365group-conversation-list.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-conversation-list.mdx @@ -15,20 +15,29 @@ m365 entra m365group conversation list [options] ## Options ```md definition-list -`-i, --groupId ` -: The ID of the Microsoft 365 group +`-i, --groupId [groupId]` +: The ID of the Microsoft 365 Group. Specify either `groupId` or `groupName`, but not both. + +`-n, --groupName [groupName]` +: Display name of the Microsoft 365 Group. Specify either `groupId` or `groupName`, but not both. ``` ## Examples -Lists conversations for the specified Microsoft 365 group +Lists conversations for the Microsoft 365 group specified by id. ```sh m365 entra m365group conversation list --groupId '00000000-0000-0000-0000-000000000000' ``` +Lists conversations for the Microsoft 365 group specified by displayName. + +```sh +m365 entra m365group conversation list --groupName Finance +``` + ## Response diff --git a/docs/docs/cmd/entra/m365group/m365group-get.mdx b/docs/docs/cmd/entra/m365group/m365group-get.mdx index f7663c58ca3..c1fd7f619a8 100644 --- a/docs/docs/cmd/entra/m365group/m365group-get.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-get.mdx @@ -15,8 +15,11 @@ m365 entra m365group get [options] ## Options ```md definition-list -`-i, --id ` -: The ID of the Microsoft 365 Group or Microsoft Teams team to retrieve information for +`-i, --id [id]` +: The ID of the Microsoft 365 Group or Microsoft Teams team to retrieve information for. Specify either `id` or `displayName`, but not both. + +`-n, --displayName [displayName]` +: Display name of the Microsoft 365 Group or Microsoft Teams team to retrieve information for. Specify either `id` or `displayName`, but not both. `--includeSiteUrl` : Set to retrieve the site URL for the group @@ -32,6 +35,12 @@ Get information about the Microsoft 365 Group with id _1caf7dcd-7e83-4c3a-94f7-9 m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 ``` +Get information about the Microsoft 365 Group with displayName _Finance_ + +```sh +m365 entra m365group get --displayName Finance +``` + Get information about the Microsoft 365 Group with id _1caf7dcd-7e83-4c3a-94f7-932a1299c844_ and also retrieve the URL of the corresponding SharePoint site ```sh diff --git a/docs/docs/cmd/entra/m365group/m365group-remove.mdx b/docs/docs/cmd/entra/m365group/m365group-remove.mdx index 3b65202a7a2..0ebd7c05596 100644 --- a/docs/docs/cmd/entra/m365group/m365group-remove.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-remove.mdx @@ -13,8 +13,11 @@ m365 entra m365group remove [options] ## Options ```md definition-list -`-i, --id ` -: The ID of the Microsoft 365 Group to remove +`-i, --id [id]` +: The ID of the Microsoft 365 Group to remove. Specify either `id` or `displayName`, but not both. + +`-n, --displayName [displayName]` +: Display name of the Microsoft 365 Group to remove. Specify either `id` or `displayName`, but not both. `-f, --force` : Don't prompt for confirming removing the group @@ -47,10 +50,10 @@ Remove group with id _28beab62-7540-4db1-a23f-29a6018a3848_. Will prompt for con m365 entra m365group remove --id 28beab62-7540-4db1-a23f-29a6018a3848 ``` -Remove group with id _28beab62-7540-4db1-a23f-29a6018a3848_ without prompting for confirmation +Remove group with displayName _Finance_ without prompting for confirmation ```sh -m365 entra m365group remove --id 28beab62-7540-4db1-a23f-29a6018a3848 --force +m365 entra m365group remove --displayName Finance --force ``` Remove group with id _28beab62-7540-4db1-a23f-29a6018a3848_ without prompting for confirmation and without moving it to the Recycle Bin diff --git a/docs/docs/cmd/entra/m365group/m365group-renew.mdx b/docs/docs/cmd/entra/m365group/m365group-renew.mdx index bafec1f8263..c67fe162964 100644 --- a/docs/docs/cmd/entra/m365group/m365group-renew.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-renew.mdx @@ -13,8 +13,11 @@ m365 entra m365group renew [options] ## Options ```md definition-list -`-i, --id ` -: The ID of the Microsoft 365 group to renew +`-i, --id [id]` +: The ID of the Microsoft 365 group to renew. Specify either `id` or `displayName`, but not both. + +`-n, --displayName [displayName]` +: Display name of the Microsoft 365 Group to renew. Specify either `id` or `displayName`, but not both. ``` @@ -31,6 +34,12 @@ Renew the Microsoft 365 group with id _28beab62-7540-4db1-a23f-29a6018a3848_ m365 entra m365group renew --id 28beab62-7540-4db1-a23f-29a6018a3848 ``` +Renew the Microsoft 365 group with displayName _Finance_ + +```sh +m365 entra m365group renew --displayName Finance +``` + ## Response The command won't return a response on success. diff --git a/docs/docs/cmd/entra/m365group/m365group-teamify.mdx b/docs/docs/cmd/entra/m365group/m365group-teamify.mdx index 4f31bb9f458..093eec6476e 100644 --- a/docs/docs/cmd/entra/m365group/m365group-teamify.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-teamify.mdx @@ -14,10 +14,13 @@ m365 entra m365group teamify [options] ```md definition-list `-i, --id [id]` -: The ID of the Microsoft 365 Group to connect to Microsoft Teams. Specify either `id` or `mailNickname` but not both. +: The ID of the Microsoft 365 Group to connect to Microsoft Teams. Specify either `id`, `displayName` or `mailNickname`, but not multiple. + +`-n, --displayName [displayName]` +: Display name of the Microsoft 365 Group to connect to Microsoft Teams. Specify either `id`, `displayName` or `mailNickname`, but not multiple. `--mailNickname [mailNickname]` -: The mail alias of the Microsoft 365 Group to connect to Microsoft Teams. Specify either `id` or `mailNickname` but not both. +: The mail alias of the Microsoft 365 Group to connect to Microsoft Teams. Specify either `id`, `displayName` or `mailNickname`, but not multiple. ``` @@ -30,7 +33,13 @@ Creates a new Microsoft Teams team under existing Microsoft 365 group with the s m365 entra m365group teamify --id e3f60f99-0bad-481f-9e9f-ff0f572fbd03 ``` -Creates a new Microsoft Teams team under existing Microsoft 365 group with the specified mailNickname. +Creates a new Microsoft Teams team under existing Microsoft 365 group with the specified displayName. + +```sh +m365 entra m365group teamify --displayName Finance +``` + +Creates a new Microsoft Teams team under existing Microsoft 365 group with the specified mailNickname._ ```sh m365 entra m365group teamify --mailNickname GroupName diff --git a/docs/docs/cmd/entra/m365group/m365group-user-add.mdx b/docs/docs/cmd/entra/m365group/m365group-user-add.mdx index 8f708e970a7..b90ea8e9f77 100644 --- a/docs/docs/cmd/entra/m365group/m365group-user-add.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-user-add.mdx @@ -13,7 +13,7 @@ m365 entra m365group user add [options] ## Alias ```sh -m365 teams user add +m365 teams user add [options] ``` ## Options @@ -38,7 +38,7 @@ m365 teams user add : The user principal names of users. You can also pass a comma-separated list of UPNs. Specify either `ids` or `userNames` but not both. `-r, --role [role]` -: The role to be assigned to the new user: `Owner,Member`. Default `Member`. +: The role to be assigned to the new user. Allowed values: `Owner`, `Member`. Default `Member`. ``` diff --git a/docs/docs/cmd/entra/m365group/m365group-user-list.mdx b/docs/docs/cmd/entra/m365group/m365group-user-list.mdx index 74524fe8469..173ae648e97 100644 --- a/docs/docs/cmd/entra/m365group/m365group-user-list.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-user-list.mdx @@ -57,6 +57,12 @@ List specific properties for all group users from a group specified by ID. m365 entra m365group user list --groupId 03cba9da-3974-46c1-afaf-79caa2e45bbe --properties "id,jobTitle,companyName,accountEnabled" ``` +List all group members that are guest users. + +```sh +m365 entra m365group user list --groupDisplayName Developers --filter "userType eq 'Guest'" +``` + ## Response diff --git a/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx b/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx index a4d6b59989a..835b05790ad 100644 --- a/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-user-remove.mdx @@ -17,7 +17,7 @@ m365 entra m365group user remove [options] : The ID of the Microsoft 365 group. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`. `--groupName [groupName]` -: The display name of the Microsoft 365 group. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`.. +: The display name of the Microsoft 365 group. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`. `--teamId [teamId]` : The ID of the Microsoft Teams team. Specify only one of the following: `groupId`, `groupName`, `teamId`, or `teamName`. diff --git a/docs/docs/cmd/entra/m365group/m365group-user-set.mdx b/docs/docs/cmd/entra/m365group/m365group-user-set.mdx index b5b60faf6b6..32deed6bba5 100644 --- a/docs/docs/cmd/entra/m365group/m365group-user-set.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-user-set.mdx @@ -32,7 +32,7 @@ m365 entra m365group user set [options] : The user principal names of users. You can also pass a comma-separated list of UPNs. Specify only one of the following `ids` or `userNames`. `-r, --role ` -: Role to set for the given user in the specified Microsoft 365 Group or Microsoft Teams team. Allowed values: `Owner`, `Member` +: Role to set for the given user in the specified Microsoft 365 Group or Microsoft Teams team. Allowed values: `Owner`, `Member`. ``` @@ -70,13 +70,13 @@ m365 entra m365group user set --groupId '00000000-0000-0000-0000-000000000000' - Demote multiple users specified by the userNames parameter from Owner to Member of the given Microsoft Teams team ```sh -m365 entra teams user set --teamId '00000000-0000-0000-0000-000000000000' --userNames 'anne.matthews@contoso.onmicrosoft.com,john.doe@contoso.onmicrosoft.com' --role Member +m365 teams user set --teamId '00000000-0000-0000-0000-000000000000' --userNames 'anne.matthews@contoso.onmicrosoft.com,john.doe@contoso.onmicrosoft.com' --role Member ``` Demote multiple users specified by the ids parameter from Owner to Member in the given Microsoft Teams team ```sh -m365 entra teams user set --teamName 'Engineering' --ids '74a3b772-3122-447b-b9da-10895e238219,dd3d21e4-a142-46b9-8482-bca8fe9596b3' --role Member +m365 teams user set --teamName 'Engineering' --ids '74a3b772-3122-447b-b9da-10895e238219,dd3d21e4-a142-46b9-8482-bca8fe9596b3' --role Member ``` ## Response diff --git a/src/m365/entra/commands/m365group/m365group-conversation-list.spec.ts b/src/m365/entra/commands/m365group/m365group-conversation-list.spec.ts index c9e5ae6745f..f8ac0902fd7 100644 --- a/src/m365/entra/commands/m365group/m365group-conversation-list.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-conversation-list.spec.ts @@ -50,6 +50,7 @@ describe(commands.M365GROUP_CONVERSATION_LIST, () => { sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(true); + sinon.stub(entraGroup, 'getGroupIdByDisplayName').resolves('00000000-0000-0000-0000-000000000000'); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); }); @@ -102,7 +103,7 @@ describe(commands.M365GROUP_CONVERSATION_LIST, () => { assert.strictEqual(actual, true); }); - it('Retrieve conversations for the specified group by groupId in the tenant (verbose)', async () => { + it('Retrieve conversations for the group specified by groupId in the tenant (verbose)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/00000000-0000-0000-0000-000000000000/conversations`) { return jsonOutput; @@ -119,6 +120,25 @@ describe(commands.M365GROUP_CONVERSATION_LIST, () => { jsonOutput.value )); }); + + it('Retrieve conversations for the group specified by groupName in the tenant (verbose)', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/00000000-0000-0000-0000-000000000000/conversations`) { + return jsonOutput; + } + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + verbose: true, groupName: "Finance" + } + }); + assert(loggerLogSpy.calledWith( + jsonOutput.value + )); + }); + it('correctly handles error when listing conversations', async () => { sinon.stub(request, 'get').rejects(new Error('An error has occurred')); diff --git a/src/m365/entra/commands/m365group/m365group-conversation-list.ts b/src/m365/entra/commands/m365group/m365group-conversation-list.ts index 813dd35fe37..2db25b06e01 100644 --- a/src/m365/entra/commands/m365group/m365group-conversation-list.ts +++ b/src/m365/entra/commands/m365group/m365group-conversation-list.ts @@ -12,7 +12,8 @@ interface CommandArgs { } interface Options extends GlobalOptions { - groupId: string; + groupId?: string; + groupName?: string; } class EntraM365GroupConversationListCommand extends GraphCommand { @@ -33,12 +34,17 @@ class EntraM365GroupConversationListCommand extends GraphCommand { this.#initOptions(); this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); } #initOptions(): void { this.options.unshift( { - option: '-i, --groupId ' + option: '-i, --groupId [groupId]' + }, + { + option: '-n, --groupName [groupName]' } ); } @@ -46,7 +52,7 @@ class EntraM365GroupConversationListCommand extends GraphCommand { #initValidators(): void { this.validators.push( async (args: CommandArgs) => { - if (!validation.isValidGuid(args.options.groupId as string)) { + if (args.options.groupId && !validation.isValidGuid(args.options.groupId as string)) { return `${args.options.groupId} is not a valid GUID`; } @@ -55,15 +61,33 @@ class EntraM365GroupConversationListCommand extends GraphCommand { ); } + #initOptionSets(): void { + this.optionSets.push({ options: ['groupId', 'groupName'] }); + } + + #initTypes(): void { + this.types.string.push('groupId', 'groupName'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (this.verbose) { + await logger.logToStderr(`Retrieving conversations for Microsoft 365 Group: ${args.options.groupId || args.options.groupName}...`); + } + try { - const isUnifiedGroup = await entraGroup.isUnifiedGroup(args.options.groupId); + let groupId = args.options.groupId; + + if (args.options.groupName) { + groupId = await entraGroup.getGroupIdByDisplayName(args.options.groupName); + } + + const isUnifiedGroup = await entraGroup.isUnifiedGroup(groupId!); if (!isUnifiedGroup) { - throw Error(`Specified group with id '${args.options.groupId}' is not a Microsoft 365 group.`); + throw Error(`Specified group with id '${groupId}' is not a Microsoft 365 group.`); } - const conversations = await odata.getAllItems(`${this.resource}/v1.0/groups/${args.options.groupId}/conversations`); + const conversations = await odata.getAllItems(`${this.resource}/v1.0/groups/${groupId}/conversations`); await logger.log(conversations); } catch (err: any) { diff --git a/src/m365/entra/commands/m365group/m365group-get.spec.ts b/src/m365/entra/commands/m365group/m365group-get.spec.ts index 962e5824ade..5a322a5c5a5 100644 --- a/src/m365/entra/commands/m365group/m365group-get.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-get.spec.ts @@ -65,7 +65,7 @@ describe(commands.M365GROUP_GET, () => { assert.notStrictEqual(command.description, null); }); - it('retrieves information about the specified Microsoft 365 Group', async () => { + it('retrieves information about the Microsoft 365 Group specified by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844`) { return { @@ -126,6 +126,71 @@ describe(commands.M365GROUP_GET, () => { })); }); + it('retrieves information about the Microsoft 365 Group specified by displayName', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq 'Finance'`) { + return { + "value": [ + { + "id": "1caf7dcd-7e83-4c3a-94f7-932a1299c844", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2017-11-29T03:27:05Z", + "description": "This is the Contoso Finance Group. Please come here and check out the latest news, posts, files, and more.", + "displayName": "Finance", + "groupTypes": [ + "Unified" + ], + "mail": "finance@contoso.onmicrosoft.com", + "mailEnabled": true, + "mailNickname": "finance", + "onPremisesLastSyncDateTime": null, + "onPremisesProvisioningErrors": [], + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": null, + "proxyAddresses": [ + "SMTP:finance@contoso.onmicrosoft.com" + ], + "renewedDateTime": "2017-11-29T03:27:05Z", + "securityEnabled": false, + "visibility": "Public" + } + ] + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { displayName: 'Finance' } }); + assert(loggerLogSpy.calledWith({ + "id": "1caf7dcd-7e83-4c3a-94f7-932a1299c844", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2017-11-29T03:27:05Z", + "description": "This is the Contoso Finance Group. Please come here and check out the latest news, posts, files, and more.", + "displayName": "Finance", + "groupTypes": [ + "Unified" + ], + "mail": "finance@contoso.onmicrosoft.com", + "mailEnabled": true, + "mailNickname": "finance", + "onPremisesLastSyncDateTime": null, + "onPremisesProvisioningErrors": [], + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": null, + "proxyAddresses": [ + "SMTP:finance@contoso.onmicrosoft.com" + ], + "renewedDateTime": "2017-11-29T03:27:05Z", + "securityEnabled": false, + "visibility": "Public" + })); + }); + it('retrieves information about the specified Microsoft 365 Group (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844`) { @@ -414,12 +479,35 @@ describe(commands.M365GROUP_GET, () => { }); it('shows error when the group is not a unified group', async () => { + sinon.stub(entraGroup, 'getGroupById').resolves({ + "id": "3f04e370-cbc6-4091-80fe-1d038be2ad06", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2017-11-29T03:27:05Z", + "description": "This is the Contoso Finance Group. Please come here and check out the latest news, posts, files, and more.", + "displayName": "Finance", + "groupTypes": [], + "mail": "finance@contoso.onmicrosoft.com", + "mailEnabled": true, + "mailNickname": "finance", + "onPremisesLastSyncDateTime": null, + "onPremisesProvisioningErrors": [], + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": null, + "proxyAddresses": [ + "SMTP:finance@contoso.onmicrosoft.com" + ], + "renewedDateTime": "2017-11-29T03:27:05Z", + "securityEnabled": false, + "visibility": "Public" + }); const groupId = '3f04e370-cbc6-4091-80fe-1d038be2ad06'; sinonUtil.restore(entraGroup.isUnifiedGroup); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(false); - await assert.rejects(command.action(logger, { options: { id: groupId } } as any), + await assert.rejects(command.action(logger, { options: { id: groupId } }), new CommandError(`Specified group with id '${groupId}' is not a Microsoft 365 group.`)); }); }); diff --git a/src/m365/entra/commands/m365group/m365group-get.ts b/src/m365/entra/commands/m365group/m365group-get.ts index 6b4dc99bfe6..36d848673b0 100644 --- a/src/m365/entra/commands/m365group/m365group-get.ts +++ b/src/m365/entra/commands/m365group/m365group-get.ts @@ -12,7 +12,8 @@ interface CommandArgs { } interface Options extends GlobalOptions { - id: string; + id?: string; + displayName?: string; includeSiteUrl: boolean; } @@ -30,12 +31,17 @@ class EntraM365GroupGetCommand extends GraphCommand { this.#initOptions(); this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); } #initOptions(): void { this.options.unshift( { - option: '-i, --id ' + option: '-i, --id [id]' + }, + { + option: '-n, --displayName [displayName]' }, { option: '--includeSiteUrl' @@ -46,7 +52,7 @@ class EntraM365GroupGetCommand extends GraphCommand { #initValidators(): void { this.validators.push( async (args: CommandArgs) => { - if (!validation.isValidGuid(args.options.id)) { + if (args.options.id && !validation.isValidGuid(args.options.id)) { return `${args.options.id} is not a valid GUID`; } @@ -55,18 +61,31 @@ class EntraM365GroupGetCommand extends GraphCommand { ); } + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'displayName'] }); + } + + #initTypes(): void { + this.types.string.push('id', 'displayName'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { let group: GroupExtended; try { - const isUnifiedGroup = await entraGroup.isUnifiedGroup(args.options.id); + if (args.options.id) { + group = await entraGroup.getGroupById(args.options.id); + } + else { + group = await entraGroup.getGroupByDisplayName(args.options.displayName!); + } + + const isUnifiedGroup = await entraGroup.isUnifiedGroup(group.id!); if (!isUnifiedGroup) { - throw Error(`Specified group with id '${args.options.id}' is not a Microsoft 365 group.`); + throw Error(`Specified group with id '${group.id}' is not a Microsoft 365 group.`); } - group = await entraGroup.getGroupById(args.options.id); - if (args.options.includeSiteUrl) { const requestOptions: CliRequestOptions = { url: `${this.resource}/v1.0/groups/${group.id}/drive?$select=webUrl`, @@ -77,7 +96,7 @@ class EntraM365GroupGetCommand extends GraphCommand { }; const res = await request.get<{ webUrl: string }>(requestOptions); - group.siteUrl = res.webUrl ? res.webUrl.substr(0, res.webUrl.lastIndexOf('/')) : ''; + group.siteUrl = res.webUrl ? res.webUrl.substring(0, res.webUrl.lastIndexOf('/')) : ''; } await logger.log(group); diff --git a/src/m365/entra/commands/m365group/m365group-remove.spec.ts b/src/m365/entra/commands/m365group/m365group-remove.spec.ts index b2f5cdba427..2b4c08e26de 100644 --- a/src/m365/entra/commands/m365group/m365group-remove.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-remove.spec.ts @@ -102,6 +102,7 @@ describe(commands.M365GROUP_REMOVE, () => { sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(true); + sinon.stub(entraGroup, 'getGroupIdByDisplayName').resolves(groupId); auth.connection.active = true; auth.connection.spoUrl = 'https://contoso.sharepoint.com'; commandInfo = cli.getCommandInfo(command); @@ -182,7 +183,7 @@ describe(commands.M365GROUP_REMOVE, () => { assert(getGroupSpy.notCalled); }); - it('deletes the group site for the sepcified group id when prompt confirmed', async () => { + it('deletes the group site for the group specified by id when prompt confirmed', async () => { defaultGetStub(); const deletedGroupSpy: sinon.SinonStub = defaultPostStub(); @@ -194,6 +195,18 @@ describe(commands.M365GROUP_REMOVE, () => { assert(loggerLogToStderrSpy.calledWith(`Deleting the group site: 'https://contoso.sharepoint.com/teams/sales'...`)); }); + it('deletes the group site for the group specified by displayName when prompt confirmed', async () => { + defaultGetStub(); + const deletedGroupSpy: sinon.SinonStub = defaultPostStub(); + + sinonUtil.restore(cli.promptForConfirmation); + sinon.stub(cli, 'promptForConfirmation').resolves(true); + + await command.action(logger, { options: { displayName: 'Finance', verbose: true } }); + assert(deletedGroupSpy.calledOnce); + assert(loggerLogToStderrSpy.calledWith(`Deleting the group site: 'https://contoso.sharepoint.com/teams/sales'...`)); + }); + it('deletes the group without moving it to the Recycle Bin', async () => { defaultGetStub(); defaultPostStub(); @@ -303,7 +316,7 @@ describe(commands.M365GROUP_REMOVE, () => { sinonUtil.restore(entraGroup.isUnifiedGroup); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(false); - await assert.rejects(command.action(logger, { options: { id: groupId, force: true } } as any), + await assert.rejects(command.action(logger, { options: { id: groupId, force: true } }), new CommandError(`Specified group with id '${groupId}' is not a Microsoft 365 group.`)); }); }); diff --git a/src/m365/entra/commands/m365group/m365group-remove.ts b/src/m365/entra/commands/m365group/m365group-remove.ts index c6384714180..6d0987617d8 100644 --- a/src/m365/entra/commands/m365group/m365group-remove.ts +++ b/src/m365/entra/commands/m365group/m365group-remove.ts @@ -16,7 +16,8 @@ interface CommandArgs { } interface Options extends GlobalOptions { - id: string; + id?: string; + displayName?: string; force?: boolean; skipRecycleBin: boolean; } @@ -39,6 +40,8 @@ class EntraM365GroupRemoveCommand extends GraphCommand { this.#initTelemetry(); this.#initOptions(); this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); } #initTelemetry(): void { @@ -53,7 +56,10 @@ class EntraM365GroupRemoveCommand extends GraphCommand { #initOptions(): void { this.options.unshift( { - option: '-i, --id ' + option: '-i, --id [id]' + }, + { + option: '-n, --displayName [displayName]' }, { option: '-f, --force' @@ -67,7 +73,7 @@ class EntraM365GroupRemoveCommand extends GraphCommand { #initValidators(): void { this.validators.push( async (args: CommandArgs) => { - if (!validation.isValidGuid(args.options.id)) { + if (args.options.id && !validation.isValidGuid(args.options.id)) { return `${args.options.id} is not a valid GUID`; } @@ -76,27 +82,40 @@ class EntraM365GroupRemoveCommand extends GraphCommand { ); } + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'displayName'] }); + } + + #initTypes(): void { + this.types.string.push('id', 'displayName'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { const removeGroup = async (): Promise => { if (this.verbose) { - await logger.logToStderr(`Removing Microsoft 365 Group: ${args.options.id}...`); + await logger.logToStderr(`Removing Microsoft 365 Group: ${args.options.id || args.options.displayName}...`); } try { - const isUnifiedGroup = await entraGroup.isUnifiedGroup(args.options.id); + let groupId = args.options.id; + + if (args.options.displayName) { + groupId = await entraGroup.getGroupIdByDisplayName(args.options.displayName); + } + const isUnifiedGroup = await entraGroup.isUnifiedGroup(groupId!); if (!isUnifiedGroup) { - throw Error(`Specified group with id '${args.options.id}' is not a Microsoft 365 group.`); + throw Error(`Specified group with id '${groupId}' is not a Microsoft 365 group.`); } - const siteUrl = await this.getM365GroupSiteUrl(logger, args.options.id); + const siteUrl = await this.getM365GroupSiteUrl(logger, groupId!); const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug); // Delete the Microsoft 365 group site. This operation will also delete the group. await this.deleteM365GroupSite(logger, siteUrl, spoAdminUrl); if (args.options.skipRecycleBin) { - await this.deleteM365GroupFromRecycleBin(logger, args.options.id); + await this.deleteM365GroupFromRecycleBin(logger, groupId!); await this.deleteSiteFromRecycleBin(logger, siteUrl, spoAdminUrl); } } @@ -109,7 +128,7 @@ class EntraM365GroupRemoveCommand extends GraphCommand { await removeGroup(); } else { - const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove the group ${args.options.id}?` }); + const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove the group ${args.options.id || args.options.displayName}?` }); if (result) { await removeGroup(); diff --git a/src/m365/entra/commands/m365group/m365group-renew.spec.ts b/src/m365/entra/commands/m365group/m365group-renew.spec.ts index 1eb96a99173..2d3bf71784c 100644 --- a/src/m365/entra/commands/m365group/m365group-renew.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-renew.spec.ts @@ -21,12 +21,15 @@ describe(commands.M365GROUP_RENEW, () => { let commandInfo: CommandInfo; let loggerLogToStderrSpy: sinon.SinonSpy; + const groupId = '28beab62-7540-4db1-a23f-29a6018a3848'; + before(() => { sinon.stub(auth, 'restoreAuth').resolves(); sinon.stub(telemetry, 'trackEvent').returns(); sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(true); + sinon.stub(entraGroup, 'getGroupIdByDisplayName').resolves(groupId); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); }); @@ -68,7 +71,20 @@ describe(commands.M365GROUP_RENEW, () => { assert.notStrictEqual(command.description, null); }); - it('renews expiration the specified group', async () => { + it('renews expiration of a group specified by id', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === 'https://graph.microsoft.com/v1.0/groups/28beab62-7540-4db1-a23f-29a6018a3848/renew/') { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { id: groupId, verbose: true } }); + assert(loggerLogSpy.notCalled); + }); + + it('renews expiration of a group specified by displayName', async () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === 'https://graph.microsoft.com/v1.0/groups/28beab62-7540-4db1-a23f-29a6018a3848/renew/') { return; @@ -77,7 +93,7 @@ describe(commands.M365GROUP_RENEW, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { id: '28beab62-7540-4db1-a23f-29a6018a3848' } }); + await command.action(logger, { options: { displayName: 'Finance', verbose: true } }); assert(loggerLogSpy.notCalled); }); @@ -90,14 +106,14 @@ describe(commands.M365GROUP_RENEW, () => { throw 'Invalid request'; }); - await command.action(logger, { options: { debug: true, id: '28beab62-7540-4db1-a23f-29a6018a3848' } }); + await command.action(logger, { options: { debug: true, id: groupId } }); assert(loggerLogToStderrSpy.called); }); it('correctly handles error when group is not found', async () => { sinon.stub(request, 'post').rejects({ error: { 'odata.error': { message: { value: 'The remote server returned an error: (404) Not Found.' } } } }); - await assert.rejects(command.action(logger, { options: { id: '28beab62-7540-4db1-a23f-29a6018a3848' } } as any), + await assert.rejects(command.action(logger, { options: { id: groupId } }), new CommandError('The remote server returned an error: (404) Not Found.')); }); @@ -128,7 +144,7 @@ describe(commands.M365GROUP_RENEW, () => { sinonUtil.restore(entraGroup.isUnifiedGroup); sinon.stub(entraGroup, 'isUnifiedGroup').resolves(false); - await assert.rejects(command.action(logger, { options: { id: groupId } } as any), + await assert.rejects(command.action(logger, { options: { id: groupId } }), new CommandError(`Specified group with id '${groupId}' is not a Microsoft 365 group.`)); }); }); diff --git a/src/m365/entra/commands/m365group/m365group-renew.ts b/src/m365/entra/commands/m365group/m365group-renew.ts index aa251b235ca..36f47f6df0a 100644 --- a/src/m365/entra/commands/m365group/m365group-renew.ts +++ b/src/m365/entra/commands/m365group/m365group-renew.ts @@ -11,7 +11,8 @@ interface CommandArgs { } interface Options extends GlobalOptions { - id: string; + id?: string; + displayName?: string; } class EntraM365GroupRenewCommand extends GraphCommand { @@ -28,12 +29,17 @@ class EntraM365GroupRenewCommand extends GraphCommand { this.#initOptions(); this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); } #initOptions(): void { this.options.unshift( { - option: '-i, --id ' + option: '-i, --id [id]' + }, + { + option: '-n, --displayName [displayName]' } ); } @@ -41,7 +47,7 @@ class EntraM365GroupRenewCommand extends GraphCommand { #initValidators(): void { this.validators.push( async (args: CommandArgs) => { - if (!validation.isValidGuid(args.options.id)) { + if (args.options.id && !validation.isValidGuid(args.options.id)) { return `${args.options.id} is not a valid GUID`; } @@ -50,20 +56,33 @@ class EntraM365GroupRenewCommand extends GraphCommand { ); } + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'displayName'] }); + } + + #initTypes(): void { + this.types.string.push('id', 'displayName'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { if (this.verbose) { - await logger.logToStderr(`Renewing Microsoft 365 group's expiration: ${args.options.id}...`); + await logger.logToStderr(`Renewing Microsoft 365 group's expiration: ${args.options.id || args.options.displayName}...`); } try { - const isUnifiedGroup = await entraGroup.isUnifiedGroup(args.options.id); + let groupId = args.options.id; + + if (args.options.displayName) { + groupId = await entraGroup.getGroupIdByDisplayName(args.options.displayName); + } + const isUnifiedGroup = await entraGroup.isUnifiedGroup(groupId!); if (!isUnifiedGroup) { - throw Error(`Specified group with id '${args.options.id}' is not a Microsoft 365 group.`); + throw Error(`Specified group with id '${groupId}' is not a Microsoft 365 group.`); } const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/groups/${args.options.id}/renew/`, + url: `${this.resource}/v1.0/groups/${groupId}/renew/`, headers: { 'accept': 'application/json;odata.metadata=none' } diff --git a/src/m365/entra/commands/m365group/m365group-teamify.spec.ts b/src/m365/entra/commands/m365group/m365group-teamify.spec.ts index 727be4534ed..473c66a68c2 100644 --- a/src/m365/entra/commands/m365group/m365group-teamify.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-teamify.spec.ts @@ -68,7 +68,7 @@ describe(commands.M365GROUP_TEAMIFY, () => { assert.notStrictEqual(command.description, null); }); - it('fails validation if both id and mailNickname options are not passed', async () => { + it('fails validation if id, displayName and mailNickname options are not passed', async () => { sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { if (settingName === settingsNames.prompt) { return false; @@ -84,7 +84,7 @@ describe(commands.M365GROUP_TEAMIFY, () => { assert.notStrictEqual(actual, true); }); - it('fails validation if both id and mailNickname options are passed', async () => { + it('fails validation if id, displayName and mailNickname options are passed', async () => { sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { if (settingName === settingsNames.prompt) { return false; @@ -124,7 +124,7 @@ describe(commands.M365GROUP_TEAMIFY, () => { debug: true, mailNickname: 'GroupName' } - }), new CommandError(`The specified Microsoft 365 Group does not exist`)); + }), new CommandError(`The specified group 'GroupName' does not exist.`)); }); it('fails when multiple groups with same name exists', async () => { @@ -236,12 +236,12 @@ describe(commands.M365GROUP_TEAMIFY, () => { debug: true, mailNickname: 'GroupName' } - }), new CommandError("Multiple Microsoft 365 Groups with name 'GroupName' found. Found: 00000000-0000-0000-0000-000000000000.")); + }), new CommandError("Multiple groups with mail nickname 'GroupName' found. Found: 00000000-0000-0000-0000-000000000000.")); }); it('handles selecting single result when multiple groups with the specified name found and cli is set to prompt', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === "https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq 'groupname'") { + if (opts.url === "https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq 'groupname'&$select=id") { return { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups", "value": [ @@ -593,6 +593,114 @@ describe(commands.M365GROUP_TEAMIFY, () => { assert.strictEqual(requestStub.lastCall.args[0].url, 'https://graph.microsoft.com/v1.0/groups/00000000-0000-0000-0000-000000000000/team'); }); + it('Teamify M365 group by displayName', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/v1.0/groups?$filter=displayName eq `) > -1) { + return { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups", + "value": [ + { + "@odata.id": "https://graph.microsoft.com/v2/00000000-0000-0000-0000-000000000000/directoryObjects/00000000-0000-0000-0000-000000000000/Microsoft.DirectoryServices.Group", + "id": "00000000-0000-0000-0000-000000000000", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2021-09-05T09:01:19Z", + "creationOptions": [], + "description": "GroupName", + "displayName": "GroupName", + "expirationDateTime": null, + "groupTypes": [ + "Unified" + ], + "isAssignableToRole": null, + "mail": "groupname@contoso.onmicrosoft.com", + "mailEnabled": true, + "mailNickname": "groupname", + "membershipRule": null, + "membershipRuleProcessingState": null, + "onPremisesDomainName": null, + "onPremisesLastSyncDateTime": null, + "onPremisesNetBiosName": null, + "onPremisesSamAccountName": null, + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": null, + "preferredLanguage": null, + "proxyAddresses": [ + "SPO:SPO_00000000-0000-0000-0000-000000000000@SPO_00000000-0000-0000-0000-000000000000", + "SMTP:groupname@contoso.onmicrosoft.com" + ], + "renewedDateTime": "2021-09-05T09:01:19Z", + "resourceBehaviorOptions": [], + "resourceProvisioningOptions": [ + "Team" + ], + "securityEnabled": false, + "securityIdentifier": "S-1-12-1-71288816-1279290235-2033184675-371261341", + "theme": null, + "visibility": "Public", + "onPremisesProvisioningErrors": [] + } + ] + }; + } + + throw 'Invalid request'; + }); + + const requestStub: sinon.SinonStub = sinon.stub(request, 'put').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups/00000000-0000-0000-0000-000000000000/team`) { + return { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams/$entity", + "id": "00000000-0000-0000-0000-000000000000", + "createdDateTime": null, + "displayName": "Group Team", + "description": "Group Team description", + "internalId": "19:ASjdflg-xKFnjueOwbm3es6HF2zx3Ki57MyfDFrjeg01@thread.tacv2", + "classification": null, + "specialization": null, + "mailNickname": "groupname", + "visibility": "public", + "webUrl": "https://teams.microsoft.com/l/team/19:ASjdflg-xKFnjueOwbm3es6HF2zx3Ki57MyfDFrjeg01%40thread.tacv2/conversations?groupId=00000000-0000-0000-0000-000000000000&tenantId=3a7a651b-2620-433b-a1a3-42de27ae94e8", + "isArchived": null, + "isMembershipLimitedToOwners": false, + "discoverySettings": null, + "memberSettings": { + "allowCreateUpdateChannels": true, + "allowCreatePrivateChannels": true, + "allowDeleteChannels": true, + "allowAddRemoveApps": true, + "allowCreateUpdateRemoveTabs": true, + "allowCreateUpdateRemoveConnectors": true + }, + "guestSettings": { + "allowCreateUpdateChannels": false, + "allowDeleteChannels": false + }, + "messagingSettings": { + "allowUserEditMessages": true, + "allowUserDeleteMessages": true, + "allowOwnerDeleteMessages": true, + "allowTeamMentions": true, + "allowChannelMentions": true + }, + "funSettings": { + "allowGiphy": true, + "giphyContentRating": "moderate", + "allowStickersAndMemes": true, + "allowCustomMemes": true + } + }; + } + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { displayName: 'GroupName' } + }); + assert.strictEqual(requestStub.lastCall.args[0].url, 'https://graph.microsoft.com/v1.0/groups/00000000-0000-0000-0000-000000000000/team'); + }); + it('should handle Microsoft graph error response', async () => { sinon.stub(request, 'put').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/8231f9f2-701f-4c6e-93ce-ecb563e3c1ee/team`) { diff --git a/src/m365/entra/commands/m365group/m365group-teamify.ts b/src/m365/entra/commands/m365group/m365group-teamify.ts index de91436791a..39831cc5db3 100644 --- a/src/m365/entra/commands/m365group/m365group-teamify.ts +++ b/src/m365/entra/commands/m365group/m365group-teamify.ts @@ -1,4 +1,3 @@ -import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -14,6 +13,7 @@ interface CommandArgs { interface Options extends GlobalOptions { id?: string; + displayName?: string; mailNickname?: string; } @@ -33,13 +33,15 @@ class EntraM365GroupTeamifyCommand extends GraphCommand { this.#initOptions(); this.#initValidators(); this.#initOptionSets(); + this.#initTypes(); } #initTelemetry(): void { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { id: typeof args.options.id !== 'undefined', - mailNickname: typeof args.options.mailNickname !== 'undefined' + mailNickname: typeof args.options.mailNickname !== 'undefined', + displayName: typeof args.options.displayName !== 'undefined' }); }); } @@ -49,6 +51,9 @@ class EntraM365GroupTeamifyCommand extends GraphCommand { { option: '-i, --id [id]' }, + { + option: '-n, --displayName [displayName]' + }, { option: '--mailNickname [mailNickname]' } @@ -68,7 +73,11 @@ class EntraM365GroupTeamifyCommand extends GraphCommand { } #initOptionSets(): void { - this.optionSets.push({ options: ['id', 'mailNickname'] }); + this.optionSets.push({ options: ['id', 'displayName', 'mailNickname'] }); + } + + #initTypes(): void { + this.types.string.push('id', 'displayName', 'mailNickname'); } private async getGroupId(options: Options): Promise { @@ -76,28 +85,11 @@ class EntraM365GroupTeamifyCommand extends GraphCommand { return options.id; } - const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(options.mailNickname as string)}'`, - headers: { - accept: 'application/json;odata.metadata=none' - }, - responseType: 'json' - }; - - const response = await request.get<{ value: [{ id: string }] }>(requestOptions); - const groupItem: { id: string } | undefined = response.value[0]; - - if (!groupItem) { - throw `The specified Microsoft 365 Group does not exist`; - } - - if (response.value.length > 1) { - const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', response.value); - const result = await cli.handleMultipleResultsFound<{ id: string }>(`Multiple Microsoft 365 Groups with name '${options.mailNickname}' found.`, resultAsKeyValuePair); - return result.id; + if (options.displayName) { + return await entraGroup.getGroupIdByDisplayName(options.displayName); } - return groupItem.id; + return await entraGroup.getGroupIdByMailNickname(options.mailNickname!); } public async commandAction(logger: Logger, args: CommandArgs): Promise { diff --git a/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts b/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts index fe6b05b7574..727208c0bce 100644 --- a/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-user-remove.spec.ts @@ -31,6 +31,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { sinon.stub(telemetry, 'trackEvent').returns(); sinon.stub(pid, 'getProcessName').returns(''); sinon.stub(session, 'getId').returns(''); + sinon.stub(entraGroup, 'getGroupIdByDisplayName').resolves('00000000-0000-0000-0000-000000000000'); auth.connection.active = true; commandInfo = cli.getCommandInfo(command); }); @@ -173,7 +174,7 @@ describe(commands.M365GROUP_USER_REMOVE, () => { assert(postSpy.notCalled); }); - it('removes the specified owner from owners and members endpoint of the specified Microsoft 365 Group with accepted prompt', async () => { + it('removes the specified owner from owners and members endpoint of the Microsoft 365 Group specified by id with accepted prompt', async () => { let memberDeleteCallIssued = false; sinon.stub(request, 'delete').callsFake(async (opts) => { diff --git a/src/m365/entra/commands/m365group/m365group-user-remove.ts b/src/m365/entra/commands/m365group/m365group-user-remove.ts index 126ab7dc847..7875886358e 100644 --- a/src/m365/entra/commands/m365group/m365group-user-remove.ts +++ b/src/m365/entra/commands/m365group/m365group-user-remove.ts @@ -129,6 +129,10 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { } public async commandAction(logger: Logger, args: CommandArgs): Promise { + if (args.options.userName) { + await this.warn(logger, `Option 'userName' is deprecated. Please use 'ids' or 'userNames' instead.`); + } + const removeUser = async (): Promise => { try { const groupId: string = await this.getGroupId(logger, args.options.groupId, args.options.teamId, args.options.groupName, args.options.teamName); diff --git a/src/utils/entraGroup.spec.ts b/src/utils/entraGroup.spec.ts index 97d60fc8f2f..2ed095c8dbe 100644 --- a/src/utils/entraGroup.spec.ts +++ b/src/utils/entraGroup.spec.ts @@ -10,6 +10,7 @@ import { settingsNames } from '../settingsNames.js'; const validGroupName = 'Group name'; const validGroupId = '00000000-0000-0000-0000-000000000000'; +const validGroupMailNickname = 'GroupName'; const singleGroupResponse = { id: validGroupId, @@ -185,6 +186,80 @@ describe('utils/entraGroup', () => { assert.deepStrictEqual(actual, validGroupId); }); + it('correctly get single group id by name using getGroupIdByMailNickname', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(validGroupMailNickname)}'&$select=id`) { + return { + value: [ + { id: singleGroupResponse.id } + ] + }; + } + + return 'Invalid Request'; + }); + + const actual = await entraGroup.getGroupIdByMailNickname(validGroupMailNickname); + assert.deepStrictEqual(actual, singleGroupResponse.id); + }); + + it('throws error message when no group was found using getGroupIdByMailNickname', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(validGroupMailNickname)}'&$select=id`) { + return { value: [] }; + } + + return 'Invalid Request'; + }); + + await assert.rejects(entraGroup.getGroupIdByMailNickname(validGroupMailNickname), Error(`The specified group '${validGroupMailNickname}' does not exist.`)); + }); + + it('throws error message when multiple groups were found using getGroupIdByMailNickname', async () => { + sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { + if (settingName === settingsNames.prompt) { + return false; + } + + return defaultValue; + }); + + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(validGroupMailNickname)}'&$select=id`) { + return { + value: [ + { id: validGroupId }, + { id: validGroupId } + ] + }; + } + + return 'Invalid Request'; + }); + + await assert.rejects(entraGroup.getGroupIdByMailNickname(validGroupMailNickname), Error(`Multiple groups with mail nickname 'GroupName' found. Found: 00000000-0000-0000-0000-000000000000.`)); + }); + + it('handles selecting single result when multiple groups with the specified name found using getGroupIdByMailNickname and cli is set to prompt', async () => { + sinon.stub(request, 'get').callsFake(async opts => { + if (opts.url === `https://graph.microsoft.com/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(validGroupMailNickname)}'&$select=id`) { + return { + value: [ + { id: validGroupId }, + { id: validGroupId } + ] + }; + } + + return 'Invalid Request'; + }); + + sinon.stub(cli, 'handleMultipleResultsFound').resolves({ id: validGroupId }); + + const actual = await entraGroup.getGroupIdByMailNickname(validGroupMailNickname); + assert.deepStrictEqual(actual, validGroupId); + }); + it('updates a group to public successfully', async () => { const patchStub = sinon.stub(request, 'patch').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/${validGroupId}`) { diff --git a/src/utils/entraGroup.ts b/src/utils/entraGroup.ts index fb516e48f39..92e0a372a79 100644 --- a/src/utils/entraGroup.ts +++ b/src/utils/entraGroup.ts @@ -75,6 +75,28 @@ export const entraGroup = { return groups[0].id!; }, + /** + * Get id of a group by its mail nickname. + * @param mailNickname Group mail nickname. + * @throws Error when group was not found. + * @throws Error when multiple groups with the same name were found. + */ + async getGroupIdByMailNickname(mailNickname: string): Promise { + const groups = await odata.getAllItems(`${graphResource}/v1.0/groups?$filter=mailNickname eq '${formatting.encodeQueryParameter(mailNickname)}'&$select=id`); + + if (!groups.length) { + throw Error(`The specified group '${mailNickname}' does not exist.`); + } + + if (groups.length > 1) { + const resultAsKeyValuePair = formatting.convertArrayToHashTable('id', groups); + const result = await cli.handleMultipleResultsFound(`Multiple groups with mail nickname '${mailNickname}' found.`, resultAsKeyValuePair); + return result.id!; + } + + return groups[0].id!; + }, + async setGroup(id: string, isPrivate: boolean, logger?: Logger, verbose?: boolean): Promise { if (verbose && logger) { await logger.logToStderr(`Updating Microsoft 365 Group ${id}...`); From a51d625d8f91ffb03565bcf1783739d6f85595bf Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Thu, 29 Aug 2024 15:51:29 +0200 Subject: [PATCH 24/42] Adds command 'viva engage community set'. Closes #6279 --- .../cmd/viva/engage/engage-community-set.mdx | 61 ++++++ docs/src/config/sidebars.ts | 5 + src/m365/viva/commands.ts | 1 + .../engage/engage-community-set.spec.ts | 177 ++++++++++++++++++ .../commands/engage/engage-community-set.ts | 152 +++++++++++++++ src/utils/vivaEngage.ts | 2 +- 6 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 docs/docs/cmd/viva/engage/engage-community-set.mdx create mode 100644 src/m365/viva/commands/engage/engage-community-set.spec.ts create mode 100644 src/m365/viva/commands/engage/engage-community-set.ts diff --git a/docs/docs/cmd/viva/engage/engage-community-set.mdx b/docs/docs/cmd/viva/engage/engage-community-set.mdx new file mode 100644 index 00000000000..57bf76a5fff --- /dev/null +++ b/docs/docs/cmd/viva/engage/engage-community-set.mdx @@ -0,0 +1,61 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# viva engage community set + +Updates an existing Viva Engage community + +## Usage + +```sh +m365 viva engage community set [options] +``` + +## Options + +```md definition-list +`-i, --id [id]` +: The id of the community. Specify either `id`, `displayName` or `entraGroupId`, but not multiple. + +`-d, --displayName [displayName]` +: The name of the community. Specify either `id`, `displayName` or `entraGroupId`, but not multiple. + +`--entraGroupId [entraGroupId]` +: The id of the Microsoft Entra group associated with the community. Specify either `id`, `displayName` or `entraGroupId`, but not multiple. + +`--newDisplayName [newDisplayName]` +: New name for the community. The maximum length is 255 characters. + +`--description [description]` +: The description of the community. The maximum length is 1024 characters. + +`--privacy [privacy]` +: Defines the privacy level of the community. The possible values are: `public`, and `private`. +``` + + + +## Examples + +Update info about the community specified by id + +```sh +m365 viva engage community set --id eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9 --newDisplayName 'Developers' --description 'Community for all devs' --privacy public +``` + +Update info about the community specified by name + +```sh +m365 viva engage community set --displayName 'Developrs' --newDisplayName 'Developers' +``` + +Update info about the community specified by Entra group id + +```sh +m365 viva engage community set --entraGroupId '0bed8b86-5026-4a93-ac7d-56750cc099f1' --newDisplayName 'Developers' +``` + +## Response + +The command won't return a response on success. \ No newline at end of file diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 2ac88fa5dec..4dbf41867ee 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -4527,6 +4527,11 @@ const sidebars: SidebarsConfig = { label: 'engage community list', id: 'cmd/viva/engage/engage-community-list' }, + { + type: 'doc', + label: 'engage community set', + id: 'cmd/viva/engage/engage-community-set' + }, { type: 'doc', label: 'engage community user list', diff --git a/src/m365/viva/commands.ts b/src/m365/viva/commands.ts index 4c431736913..3061b0d2b6d 100644 --- a/src/m365/viva/commands.ts +++ b/src/m365/viva/commands.ts @@ -5,6 +5,7 @@ export default { ENGAGE_COMMUNITY_ADD: `${prefix} engage community add`, ENGAGE_COMMUNITY_GET: `${prefix} engage community get`, ENGAGE_COMMUNITY_LIST: `${prefix} engage community list`, + ENGAGE_COMMUNITY_SET: `${prefix} engage community set`, ENGAGE_COMMUNITY_USER_LIST: `${prefix} engage community user list`, ENGAGE_GROUP_LIST: `${prefix} engage group list`, ENGAGE_GROUP_USER_ADD: `${prefix} engage group user add`, diff --git a/src/m365/viva/commands/engage/engage-community-set.spec.ts b/src/m365/viva/commands/engage/engage-community-set.spec.ts new file mode 100644 index 00000000000..7f73364a19d --- /dev/null +++ b/src/m365/viva/commands/engage/engage-community-set.spec.ts @@ -0,0 +1,177 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './engage-community-set.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { vivaEngage } from '../../../../utils/vivaEngage.js'; +import { cli } from '../../../../cli/cli.js'; + +describe(commands.ENGAGE_COMMUNITY_SET, () => { + const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9'; + const displayName = 'Software Engineers'; + const entraGroupId = '0bed8b86-5026-4a93-ac7d-56750cc099f1'; + let log: string[]; + let logger: Logger; + let commandInfo: CommandInfo; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + commandInfo = cli.getCommandInfo(command); + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + }); + + afterEach(() => { + sinonUtil.restore([ + request.patch + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.ENGAGE_COMMUNITY_SET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('passes validation when id is specified', async () => { + const actual = await command.validate({ options: { id: communityId, description: 'Community for all devs' } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation when displayName is specified', async () => { + const actual = await command.validate({ options: { displayName: 'Software Engineers', description: 'Community for all devs' } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation when entraGroupId is specified', async () => { + const actual = await command.validate({ options: { entraGroupId: '0bed8b86-5026-4a93-ac7d-56750cc099f1', description: 'Community for all devs' } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('fails validation when newDisplayName, description or privacy is not specified', async () => { + const actual = await command.validate({ options: { displayName: 'Software Engineers' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if newDisplayName is more than 255 characters', async () => { + const actual = await command.validate({ + options: { + id: communityId, + newDisplayName: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries." + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if description is more than 1024 characters', async () => { + const actual = await command.validate({ + options: { + displayName: 'Software engineers', + description: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text.All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet.` + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation when invalid privacy option is provided', async () => { + const actual = await command.validate({ + options: { + displayName: 'Software engineers', + privacy: 'invalid' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation when entraGroupId is not a valid GUID', async () => { + const actual = await command.validate({ options: { entraGroupId: 'foo', description: 'Community for all devs' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('updates info about a community specified by id', async () => { + const patchRequestStub = sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { id: communityId, newDisplayName: 'Software Engineers', verbose: true } }); + assert(patchRequestStub.called); + }); + + it('updates info about a community specified by displayName', async () => { + sinon.stub(vivaEngage, 'getCommunityByDisplayName').resolves({ id: communityId }); + const patchRequestStub = sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { displayName: displayName, description: 'Community for all devs', privacy: 'Public', verbose: true } }); + assert(patchRequestStub.called); + }); + + it('updates info about a community specified by entraGroupId', async () => { + sinon.stub(vivaEngage, 'getCommunityByEntraGroupId').resolves({ id: communityId }); + const patchRequestStub = sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { entraGroupId: entraGroupId, description: 'Community for all devs', privacy: 'Public', verbose: true } }); + assert(patchRequestStub.called); + }); + + it('handles error when updating Viva Engage community failed', async () => { + sinon.stub(request, 'patch').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) { + throw { error: { message: 'An error has occurred' } }; + } + throw `Invalid request`; + }); + + await assert.rejects( + command.action(logger, { options: { id: communityId } } as any), + new CommandError('An error has occurred') + ); + }); +}); \ No newline at end of file diff --git a/src/m365/viva/commands/engage/engage-community-set.ts b/src/m365/viva/commands/engage/engage-community-set.ts new file mode 100644 index 00000000000..e4bc07965ae --- /dev/null +++ b/src/m365/viva/commands/engage/engage-community-set.ts @@ -0,0 +1,152 @@ +import GlobalOptions from '../../../../GlobalOptions.js'; +import { Logger } from '../../../../cli/Logger.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { validation } from '../../../../utils/validation.js'; +import { vivaEngage } from '../../../../utils/vivaEngage.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; + +interface CommandArgs { + options: Options; +} + +interface Options extends GlobalOptions { + id?: string; + displayName?: string; + entraGroupId?: string; + newDisplayName?: string; + description?: string; + privacy?: string; +} + +class VivaEngageCommunitySetCommand extends GraphCommand { + private privacyOptions: string[] = ['public', 'private']; + + public get name(): string { + return commands.ENGAGE_COMMUNITY_SET; + } + + public get description(): string { + return 'Updates an existing Viva Engage community'; + } + + constructor() { + super(); + + this.#initTelemetry(); + this.#initOptions(); + this.#initValidators(); + this.#initTypes(); + this.#initOptionSets(); + } + + #initTelemetry(): void { + this.telemetry.push((args: CommandArgs) => { + Object.assign(this.telemetryProperties, { + id: typeof args.options.id !== 'undefined', + displayName: typeof args.options.displayName !== 'undefined', + entraGroupId: typeof args.options.entraGroupId !== 'undefined', + newDisplayName: typeof args.options.newDisplayName !== 'undefined', + description: typeof args.options.description !== 'undefined', + privacy: typeof args.options.privacy !== 'undefined' + }); + }); + } + + #initOptions(): void { + this.options.unshift( + { + option: '-i, --id [id]' + }, + { + option: '-d, --displayName [displayName]' + }, + { + option: '--entraGroupId [entraGroupId]' + }, + { + option: '--newDisplayName [newDisplayName]' + }, + { + option: '--description [description]' + }, + { + option: '--privacy [privacy]', + autocomplete: this.privacyOptions + } + ); + } + + #initValidators(): void { + this.validators.push( + async (args: CommandArgs) => { + if (args.options.entraGroupId && !validation.isValidGuid(args.options.entraGroupId)) { + return `${args.options.entraGroupId} is not a valid GUID for the option 'entraGroupId'.`; + } + + if (args.options.newDisplayName && args.options.newDisplayName.length > 255) { + return `The maximum amount of characters for 'newDisplayName' is 255.`; + } + + if (args.options.description && args.options.description.length > 1024) { + return `The maximum amount of characters for 'description' is 1024.`; + } + + if (args.options.privacy && this.privacyOptions.map(x => x.toLowerCase()).indexOf(args.options.privacy.toLowerCase()) === -1) { + return `${args.options.privacy} is not a valid privacy. Allowed values are ${this.privacyOptions.join(', ')}`; + } + + if (!args.options.newDisplayName && !args.options.description && !args.options.privacy) { + return 'Specify at least newDisplayName, description, or privacy.'; + } + + return true; + } + ); + } + + #initTypes(): void { + this.types.string.push('id', 'displayName', 'entraGroupId', 'newDisplayName', 'description', 'privacy'); + } + + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'displayName', 'entraGroupId'] }); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + let communityId = args.options.id; + + if (args.options.displayName) { + communityId = (await vivaEngage.getCommunityByDisplayName(args.options.displayName, ['id'])).id!; + } + else if (args.options.entraGroupId) { + communityId = (await vivaEngage.getCommunityByEntraGroupId(args.options.entraGroupId, ['id'])).id!; + } + + if (this.verbose) { + await logger.logToStderr(`Updating Viva Engage community with ID ${communityId}...`); + } + + const requestOptions: CliRequestOptions = { + url: `${this.resource}/v1.0/employeeExperience/communities/${communityId}`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json', + data: { + description: args.options.description, + displayName: args.options.newDisplayName, + privacy: args.options.privacy + } + }; + + try { + await request.patch(requestOptions); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new VivaEngageCommunitySetCommand(); \ No newline at end of file diff --git a/src/utils/vivaEngage.ts b/src/utils/vivaEngage.ts index 40ba84ddc2d..0efcbfe39a1 100644 --- a/src/utils/vivaEngage.ts +++ b/src/utils/vivaEngage.ts @@ -70,4 +70,4 @@ export const vivaEngage = { return filteredCommunity; } -}; \ No newline at end of file +}; From 2716e3a704db56f745ae9cbc7d01ccbccd7d490a Mon Sep 17 00:00:00 2001 From: waldekmastykarz Date: Sun, 15 Sep 2024 12:35:05 +0200 Subject: [PATCH 25/42] Speeds up linting. Closes #6348 --- .github/workflows/check_pr.yml | 14 +++++++++++--- .github/workflows/release.yml | 7 +++++++ .github/workflows/release_next.yml | 8 ++++++++ package.json | 2 +- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check_pr.yml b/.github/workflows/check_pr.yml index dd0064b94b7..8c684139037 100644 --- a/.github/workflows/check_pr.yml +++ b/.github/workflows/check_pr.yml @@ -24,7 +24,7 @@ jobs: with: path: | **/node_modules - key: node_modules-${{ matrix.os }}-${{ matrix.node }}-${{ hashFiles('**/npm-shrinkwrap.json') }} + key: node_modules-${{ matrix.os }}-${{ matrix.node }}-${{ hashFiles('npm-shrinkwrap.json') }} - name: Restore dependencies if: steps.cache.outputs.cache-hit != 'true' run: npm ci @@ -87,10 +87,18 @@ jobs: with: path: | **/node_modules - key: node_modules-${{ matrix.os }}-${{ matrix.nodeBuild }}-${{ hashFiles('**/npm-shrinkwrap.json') }} + key: node_modules-${{ matrix.os }}-${{ matrix.nodeBuild }}-${{ hashFiles('npm-shrinkwrap.json') }} - name: Restore dependencies if: steps.cache.outputs.cache-hit != 'true' run: npm ci + - name: Cache .eslintcache + if: matrix.nodeRun == matrix.nodeBuild + id: eslintcache + uses: actions/cache@v4 + with: + path: | + .eslintcache + key: eslintcache-${{ matrix.os }}-${{ hashFiles('npm-shrinkwrap.json', '.eslintrc.cjs') }} - name: Test with coverage # we run coverage only on Node that was used to build if: matrix.nodeRun == matrix.nodeBuild @@ -126,7 +134,7 @@ jobs: with: path: | **/docs/node_modules - key: docs_node_modules-${{ hashFiles('**/docs/package-lock.json') }} + key: docs_node_modules-${{ hashFiles('./docs/package-lock.json') }} - name: Restore dependencies if: steps.cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 80222e72c54..c281d3912e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,6 +64,13 @@ jobs: - name: Restore dependencies if: steps.cache.outputs.cache-hit != 'true' run: npm ci + - name: Cache .eslintcache + id: eslintcache + uses: actions/cache@v4 + with: + path: | + .eslintcache + key: eslintcache-${{ matrix.os }}-${{ hashFiles('npm-shrinkwrap.json', '.eslintrc.cjs') }} - name: Test run: npm test env: diff --git a/.github/workflows/release_next.yml b/.github/workflows/release_next.yml index 5b2ae7694db..837c176899f 100644 --- a/.github/workflows/release_next.yml +++ b/.github/workflows/release_next.yml @@ -93,6 +93,14 @@ jobs: - name: Restore dependencies if: steps.cache.outputs.cache-hit != 'true' run: npm ci + - name: Cache .eslintcache + if: matrix.nodeRun == matrix.nodeBuild + id: eslintcache + uses: actions/cache@v4 + with: + path: | + .eslintcache + key: eslintcache-${{ matrix.os }}-${{ hashFiles('npm-shrinkwrap.json', '.eslintrc.cjs') }} - name: Test with coverage # we run coverage only on Node that was used to build if: matrix.nodeRun == matrix.nodeBuild diff --git a/package.json b/package.json index b5cdf8ccd70..a52a5c6de65 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "test:version": "node scripts/check-version.js", "test:cov": "c8 npm run test:test", "test:test": "mocha", - "lint": "eslint --ext .ts src" + "lint": "eslint --ext .ts src --cache --cache-strategy content" }, "keywords": [ "adaptive card", From cf847f51306bf710817ca261a04761f6fe840ef5 Mon Sep 17 00:00:00 2001 From: Shantha Kumar T Date: Wed, 25 Sep 2024 05:54:58 +0530 Subject: [PATCH 26/42] Enhances 'entra m365group get' with additional properties. Closes #6148 --- .../cmd/entra/m365group/m365group-get.mdx | 21 +++- .../entra/commands/group/group-get.spec.ts | 7 +- .../commands/m365group/m365group-get.spec.ts | 103 ++++++++++++++++-- .../entra/commands/m365group/m365group-get.ts | 10 ++ .../m365group/m365group-user-remove.ts | 4 - 5 files changed, 130 insertions(+), 15 deletions(-) diff --git a/docs/docs/cmd/entra/m365group/m365group-get.mdx b/docs/docs/cmd/entra/m365group/m365group-get.mdx index c1fd7f619a8..9a81bdc7a68 100644 --- a/docs/docs/cmd/entra/m365group/m365group-get.mdx +++ b/docs/docs/cmd/entra/m365group/m365group-get.mdx @@ -89,7 +89,12 @@ m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --includeSite "securityIdentifier": "S-1-12-1-4288263055-1337657931-1773679776-1830684296", "theme": null, "visibility": "Public", - "onPremisesProvisioningErrors": [] + "onPremisesProvisioningErrors": [], + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false } ``` @@ -97,6 +102,8 @@ m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --includeSite ```text + allowExternalSenders : false + autoSubscribeNewMembers : false classification : null createdDateTime : 2023-06-01T20:18:30Z creationOptions : [] @@ -105,8 +112,11 @@ m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --includeSite displayName : Finance expirationDateTime : null groupTypes : ["Unified"] + hideFromAddressLists : false + hideFromOutlookClients : false id : ff99b38f-0a4b-4fbb-a034-b86988061e6d isAssignableToRole : null + isSubscribedByMail : false mail : finance@contoso.onmicrosoft.com mailEnabled : true mailNickname : finance @@ -135,8 +145,8 @@ m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --includeSite ```csv - id,createdDateTime,description,displayName,mail,mailEnabled,mailNickname,renewedDateTime,securityEnabled,securityIdentifier,visibility - ff99b38f-0a4b-4fbb-a034-b86988061e6d,2023-06-01T20:18:30Z,"This is the Contoso Finance Group. Please come here and check out the latest news, posts, files, and more.",Finance,finance@contoso.onmicrosoft.com,1,finance,2023-06-01T20:18:30Z,,S-1-12-1-4288263055-1337657931-1773679776-1830684296,Public + id,createdDateTime,description,displayName,mail,mailEnabled,mailNickname,renewedDateTime,securityEnabled,securityIdentifier,visibility,allowExternalSenders,autoSubscribeNewMembers,isSubscribedByMail,hideFromOutlookClients,hideFromAddressLists + ff99b38f-0a4b-4fbb-a034-b86988061e6d,2023-06-01T20:18:30Z,"This is the Contoso Finance Group. Please come here and check out the latest news, posts, files, and more.",Finance,finance@contoso.onmicrosoft.com,1,finance,2023-06-01T20:18:30Z,,S-1-12-1-4288263055-1337657931-1773679776-1830684296,Public,false,false,false,false,false ``` @@ -162,6 +172,11 @@ m365 entra m365group get --id 1caf7dcd-7e83-4c3a-94f7-932a1299c844 --includeSite securityEnabled | false securityIdentifier | S-1-12-1-4288263055-1337657931-1773679776-1830684296 visibility | Public + allowExternalSenders | false + autoSubscribeNewMembers | false + isSubscribedByMail | false + hideFromOutlookClients | false + hideFromAddressLists | false ``` diff --git a/src/m365/entra/commands/group/group-get.spec.ts b/src/m365/entra/commands/group/group-get.spec.ts index 725c6501aaa..6d70016a569 100644 --- a/src/m365/entra/commands/group/group-get.spec.ts +++ b/src/m365/entra/commands/group/group-get.spec.ts @@ -42,7 +42,12 @@ describe(commands.GROUP_GET, () => { ], "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false }] }; const validId = "1caf7dcd-7e83-4c3a-94f7-932a1299c844"; diff --git a/src/m365/entra/commands/m365group/m365group-get.spec.ts b/src/m365/entra/commands/m365group/m365group-get.spec.ts index 5a322a5c5a5..9cd0f6396c4 100644 --- a/src/m365/entra/commands/m365group/m365group-get.spec.ts +++ b/src/m365/entra/commands/m365group/m365group-get.spec.ts @@ -66,6 +66,7 @@ describe(commands.M365GROUP_GET, () => { }); it('retrieves information about the Microsoft 365 Group specified by id', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844`) { return { @@ -95,6 +96,16 @@ describe(commands.M365GROUP_GET, () => { }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } + throw 'Invalid request'; }); @@ -122,7 +133,12 @@ describe(commands.M365GROUP_GET, () => { ], "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); @@ -160,6 +176,16 @@ describe(commands.M365GROUP_GET, () => { }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } + throw 'Invalid request'; }); @@ -187,7 +213,12 @@ describe(commands.M365GROUP_GET, () => { ], "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); @@ -221,6 +252,16 @@ describe(commands.M365GROUP_GET, () => { }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } + throw 'Invalid request'; }); @@ -248,7 +289,12 @@ describe(commands.M365GROUP_GET, () => { ], "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); @@ -281,11 +327,19 @@ describe(commands.M365GROUP_GET, () => { "visibility": "Public" }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844/drive?$select=webUrl`) { return { webUrl: "https://contoso.sharepoint.com/sites/finance/Shared%20Documents" }; } - throw 'Invalid request'; }); @@ -314,7 +368,12 @@ describe(commands.M365GROUP_GET, () => { "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, "siteUrl": "https://contoso.sharepoint.com/sites/finance", - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); @@ -348,6 +407,16 @@ describe(commands.M365GROUP_GET, () => { }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844/drive?$select=webUrl`) { return { webUrl: "https://contoso.sharepoint.com/sites/finance/Shared%20Documents" }; } @@ -380,7 +449,12 @@ describe(commands.M365GROUP_GET, () => { "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, "siteUrl": "https://contoso.sharepoint.com/sites/finance", - "visibility": "Public" + "visibility": "Public", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); @@ -414,6 +488,16 @@ describe(commands.M365GROUP_GET, () => { }; } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`) { + return { + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false + }; + } + if (opts.url === `https://graph.microsoft.com/v1.0/groups/1caf7dcd-7e83-4c3a-94f7-932a1299c844/drive?$select=webUrl`) { return { webUrl: "" }; } @@ -446,7 +530,12 @@ describe(commands.M365GROUP_GET, () => { "renewedDateTime": "2017-11-29T03:27:05Z", "securityEnabled": false, "visibility": "Public", - "siteUrl": "" + "siteUrl": "", + "allowExternalSenders": false, + "autoSubscribeNewMembers": false, + "isSubscribedByMail": false, + "hideFromOutlookClients": false, + "hideFromAddressLists": false })); }); diff --git a/src/m365/entra/commands/m365group/m365group-get.ts b/src/m365/entra/commands/m365group/m365group-get.ts index 36d848673b0..633e9184902 100644 --- a/src/m365/entra/commands/m365group/m365group-get.ts +++ b/src/m365/entra/commands/m365group/m365group-get.ts @@ -86,6 +86,16 @@ class EntraM365GroupGetCommand extends GraphCommand { throw Error(`Specified group with id '${group.id}' is not a Microsoft 365 group.`); } + const requestExtendedOptions: CliRequestOptions = { + url: `${this.resource}/v1.0/groups/${group.id}?$select=allowExternalSenders,autoSubscribeNewMembers,hideFromAddressLists,hideFromOutlookClients,isSubscribedByMail`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + const groupExtended = await request.get<{ allowExternalSenders: boolean, autoSubscribeNewMembers: boolean, hideFromAddressLists: boolean, hideFromOutlookClients: boolean, isSubscribedByMail: boolean }>(requestExtendedOptions); + group = { ...group, ...groupExtended }; + if (args.options.includeSiteUrl) { const requestOptions: CliRequestOptions = { url: `${this.resource}/v1.0/groups/${group.id}/drive?$select=webUrl`, diff --git a/src/m365/entra/commands/m365group/m365group-user-remove.ts b/src/m365/entra/commands/m365group/m365group-user-remove.ts index 7875886358e..126ab7dc847 100644 --- a/src/m365/entra/commands/m365group/m365group-user-remove.ts +++ b/src/m365/entra/commands/m365group/m365group-user-remove.ts @@ -129,10 +129,6 @@ class EntraM365GroupUserRemoveCommand extends GraphCommand { } public async commandAction(logger: Logger, args: CommandArgs): Promise { - if (args.options.userName) { - await this.warn(logger, `Option 'userName' is deprecated. Please use 'ids' or 'userNames' instead.`); - } - const removeUser = async (): Promise => { try { const groupId: string = await this.getGroupId(logger, args.options.groupId, args.options.teamId, args.options.groupName, args.options.teamName); From d90637cb27f91f573258d89a98d474a5e61b1f96 Mon Sep 17 00:00:00 2001 From: Mathijs Verbeeck Date: Thu, 26 Sep 2024 10:07:27 +0200 Subject: [PATCH 27/42] Enhances spo contenttype commands with Parent expand. Closes #6329 --- .../cmd/spo/contenttype/contenttype-get.mdx | 8 +- .../cmd/spo/contenttype/contenttype-list.mdx | 8 +- .../cmd/spo/list/list-contenttype-list.mdx | 8 +- src/Command.ts | 2 +- .../contenttype/contenttype-get.spec.ts | 26 +- .../commands/contenttype/contenttype-get.ts | 4 +- .../contenttype/contenttype-list.spec.ts | 529 +++--------------- .../commands/contenttype/contenttype-list.ts | 9 +- .../list/list-contenttype-list.spec.ts | 22 +- .../commands/list/list-contenttype-list.ts | 7 +- 10 files changed, 147 insertions(+), 476 deletions(-) diff --git a/docs/docs/cmd/spo/contenttype/contenttype-get.mdx b/docs/docs/cmd/spo/contenttype/contenttype-get.mdx index 6a6ff026de6..c8bf91effc1 100644 --- a/docs/docs/cmd/spo/contenttype/contenttype-get.mdx +++ b/docs/docs/cmd/spo/contenttype/contenttype-get.mdx @@ -108,6 +108,12 @@ m365 spo contenttype get --webUrl https://contoso.sharepoint.com/sites/contoso-s "NewFormTarget": 0, "NewFormTemplateName": "ListForm", "NewFormUrl": "", + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, "ReadOnly": false, "SchemaXml": "ListFormListFormListForm", "Scope": "/sites/contoso-sales", @@ -170,7 +176,7 @@ m365 spo contenttype get --webUrl https://contoso.sharepoint.com/sites/contoso-s Date: 10/2/2023 - ## PnP Alert ([object Object]) + ## PnP Alert (0x01007926A45D687BA842B947286090B8F67D) Property | Value ---------|------- diff --git a/docs/docs/cmd/spo/contenttype/contenttype-list.mdx b/docs/docs/cmd/spo/contenttype/contenttype-list.mdx index 4e35cd377e0..b2964a64679 100644 --- a/docs/docs/cmd/spo/contenttype/contenttype-list.mdx +++ b/docs/docs/cmd/spo/contenttype/contenttype-list.mdx @@ -75,6 +75,12 @@ m365 spo contenttype list --webUrl "https://contoso.sharepoint.com/sites/contoso "NewFormTarget": 0, "NewFormTemplateName": "ListForm", "NewFormUrl": "", + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, "ReadOnly": false, "SchemaXml": "ListFormListFormListForm", "Scope": "/sites/contoso-sales", @@ -108,7 +114,7 @@ m365 spo contenttype list --webUrl "https://contoso.sharepoint.com/sites/contoso # spo contenttype list --webUrl "https://contoso.sharepoint.com/sites/contoso-sales" Date: 10/2/2023 - ## PnP Alert ([object Object]) + ## PnP Alert (0x01007926A45D687BA842B947286090B8F67D) Property | Value ---------|------- diff --git a/docs/docs/cmd/spo/list/list-contenttype-list.mdx b/docs/docs/cmd/spo/list/list-contenttype-list.mdx index f0db7ff4106..71cced4d53e 100644 --- a/docs/docs/cmd/spo/list/list-contenttype-list.mdx +++ b/docs/docs/cmd/spo/list/list-contenttype-list.mdx @@ -87,6 +87,12 @@ m365 spo list contenttype list --webUrl https://contoso.sharepoint.com/sites/pro "NewFormTarget": 0, "NewFormTemplateName": "ListForm", "NewFormUrl": "", + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, "ReadOnly": false, "SchemaXml": "ListFormListFormListForm", "Scope": "/Lists/Test", @@ -121,7 +127,7 @@ m365 spo list contenttype list --webUrl https://contoso.sharepoint.com/sites/pro Date: 2/20/2023 - ## Document ([object Object]) + ## Document (0x01000B1208C5D23DF44B9F1AEE7373DE9D5E) Property | Value ---------|------- diff --git a/src/Command.ts b/src/Command.ts index 54c28bcd8e6..21a579905b6 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -745,7 +745,7 @@ export default abstract class Command { } private getLogItemId(logItem: any): string | undefined { - return logItem.id ?? logItem.Id ?? logItem.ID ?? + return logItem.id ?? logItem.Id?.StringValue ?? logItem.Id ?? logItem.ID ?? logItem.uniqueId ?? logItem.UniqueId ?? logItem.objectId ?? logItem.ObjectId ?? logItem.url ?? logItem.Url ?? logItem.URL; diff --git a/src/m365/spo/commands/contenttype/contenttype-get.spec.ts b/src/m365/spo/commands/contenttype/contenttype-get.spec.ts index 2969ff88213..b4b7f7b0368 100644 --- a/src/m365/spo/commands/contenttype/contenttype-get.spec.ts +++ b/src/m365/spo/commands/contenttype/contenttype-get.spec.ts @@ -69,7 +69,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a site content type by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')?$expand=Parent`) { return contentTypeByIdResponse; } @@ -82,7 +82,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a site content type by name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/_api/web/contenttypes?$filter=Name eq 'PnP%20Alert'`) > -1) { + if ((opts.url as string).indexOf(`/_api/web/contenttypes?$filter=Name eq 'PnP%20Alert'&$expand=Parent`) > -1) { return contentTypeByNameResponse; } @@ -95,7 +95,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list content type by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Events')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Events')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')?$expand=Parent`) { return contentTypeByIdResponse; } @@ -108,7 +108,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list retrieved by its title and the content type by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists(guid'9153a1f5-22f7-49e8-a854-06bb4477c2a2')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists(guid'9153a1f5-22f7-49e8-a854-06bb4477c2a2')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')?$expand=Parent`) { return contentTypeByIdResponse; } @@ -121,7 +121,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list retrieved by its url and the content type by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/GetList('%2Fsites%2Fportal%2Fdocuments')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/GetList('%2Fsites%2Fportal%2Fdocuments')/contenttypes('0x010200973548ACFFDA0948BE80AF607C4E28F9')?$expand=Parent`) { return contentTypeByIdResponse; } @@ -134,7 +134,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list retrieved by its title and the content type by name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Events')/contenttypes?$filter=Name eq 'Event'`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Events')/contenttypes?$filter=Name eq 'Event'&$expand=Parent`) { return contentTypeByNameResponse; } @@ -147,7 +147,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list retrieved by its id and the content type by name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists(guid'9153a1f5-22f7-49e8-a854-06bb4477c2a2')/contenttypes?$filter=Name eq 'Event'`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists(guid'9153a1f5-22f7-49e8-a854-06bb4477c2a2')/contenttypes?$filter=Name eq 'Event'&$expand=Parent`) { return contentTypeByNameResponse; } @@ -160,7 +160,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('gets information about a list retrieved by its url and the content type by name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/GetList('%2Fsites%2Fportal%2Fdocuments')/contenttypes?$filter=Name eq 'Event'`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/GetList('%2Fsites%2Fportal%2Fdocuments')/contenttypes?$filter=Name eq 'Event'&$expand=Parent`) { return contentTypeByNameResponse; } @@ -173,7 +173,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('correctly escapes special characters in the content type id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0%3D0100558D85B7216F6A489A499DB361E1AE2F')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0%3D0100558D85B7216F6A489A499DB361E1AE2F')?$expand=Parent`) { return { "odata.null": true }; } @@ -186,7 +186,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('correctly handles site content type not found by id', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')?$expand=Parent`) { return { "odata.null": true }; } @@ -199,7 +199,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('correctly handles site content type not found by content type name', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes?$filter=Name eq 'PnP%20Alert'`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/contenttypes?$filter=Name eq 'PnP%20Alert'&$expand=Parent`) { return { "value": [] }; } @@ -212,7 +212,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('correctly handles list content type not found', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Documents')/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Documents')/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')?$expand=Parent`) { return { "odata.null": true }; } @@ -225,7 +225,7 @@ describe(commands.CONTENTTYPE_GET, () => { it('correctly handles list not found', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Documents')/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')`) { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/web/lists/getByTitle('Documents')/contenttypes('0x0100558D85B7216F6A489A499DB361E1AE2F')?$expand=Parent`) { throw { error: { "odata.error": { diff --git a/src/m365/spo/commands/contenttype/contenttype-get.ts b/src/m365/spo/commands/contenttype/contenttype-get.ts index e419a5da2f0..0ab5a9b9e8c 100644 --- a/src/m365/spo/commands/contenttype/contenttype-get.ts +++ b/src/m365/spo/commands/contenttype/contenttype-get.ts @@ -115,10 +115,10 @@ class SpoContentTypeGetCommand extends SpoCommand { requestUrl += "/contenttypes"; if (args.options.id) { - requestUrl += `('${formatting.encodeQueryParameter(args.options.id)}')`; + requestUrl += `('${formatting.encodeQueryParameter(args.options.id)}')?$expand=Parent`; } else if (args.options.name) { - requestUrl += `?$filter=Name eq '${formatting.encodeQueryParameter(args.options.name)}'`; + requestUrl += `?$filter=Name eq '${formatting.encodeQueryParameter(args.options.name)}'&$expand=Parent`; } const requestOptions: CliRequestOptions = { diff --git a/src/m365/spo/commands/contenttype/contenttype-list.spec.ts b/src/m365/spo/commands/contenttype/contenttype-list.spec.ts index 54f06af0f9f..9b733922831 100644 --- a/src/m365/spo/commands/contenttype/contenttype-list.spec.ts +++ b/src/m365/spo/commands/contenttype/contenttype-list.spec.ts @@ -14,6 +14,75 @@ import commands from '../../commands.js'; import command from './contenttype-list.js'; describe(commands.CONTENTTYPE_LIST, () => { + const ctResponse = { + "value": [ + { + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, + "Description": "Create a new document.", + "DisplayFormTemplateName": "DocumentLibraryForm", + "DisplayFormUrl": "", + "DocumentTemplate": "/Shared Documents/Forms/template.dotx", + "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", + "EditFormTemplateName": "DocumentLibraryForm", + "EditFormUrl": "", + "Group": "Document Content Types", + "Hidden": false, + "Id": { + "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" + }, + "JSLink": "", + "MobileDisplayFormUrl": "", + "MobileEditFormUrl": "", + "MobileNewFormUrl": "", + "Name": "Document", + "NewFormTemplateName": "DocumentLibraryForm", + "NewFormUrl": "", + "ReadOnly": false, + "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", + "Scope": "/Shared Documents", + "Sealed": false, + "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" + }, + { + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, + "Description": "Create a new folder.", + "DisplayFormTemplateName": "ListForm", + "DisplayFormUrl": "", + "DocumentTemplate": "", + "DocumentTemplateUrl": "", + "EditFormTemplateName": "ListForm", + "EditFormUrl": "", + "Group": "Folder Content Types", + "Hidden": false, + "Id": { + "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" + }, + "JSLink": "", + "MobileDisplayFormUrl": "", + "MobileEditFormUrl": "", + "MobileNewFormUrl": "", + "Name": "Folder", + "NewFormTemplateName": "ListForm", + "NewFormUrl": "", + "ReadOnly": false, + "SchemaXml": "ListFormListFormListForm", + "Scope": "/Shared Documents", + "Sealed": true, + "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" + } + ] + }; + let log: any[]; let logger: Logger; let loggerLogSpy: sinon.SinonSpy; @@ -83,9 +152,9 @@ describe(commands.CONTENTTYPE_LIST, () => { }); it('command correctly handles reject request', async () => { - const err = 'Invalid request'; + const err = 'Error occured...'; sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf('/_api/web/ContentTypes') > -1) { + if (opts.url === 'https://contoso.sharepoint.com/_api/web/ContentTypes?$expand=Parent') { throw err; } @@ -102,66 +171,11 @@ describe(commands.CONTENTTYPE_LIST, () => { it('retrieves all content types (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes`) > -1) { + if (opts.url === 'https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$expand=Parent') { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { - return { - "value": [ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ] - }; + return ctResponse; } } @@ -174,124 +188,16 @@ describe(commands.CONTENTTYPE_LIST, () => { webUrl: 'https://contoso.sharepoint.com/sites/test' } }); - assert(loggerLogSpy.calledWith([ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ])); + assert(loggerLogSpy.calledWith(ctResponse.value)); }); it('retrieves all content types', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$expand=Parent`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { - return { - "value": [ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ] - }; + return ctResponse; } } @@ -303,124 +209,16 @@ describe(commands.CONTENTTYPE_LIST, () => { webUrl: 'https://contoso.sharepoint.com/sites/test' } }); - assert(loggerLogSpy.calledWith([ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ])); + assert(loggerLogSpy.calledWith(ctResponse.value)); }); it('retrieves all content types by category (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$filter=Group eq 'List%20Content%20Types'`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$expand=Parent&$filter=Group eq 'List%20Content%20Types'`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { - return { - "value": [ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ] - }; + return ctResponse; } } @@ -434,124 +232,16 @@ describe(commands.CONTENTTYPE_LIST, () => { webUrl: 'https://contoso.sharepoint.com/sites/test' } }); - assert(loggerLogSpy.calledWith([ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ])); + assert(loggerLogSpy.calledWith(ctResponse.value)); }); it('retrieves all content types by category', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$filter=Group eq 'List%20Content%20Types'`) > -1) { + if (opts.url === `https://contoso.sharepoint.com/sites/test/_api/web/ContentTypes?$expand=Parent&$filter=Group eq 'List%20Content%20Types'`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { - return { - "value": [ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ] - }; + return ctResponse; } } @@ -564,59 +254,6 @@ describe(commands.CONTENTTYPE_LIST, () => { webUrl: 'https://contoso.sharepoint.com/sites/test' } }); - assert(loggerLogSpy.calledWith([ - { - "Description": "Create a new document.", - "DisplayFormTemplateName": "DocumentLibraryForm", - "DisplayFormUrl": "", - "DocumentTemplate": "/Shared Documents/Forms/template.dotx", - "DocumentTemplateUrl": "/Shared Documents/Forms/template.dotx", - "EditFormTemplateName": "DocumentLibraryForm", - "EditFormUrl": "", - "Group": "Document Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Document", - "NewFormTemplateName": "DocumentLibraryForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "DocumentLibraryFormDocumentLibraryFormDocumentLibraryForm", - "Scope": "/Shared Documents", - "Sealed": false, - "StringId": "0x010100260C61709CD8E548948F9BF605F8F54F" - }, - { - "Description": "Create a new folder.", - "DisplayFormTemplateName": "ListForm", - "DisplayFormUrl": "", - "DocumentTemplate": "", - "DocumentTemplateUrl": "", - "EditFormTemplateName": "ListForm", - "EditFormUrl": "", - "Group": "Folder Content Types", - "Hidden": false, - "Id": { - "StringValue": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - }, - "JSLink": "", - "MobileDisplayFormUrl": "", - "MobileEditFormUrl": "", - "MobileNewFormUrl": "", - "Name": "Folder", - "NewFormTemplateName": "ListForm", - "NewFormUrl": "", - "ReadOnly": false, - "SchemaXml": "ListFormListFormListForm", - "Scope": "/Shared Documents", - "Sealed": true, - "StringId": "0x0120000EAD53EDAD7C6647B0D976EEC953F99E" - } - ])); + assert(loggerLogSpy.calledWith(ctResponse.value)); }); }); diff --git a/src/m365/spo/commands/contenttype/contenttype-list.ts b/src/m365/spo/commands/contenttype/contenttype-list.ts index 8c0aaf6fe38..e09ca57352d 100644 --- a/src/m365/spo/commands/contenttype/contenttype-list.ts +++ b/src/m365/spo/commands/contenttype/contenttype-list.ts @@ -34,6 +34,7 @@ class SpoContentTypeListCommand extends SpoCommand { this.#initTelemetry(); this.#initOptions(); this.#initValidators(); + this.#initTypes(); } #initTelemetry(): void { @@ -61,12 +62,16 @@ class SpoContentTypeListCommand extends SpoCommand { ); } + #initTypes(): void { + this.types.string.push('webUrl', 'category'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { try { - let requestUrl: string = `${args.options.webUrl}/_api/web/ContentTypes`; + let requestUrl: string = `${args.options.webUrl}/_api/web/ContentTypes?$expand=Parent`; if (args.options.category) { - requestUrl += `?$filter=Group eq '${formatting.encodeQueryParameter(args.options.category as string)}'`; + requestUrl += `&$filter=Group eq '${formatting.encodeQueryParameter(args.options.category as string)}'`; } const res = await odata.getAllItems(requestUrl); diff --git a/src/m365/spo/commands/list/list-contenttype-list.spec.ts b/src/m365/spo/commands/list/list-contenttype-list.spec.ts index cc827b4c4fc..831f4de7d5e 100644 --- a/src/m365/spo/commands/list/list-contenttype-list.spec.ts +++ b/src/m365/spo/commands/list/list-contenttype-list.spec.ts @@ -18,6 +18,12 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { const contentTypeResponse = { "value": [ { + "Parent": { + "StringId": "0x01000B1208C5D23DF", + "Name": "Item", + "Group": "List items", + "Id": { "StringId": "0x01000B1208C5D23DF" } + }, "Description": "Create a new document.", "DisplayFormTemplateName": "DocumentLibraryForm", "DisplayFormUrl": "", @@ -127,7 +133,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listTitle option is passed (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists/getByTitle('Documents')/ContentTypes`) { + if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists/getByTitle('Documents')/ContentTypes?$expand=Parent`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -150,7 +156,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listTitle option is passed', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists/getByTitle('Documents')/ContentTypes`) { + if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists/getByTitle('Documents')/ContentTypes?$expand=Parent`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -172,7 +178,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listId option is passed (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes`) > -1) { + if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes?$expand=Parent`) > -1) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -195,7 +201,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listUrl option is passed', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === 'https://contoso.sharepoint.com/_api/web/GetList(\'%2Fsites%2Fdocuments\')/ContentTypes') { + if (opts.url === 'https://contoso.sharepoint.com/_api/web/GetList(\'%2Fsites%2Fdocuments\')/ContentTypes?$expand=Parent') { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -217,7 +223,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listUrl option is passed (debug)', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === 'https://contoso.sharepoint.com/_api/web/GetList(\'%2Fsites%2Fdocuments\')/ContentTypes') { + if (opts.url === 'https://contoso.sharepoint.com/_api/web/GetList(\'%2Fsites%2Fdocuments\')/ContentTypes?$expand=Parent') { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -240,7 +246,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('retrieves all content types of the specific list if listId option is passed', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes`) > -1) { + if ((opts.url as string).indexOf(`https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes?$expand=Parent`) > -1) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -263,7 +269,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('outputs all properties when output is JSON', async () => { sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes`) { + if (opts.url === `https://contoso.sharepoint.com/sites/ninja/_api/web/lists(guid'dfddade1-4729-428d-881e-7fedf3cae50d')/ContentTypes?$expand=Parent`) { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { @@ -287,7 +293,7 @@ describe(commands.LIST_CONTENTTYPE_LIST, () => { it('command correctly handles list get reject request', async () => { const err = 'list retrieve error'; sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://contoso.sharepoint.com/_api/web/lists/getByTitle('Documents')/ContentTypes`) { + if (opts.url === `https://contoso.sharepoint.com/_api/web/lists/getByTitle('Documents')/ContentTypes?$expand=Parent`) { throw err; } diff --git a/src/m365/spo/commands/list/list-contenttype-list.ts b/src/m365/spo/commands/list/list-contenttype-list.ts index 58da790b470..b034fecfedb 100644 --- a/src/m365/spo/commands/list/list-contenttype-list.ts +++ b/src/m365/spo/commands/list/list-contenttype-list.ts @@ -38,6 +38,7 @@ class SpoListContentTypeListCommand extends SpoCommand { this.#initOptions(); this.#initValidators(); this.#initOptionSets(); + this.#initTypes(); } #initTelemetry(): void { @@ -90,6 +91,10 @@ class SpoListContentTypeListCommand extends SpoCommand { this.optionSets.push({ options: ['listId', 'listTitle', 'listUrl'] }); } + #initTypes(): void { + this.types.string.push('webUrl', 'listId', 'listTitle', 'listUrl'); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { if (this.verbose) { const list: string = (args.options.listId ? args.options.listId : args.options.listTitle ? args.options.listTitle : args.options.listUrl) as string; @@ -109,7 +114,7 @@ class SpoListContentTypeListCommand extends SpoCommand { } try { - const res = await odata.getAllItems(`${requestUrl}/ContentTypes`); + const res = await odata.getAllItems(`${requestUrl}/ContentTypes?$expand=Parent`); await logger.log(res); } catch (err: any) { From 4bf19f45c3a4aec71e8f70d8573483ec1c89681d Mon Sep 17 00:00:00 2001 From: Jwaegebaert <38426621+Jwaegebaert@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:30:50 +0200 Subject: [PATCH 28/42] Updates the docs on how to log in to CLI. Closes #6343 --- README.md | 4 +- docs/docs/cmd/login.mdx | 89 +++++++++--------- .../pp/managementapp/managementapp-add.mdx | 2 +- docs/docs/concepts/authorization-tokens.mdx | 39 ++------ docs/docs/images/consent.png | Bin 657435 -> 0 bytes docs/docs/index.mdx | 2 +- .../user-guide/connecting-microsoft-365.mdx | 16 +++- .../user-guide/manage-microsoft-365-apps.mdx | 14 +-- docs/docs/user-guide/using-own-identity.mdx | 20 ++-- 9 files changed, 85 insertions(+), 101 deletions(-) delete mode 100644 docs/docs/images/consent.png diff --git a/README.md b/README.md index 79e175e65d5..d7c91c4e7b1 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,14 @@ npm install -g @pnp/cli-microsoft365 ## Usage +>Before logging in, you should create a custom Microsoft Entra application registration. Use the `m365 setup` command or refer to the [Using your own Microsoft Entra identity](https://pnp.github.io/cli-microsoft365/user-guide/using-own-identity/) guide. + Use the `login` command to start the Device Code login flow to authenticate with your Microsoft 365 tenant. ```sh m365 login ``` ->On your first login you will be asked to consent to several permissions that the `PnP Management Shell` multi-tenant app requires for the commands to work successfully against your tenant. If you want to create your own identity to use with the CLI, refer to the [Using your own Microsoft Entra identity](https://pnp.github.io/cli-microsoft365/user-guide/using-own-identity/) guide. - >For alternative authentication methods and usage, refer to the [login](https://pnp.github.io/cli-microsoft365/cmd/login/) command documentation List all commands using the global `--help` option. diff --git a/docs/docs/cmd/login.mdx b/docs/docs/cmd/login.mdx index eb549c91567..739fe31169c 100644 --- a/docs/docs/cmd/login.mdx +++ b/docs/docs/cmd/login.mdx @@ -15,6 +15,12 @@ m365 login [options] ## Options ```md definition-list +`--appId [appId]` +: App ID of the Microsoft Entra application to use for authentication. This option is crucial and should be specified if it isn't defined elsewhere. + +`--tenant [tenant]` +: ID of the tenant from which accounts should authenticate. Use `common` or `organization` for multitenant apps. Defaults to `common` if not specified and if the config value `tenantId` and the environment variable `CLIMICROSOFT365_TENANT` are also not set. + `-t, --authType [authType]` : The type of authentication to use. Allowed values `certificate`, `deviceCode`, `password`, `identity`, `browser`, `secret`. Default `deviceCode` @@ -36,12 +42,6 @@ m365 login [options] `-s, --secret [secret]` : Client Secret of the Microsoft Entra application to use for authentication. Required when `authType` is set to `secret`. -`--appId [appId]` -: App ID of the Microsoft Entra application to use for authentication. If not specified, use the app specified in the `CLIMICROSOFT365_ENTRAAPPID` environment variable. If the environment variable is not defined, use the multitenant PnP Management Shell app - -`--tenant [tenant]` -: ID of the tenant from which accounts should be able to authenticate. Use `common` or `organization` if the app is multitenant. If not specified, use the tenant specified in the `CLIMICROSOFT365_TENANT` environment variable. If the environment variable is not defined, use `common` as the tenant identifier - `--cloud [cloud]` : Cloud to connect to. Allowed values `Public`, `USGov`, `USGovHigh`, `USGovDoD` and `China`. Default `Public` @@ -56,6 +56,20 @@ m365 login [options] ## Remarks +:::tip + +The CLI determines the `appId` and `tenant` values in the following order of precedence: + +1. The `appId` or `tenant` option specified during login. +2. The `clientId` or `tenantId` value set in the configuration. +3. The `CLIMICROSOFT365_ENTRAAPPID` or `CLIMICROSOFT365_TENANT` environment variable. + +For `appId`, it is **required** that at least one of the options is specified during login. + +For `tenant`, if none are specified, the CLI will default to `common`. + +::: + Using the `login` command you can log in to Microsoft 365. By default, the `login` command uses device code OAuth flow to log in to Microsoft 365. Alternatively, you can authenticate using a user name and password or certificate, which are convenient for CI/CD scenarios, but which come with their own [limitations](../user-guide/connecting-microsoft-365.mdx). The default `authType` can be configured using `m365 cli config set`. This means you'll be able to run `m365 login` without specifying the `--authType` option. @@ -78,116 +92,103 @@ When signing in with multiple identities, every signin will be saved as a connec ## Examples -Log in to Microsoft 365 using the device code +Log in to Microsoft 365 using the device code. + +```sh +m365 login --appId 31359c7f-bd7e-475c-86db-fdb8c937548e +``` + +Log in to Microsoft 365 using the device code and set the connection name. ```sh -m365 login +m365 login --connectionName 'myworkaccount' --appId 31359c7f-bd7e-475c-86db-fdb8c937548e ``` -Log in to Microsoft 365 using the device code and set the connection name +Log in to Microsoft 365 using your own Microsoft Entra application that's restricted only to allow accounts from the specific tenant. ```sh -m365 login --connectionName 'myworkaccount' +m365 login --appId 31359c7f-bd7e-475c-86db-fdb8c937548c --tenant 31359c7f-bd7e-475c-86db-fdb8c937548a ``` -Log in to Microsoft 365 using the device code in debug mode including detailed debug information in the console output +Log in to Microsoft 365 using your own Microsoft Entra application and a personal information exchange (.pfx) file. ```sh -m365 login --debug +m365 login --authType certificate --appId 31359c7f-bd7e-475c-86db-fdb8c937548c --tenant 31359c7f-bd7e-475c-86db-fdb8c937548a --certificateFile /Users/user/dev/localhost.pfx --password 'pass@word1' ``` -Log in to Microsoft 365 using a user name and password +Log in to Microsoft 365 using a user name and password with `clientId` set in the configuration. ```sh m365 login --authType password --userName user@contoso.com --password pass@word1 ``` -Log in to Microsoft 365 using a PEM certificate +Log in to Microsoft 365 using a PEM certificate with `clientId` set in the configuration. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pem ``` -Log in to Microsoft 365 using a PEM certificate. Use the specified thumbprint +Log in to Microsoft 365 using a PEM certificate with `clientId` set in the configuration. Use the specified thumbprint. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pem --thumbprint 47C4885736C624E90491F32B98855AA8A7562AF1 ``` -Log in to Microsoft 365 using a personal information exchange (.pfx) file +Log in to Microsoft 365 using a personal information exchange (.pfx) file with `CLIMICROSOFT365_ENTRAAPPID` environment variable set. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pfx --password 'pass@word1' ``` -Log in to Microsoft 365 using a personal information exchange (.pfx) file protected with an empty password +Log in to Microsoft 365 using a personal information exchange (.pfx) file protected with an empty password and `CLIMICROSOFT365_ENTRAAPPID` environment variable set. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pfx --password ``` -Log in to Microsoft 365 using a personal information exchange (.pfx) file not protected with a password +Log in to Microsoft 365 using a personal information exchange (.pfx) file not protected with a password and `CLIMICROSOFT365_ENTRAAPPID` environment variable set. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pfx ``` -Log in to Microsoft 365 using a personal information exchange (.pfx) file. Use the specified thumbprint +Log in to Microsoft 365 using a personal information exchange (.pfx) file. Use the specified thumbprint and `CLIMICROSOFT365_ENTRAAPPID` environment variable set. ```sh m365 login --authType certificate --certificateFile /Users/user/dev/localhost.pfx --thumbprint 47C4885736C624E90491F32B98855AA8A7562AF1 --password 'pass@word1' ``` -Log in to Microsoft 365 using a certificate from a base64-encoded string +Log in to Microsoft 365 using a certificate from a base64-encoded string with `CLIMICROSOFT365_ENTRAAPPID` environment variable set. ```sh m365 login --authType certificate --certificateBase64Encoded MIII2QIBAzCCCJ8GCSqGSIb3DQEHAaCCCJAEg...eX1N5AgIIAA== --thumbprint D0C9B442DE249F55D10CDA1A2418952DC7D407A3 ``` -Log in to Microsoft 365 using a system assigned managed identity. Applies to Azure resources with managed identity enabled, -such as Azure Virtual Machines, Azure App Service or Azure Functions +Log in to Microsoft 365 using a system assigned managed identity with `clientId` set in the configuration. Applies to Azure resources with managed identity enabled, such as Azure Virtual Machines, Azure App Service or Azure Functions. ```sh m365 login --authType identity ``` -Log in to Microsoft 365 using managed identity in Azure Cloud Shell. Uses the identity of the current user. +Log in to Microsoft 365 using managed identity in Azure Cloud Shell with `clientId` set in the configuration. Uses the identity of the current user. ```sh m365 login --authType identity ``` -Log in to Microsoft 365 using a user-assigned managed identity. Client id or principal id also known as object id value can be specified in the `userName` option. Applies to Azure resources with managed identity enabled, such as Azure Virtual Machines, Azure App Service or Azure Functions +Log in to Microsoft 365 using a user-assigned managed identity with `clientId` set in the configuration. Client id or principal id also known as object id value can be specified in the `userName` option. Applies to Azure resources with managed identity enabled, such as Azure Virtual Machines, Azure App Service or Azure Functions. ```sh m365 login --authType identity --userName ac9fbed5-804c-4362-a369-21a4ec51109e ``` -Log in to Microsoft 365 using your own multitenant Microsoft Entra application - -```sh -m365 login --appId 31359c7f-bd7e-475c-86db-fdb8c937548c -``` - -Log in to Microsoft 365 using your own Microsoft Entra application that's restricted only to allow accounts from the specific tenant - -```sh -m365 login --appId 31359c7f-bd7e-475c-86db-fdb8c937548c --tenant 31359c7f-bd7e-475c-86db-fdb8c937548a -``` - -Log in to Microsoft 365 using your own Microsoft Entra application and a personal information exchange (.pfx) file - -```sh -m365 login --authType certificate --appId 31359c7f-bd7e-475c-86db-fdb8c937548c --tenant 31359c7f-bd7e-475c-86db-fdb8c937548a --certificateFile /Users/user/dev/localhost.pfx --password 'pass@word1' -``` - -Log in to Microsoft 365 using the interactive browser authentication. Uses the identity of the current user. +Log in to Microsoft 365 using the interactive browser authentication with `clientId` set in the configuration. Uses the identity of the current user. ```sh m365 login --authType browser ``` -Log in to Microsoft 365 using a client secret. +Log in to Microsoft 365 using a client secret with `clientId` set in the configuration. ```sh m365 login --authType secret --secret topSeCr3t@007 diff --git a/docs/docs/cmd/pp/managementapp/managementapp-add.mdx b/docs/docs/cmd/pp/managementapp/managementapp-add.mdx index 8576fa7b7f6..f6ba8ce31b4 100644 --- a/docs/docs/cmd/pp/managementapp/managementapp-add.mdx +++ b/docs/docs/cmd/pp/managementapp/managementapp-add.mdx @@ -29,7 +29,7 @@ m365 pp managementapp add [options] ## Remarks -To execute this command the first time you'll need sign in using the Microsoft Azure PowerShell app registration. You can do this by executing `m365 login --appId 1950a258-227b-4e31-a9cf-717495945fc2`. To register the Microsoft Entra app registration that CLI for Microsoft 365 uses by default, execute `m365 pp managementapp add--appId 31359c7f-bd7e-475c-86db-fdb8c937548e`. +To execute this command the first time you'll need sign in using the Microsoft Azure PowerShell application registration. You can do this by executing `m365 login --appId 1950a258-227b-4e31-a9cf-717495945fc2`. To register the Microsoft Entra application registration that CLI for Microsoft 365 uses by default, execute `m365 pp managementapp add--appId 31359c7f-bd7e-475c-86db-fdb8c937548e`. For best performance use the `appId` option to reference the Microsoft Entra application registration to update. If you use `objectId` or `name`, this command will first need to find the corresponding `appId` for that application. diff --git a/docs/docs/concepts/authorization-tokens.mdx b/docs/docs/concepts/authorization-tokens.mdx index 96b42c1b8d0..ff30342e067 100644 --- a/docs/docs/concepts/authorization-tokens.mdx +++ b/docs/docs/concepts/authorization-tokens.mdx @@ -12,11 +12,11 @@ There are a number of ways in which you can authenticate and authorize with Micr ### Microsoft Entra application used by the CLI for Microsoft 365 -CLI for Microsoft 365 gets access to Microsoft 365 through a custom Microsoft Entra application named _PnP Microsoft 365 Management Shell_. If you don't want to consent this application in your tenant, you can use a different application instead. +CLI for Microsoft 365 gets access to Microsoft 365 through your own Microsoft Entra application registration. :::info -When you decide to use your own Microsoft Entra application, you need to choose the application to be a **public client**. Despite the setting's description, the application will not be publicly accessible. This setting enables the use of the device flow for your own application. Without activating this setting, it is not possible to complete the authentication process. The option is currently only available in the preview blade for managing for Microsoft Entra applications. +When you create your own Microsoft Entra application, you need to choose the application to be a **public client**. Despite the setting's description, the application will not be publicly accessible. This setting enables the use of the device flow for your own application and is only required for interactive use of the CLI. If you use the CLI with non-interactive scripts and log in using client credentials or Managed Service Identity, you don't need to activate this setting. The option is currently only available in the preview blade for managing Microsoft Entra applications.
@@ -24,42 +24,17 @@ When you decide to use your own Microsoft Entra application, you need to choose ::: -When specifying a custom Microsoft Entra application to be used by the CLI for Microsoft 365, set the `CLIMICROSOFT365_ENTRAAPPID` environment variable to the ID of your Microsoft Entra application. - -CLI for Microsoft 365 requires the following permissions to Microsoft 365 services: - -- Microsoft 365 SharePoint Online - - Have full control of all site collections - - Read user profiles - - Read and write managed metadata -- Microsoft Graph - - Read and write to all app catalogs - - Read and write all groups - - Read and write directory data - - Access directory as the signed in user - - Read and write identity providers - - Read and write access to user mail - - Send mail as a user - - Read all usage reports - - Manage user's installed Teams apps - - Invite guest users to the organization - - Create, read, update and delete user tasks and projects -- Microsoft 365 Management APIs - - Read service health information for your organization -- Windows Azure Active Directory - - Access the directory as the signed-in user -- Windows Azure Service Management API - - Access Azure Service Management as organization users -- Viva Engage - - Read and write to the Viva Engage platform +When specifying a application registration to be used by the CLI for Microsoft 365, set the `CLIMICROSOFT365_ENTRAAPPID` environment variable to the ID of your application registration. + +For a streamlined setup, you can use the [m365 setup](../cmd/setup.mdx) command to create a new application registration with all the necessary permissions to fully use the CLI for Microsoft 365. :::warning -After changing the ID of the Microsoft Entra application used by the CLI for Microsoft 365 refresh the existing connection to Microsoft 365 using the `login` command. If you try to use the existing connection, CLI for Microsoft 365 will fail when trying to refresh the existing access token. +After changing the ID of the application registration used by the CLI for Microsoft 365 refresh the existing connection to Microsoft 365 using the `login` command. If you try to use the existing connection, CLI for Microsoft 365 will fail when trying to refresh the existing access token. ::: -For instructions on how to create your own Microsoft Entra app see [Using your own Microsoft Entra identity](../user-guide/using-own-identity.mdx). +For instructions on how to create your own Microsoft Entra application registration see [Using your own Microsoft Entra identity](../user-guide/using-own-identity.mdx). ### Access and refresh tokens in the CLI for Microsoft 365 diff --git a/docs/docs/images/consent.png b/docs/docs/images/consent.png deleted file mode 100644 index 9aaaf04514b1953efbbd7d405c2e1b38b5d8a16c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 657435 zcmZU5cU+R&AAenMTDj^r%-oronOb^tQnP!r8O&^w*dsP<-_cxv*mk6wEv{_@)O z@a7|&cgU=^V6A1Ym9f3bo(I^IC*T`S=X2Tm|s<{W~8f{2S|Zr42I^vB@0ZryTE zG-Ka2TCQsM3=Y0d9cj8oeKm2c89euNm&JCK-+m?eI@dugq*@4R*5xB0o}6!^IhxO? zd=MsI%i9yVlUr-<^c|E1 zOg~rk^!B!%ot^XIB<8B5|L-V%RnTZK3cRH6Tzxy*Wc{^t`f5puhKyqBc;K2YrfRbG z|JkK*^unG4QJ=1H?|{Kxlpl4vX8N;lW@dDa4QP7*-;;Wc>ihcNd`jna27DWo7eoHf z1K39+_0X6_!#mA$vxC3W^5+^o?UwU$)1#xeN!V#&xh6Ez^ZzqiG*0r;f~>>ZTFYmp z{~0WM+%RJ2{FZ(Hb6KVR_Hs86*7FGu4~y`Z5xdZOCxn*4a-JB7-DM%#_IC(zB6q*R z7kqNIZw3c1WEKhUJi~n^T^3UKQ0Q-uExU1tXO!eFkFRwDp-0j-jw^<3sFJdY{6uRY zFy>-JQBe`;gP3Vmsearxu$|WZM?0hJn}hocb{+aTV_)pGQqS8@GP$b%O&OtdpkUFf zpo9>Nd+aNklX2UB=W+I5aKpgZpYm4op_lSQZ7t9$By4D#LG+W1!LuVg_|e7x2+RHx z6Q}&v^uXOukM@Y>o!D}%>#C%!tf{nJUtd4H2KIE9?xE$0I^(ow=8T(uy*954)EIV( zXKjiDH}y>}9{#nCiw!Hy+bx>j4JZ#?m9~AHCtB?PZE(yEBcb+m*SG!5P7`C}@3qA} zB5jLp?)H({)X{&*?MZt|Jin{Pa8b16!Y%)u>wlj)1pssffGxJmo}2}Xt+#J&OGk9Zm#x|By_UW)r6kOy$2%bsrfZ4YoH0mu{)C!_Euq-JwkqlU8gi7 z^7?K6+wF!4Y-trC`HspWA2+q_syO0pC$)SS}J}W-7-|6pjT7% zkptXqfi13vK+@wYopzi2CLtT%hG&N2ugDiHQWPBw_oeKzz+$UE4h-)2P5ySTdhw=l zJgrlvl6I@7bA^$W#(L_U8+l4*FW>WqJAOcp?}YTbo2LlRP$4{P|KrQ ziJEXmz1{m;=AOgdk2z&YVBY2#R`*Fg$b=f9d)T=1-1Xznvm3~@m8bR6gu+U@+0m1( zTMaoBI{p~fkQfvRR*)+Ep#3iaWhI0JTo;*$ItCn{bg)j%G5@+|S}dg0a#;K~^8?b` ze{ChnG`}N~57;HT*)}V4)vhEkP zlT0%w%$WJXKcdLa`0xx9h6XU1$n7IOSgoe zvwy+ky!r+YvJ$$u&#Y~2d%%Jj04Z^_nM3@K16RfR6WM*l&4f-+!HiW13{V~N$cn+} z$qRo0Xt8}IVcQ`e{QY$e?Bqhs}ZStKvn%-TbWu zXFLF<)7+VES~`jRGP!?Qc;N-rjHU$V20;3a5EAZz!Tr5h0%qeH>nox{I{C;?iGQ}0 zW_}H~jgF*+BU_qpXZBbr9(aG2H|oxPG%1Zx=e=;rY(QUB+#6}J-TAoT@}dF9+Vwme{!@T^3$trx{W*(X{yQf*FKS`D*KXt4u|lQLH8DJsPX7$Z2*E7vKIDjY(rzjK zpy?;Yh}a7&paAm^xYmfZ%>|dR+5udVuu&J{AtsvHwCmosAgnp{YV~B%74(jy!;(uE z|KmuMFD7oYkH}51Ta=WzI`!?5%~Z(lrxMaqcGp55wUAVn&iV3FT3CfDdp8My=5zd6 zy5?{Exa3qH)o4g+3?T;HZE+#)okGD4r@Owl#+u}o@(R7~*I_pnvY!CAtF*3C zLokl@e#AV5x3OZ#q^PlX9!9SASp?{jUlVS~zk>GzsGBoq@2$#*xEerLe=?!%dul#6 zEjnL7-7+k=J=aigxbJyemedA{FhPMOulS(Z4SA7!;2My;|U zhfpy$^v>5?8ZqYC3N2SAWcuHPe~EKbvJ_OV!Br56nb5UN(kB4N$^XVGLeKERw+kHY zVd;lIw55|WTNo^ez*rXtn~Wp*U~0{&{)lEnW64Z=6LquN!{ee1-n-E@CR_=%02Lgj zKO~5XBxvD*mzF$ANB9balhFPF^0Tg8I`7G-lF|GdN*FWW0REiR5XE`f^vdWFyq2sK zOnLELO*rHJ2zA0huvkf~SPk`GtmLy5AZvZue@{qXFLTw{qpALc@TbHabFM%fb1r-( z3OGP-Kr?Juvj&)V~EufBN6Uvyi3Qo6V{TS0HjlzVT8)Xozwz)s;ItHm7_bWZ{@#9CV@lQ^j zulQh=>lUV0k9$W?5qtJwKi`GFR79b+jaQNbOcuV%c``N<=%^Eh(dKC(OCQ}v1RR*M zuiSAe`jRDpS8^zm!(S9do={%DE3p_&W_JxY@lHOEZY#OXF^nKe*FU{s8QM=vXj59k z`EZOa>MEs0tgj<{sCn5T7{+;eLDKN4yMl+z6XkeKgYHER0Q23KlJBBxePZS5)}zL$ zi)8qEOuxH+Ra=Ss{6;QBXaJ2C-lme;q&sgq48a(Vi0#gA$gWal*>b5*CMNoRnde=) zv48V&5I8#d&#sDV` zg9HSw-(@2mr(@>K=LvET2{t-Y$$J#a47j{^6)n!|R)P=%`QmR|UuI(*6!x;_^Kl$l zm8s#5OH>TZx6nH?2;vQl(laboKV7?E4A&GyGXU)P2~qRB(26m5(9+0N0UMmzEnQ!k z!2z(J?7BxDrM27;+f=+daz_SR%?y4JHgK(D#8w&=vLtIogw3fnjnjBZ?qYt|7qr;4Xech>Z zJrwVgo3(at9=`|NW0K(q9(9j3FOD{x2alZ_i#!XtfD3`W22aRGI>~^2WJd*#d`;Oj z)V=U#msJg;)0^WfZ#@X8ecXWAt1t%mVtS;t??th$yifqD!8=8;Q^Y_z91Hafx|7?6 znW*YMg+`dX-8f#pF&5x5?rbmX&q9Z^4rIG)`_)-TOBOB753`=#^?(0K1fNvs2+r&J z0|WG2!UU$z-&qbiQ$j|u{7^O_d1oU4nsE{d+ggzm6VN0*{m!WW!k~4j{)(DWiOc&W1R~*%8!MPoEN|x%~pQUy2U_iTfDgqoXR#EW0EY^~k7wd-|1lC*KiLct%x~`~w z@+etNBH+4xv1n7`DDqu>b0N|*sF@rMeXplfU1!I?4UFI9g8>{R z`cNhG8tr8I4?G-Zh5buw4stV}hrmB8D_Z#+{>x8!;$M%bbUV3;BFtagg5OXzR)9p`{_FMPFiaXU0W|jHRUf<^!{mN;p%Uw_2k&qpL zcYSK3>&vgbo=Nq4>dY{=ggtjBXos*c2jCiKTxV&Qbj;kStX(PqR9A%Njp(iT@XXBT;$Nt9Oa+LCPJzYFb5J8Dn(y4ZKOF+VGbHL>|_6gW>Ep? z?^jQpShEca@tl(v#u$WIh<}fzz?F6_dTZPpN;P1ES$WX8=kX~w;%()Q4iNvgaEpue zcRwh^)41haLSLyDW03+GfY(VaPffVfIFGSBIC5F=k4J2o|BIEqxvbIPMr1}&(kbD~ zl1>eFM3JAyJ9=wl)iN^9HE%3xrzc@`eu2KMZ8BGp`x(!ojae2^R-1fQEM=gVeCsKt z8{3UG#WK62yOoIz#IP4;MG^TPVZb2GXokcNH+$w-%B_Gd1(%vA`wJ!)qg{V(`{}N$ z+ZuMxKiq$9&lfTFp{MctJ!D+{+dk<~8#NpIUXGwu)An~YxU;kD-h;hi*E^*Q&n0rQ zWqPto(JHhE#)Yi$Z_9tdkge=qyBijHn_YBj;JFI#(^a)%t8W2e$Q)JBec5=GrxJ<# zG#z_BUv}MJXC&@>aH+jy37NYwIW1#FWDgr~Ad`g3pno%J?EII$IbM{s(YZW!8uHJ| zh&y-gkoE{R{KC6G{@;+lU6>=bS#c!~!h!GZm7pSq@^B)c!#^DvG-JC;gqZPo8wfBt zrTk0gYeJ$PztgPbK9Tx6Nc_jokjclyaN9pB8YSWY{euZSMpGhnkK5n#o4SOUP%dro zOT+JDIt%G>jK{wAB>{lCRGZt{tLc^R$Lgoc`A14 z+`kzD|I5-GZ0z=A;|wCR3H~~e^L_v7XT?cVOp$suJUI-^^29b z9b?(|@87>XtO%)MXZvB!`eLB9V|q{o`)ShGXUqa2esT3t_M^oAHwXPQj-~wvGreg+ z9q?7}-OZyqzW)CtKHeGY632v7^P<7Rcl}!2E{h8adFKC{DL=C%h&YIgn+zoj@9#rf z{;O2N#bt<$=bLxX%yy`FK6HL5z3sQDw#<0iy2bfna|LbNo-M8o%AcLJF3)zGtCO zcEV-r-amf47x!t4ZIHs}?phO-%zyVB^wT?RL^dfEOv?&Mq5JK4?}i}ePmg~8T?9mg z>pi9ZFY3R9z~Z75OvW6W2Xqv12n-B_hMiRwjxf9vf`4`u-ex}bQc*#hDtNT%zEZ8`zEVt?rZTvr83prZYzGdhMtj(y=>b#IYvIoQuH3#=%xnr)| z|D}%bdF)*~<1?oW4Ak{TS38ctp~4&wmo=`}?8&Tft2u#R`taAkD-do@M4{l5BqLx4 zK;m*AbwaB-n3^_{%!C5vY}#eM=K zzmLY+P5pmZqwKN&YrXKk=u?ePj{|3C?|2a^+=Mu(`da_&?CUil!n;fN_lY7@Jv=;g z(YIZ%#{-LP%bZk7ua~Fn(>!7!s^a z)DpGX^UfV)FCHIr@OKK_6Gc0IX2xyit@?sDCzAhxAkJqiy(b&@{>P<}QRgCJG9_@@+F?B3`{#sDfQI zl`lA?D(P$YyiFs=ysl+3Tl)Wb0h*Y6%YU)om^N5@Td3NR^#lD`xiwxmg>aOm#Pv57 zFQ(pS)M|tL>;vcaXJu*l*%vb2Zj-W0PWFPq%&8;ch=^7CtIFR5D{1NpP3`3O6S`62 z;TLT0d_jO0j~~pTJjJa|u{zZhc_V?hCN;LA?r507&4p+@nc7S{wxN_~lO7}D;OU71 z|CreRA6tZKu{pV+8bXAItYjor2gJh~pD3MZE3iBndb_9ebWnoN*+^+qDE>ol@3I5t zd%cu|_K33m=`WrqOAX3Q*#lcE{iCZIL5FH?w?C7tY>o(j;J-swGI;sBwsA1~LD28I zi@o}I6cLX=boZodp7qplN?J`%)eXuQ!?q@J0J>rMF<6*o-Z74%fAR5N?^L%@TE@wS z#J2|il^QvWr?tgre3f@bS^y9ty>!mPCw~LTtLppZFY_PL~qAMLvA~b&XGUgz9LW<{-%&hRo@B zVz710TFS5KH#+b7_F@h|LqYoL2%?pO+2p2kj?}_NlMr@X+z+K zF5^T zA2~Nll-&&Jqf|Y9=QCQr82&lR{yJC`-VwX+RZK+c6lY1nIC%c%K2hzj`h78fqy?a` zVc-LAW0x{wuN+8i+cNZhn~ZDKN^Q2Eo;QWKTQLg9fT5rEka}zT)7qs0$IqEIOoV4g&MXm=fRWRCAfrA* z3VZPbuPZ1qW}DI~x(Dig#*dALcuOn(C)p}b2Ls2dySmjfTU@nU*UKS1NkYtBfpaVE z{)nG|7iA!SV)6s^wN8?VZNz)2ls?LjSuw*{B_`rUJP>PUFNRYfKyUc+xFDQGBm7(O z>TpBrd0R6UY4SxRPmY*GK(m9|siY zI+OP;^XFx{V+5;8q$lcmL|wyeyUr>=@)*nD=whXuPiv>{`X3|*GmFmru30vQOJy+* z5A7x+aIDwkdT;WCp7V74Iu0dcMSm2_6y}L zk-Xq5SBKyM{M7vQbFHUkMXSHn2f)8jM(dX#nXzEgIp@=c+X{eAM508pImt% zhV=q#LTq{;b_G8o%zZoMg~{?YrHo4VB}HTef7K~paoVb5ORJr3H0rBY;3x&zP`3`!-R%d&bpH<2%1!{&bV2%9SrR2^Y~cZ@*!XHi0tX&56~E4 zWfY@L3*~^in}U|s4~x4lO79d;H*XQjE>Xs(7cDex`;5OWS1hp88$DPW~Ahs@CRA8odVK(2< zr@M$cn{U&HAk%yDkIapM0akRFP3^`7A}9!RaeNV?H}y!K}Uv z^crtcjl6lwoLa|!C_Cy`F05=evG>)i_2&xff_LH@!Ex?P3CR zyr{?*VC3>1p)5Z$J5#4FWh+~F`eHQ2XH*`1G3d05r124|Ui2LJrO^F9+r7jO9K2$Q zD^y&QqmMtROcu~8iAB$JZx@fIh6>jD(+Ux+Q(+`EqD1oU=|w<>Wu8C4h`la@<($0> zoMFl2XxrB~qq&`y*a7Xx>E&dJ_L#pt_8cfIs6_!IsxnR<^$OUc>G6cILHBhVC62rbBPEc@Yjof`67%Db03c5Dxas`CH=aFa5j^M#U& z?DcMSxn6E$2kYpNGNk{RH3CCS>cX-4-)T3UzwFt1L(J+~+pSwk<&NPBV~e5^f1xC= zOURQ&k?c!j0%3l3e^f|~kd@1@uu&h#{H+)<#4d~bPRuhS{z=$!BB$PeKKfTWA**dU z!I^*v9qxOcur~no-9De~SuS7`1f_4DcZCh_=dDM<;D~!sG9lhV@Y=1lvMk4`1bi={ z@<8S`q$bl@^#P1*rd!LfmCbfi<5ZenzFsi_x?X8|HQ3fKdG#8n=haz61`FjjeXx6s z`vg<^m*`C)2n7~XITP=u*Zk0b=O}u7a_^$QbC(}<$>m0;8t=H&WFdE`5@rK@Ha{ry z5fVa?AL~Add+}Prj$#vK-&rhiHJ7!-N81}-qt%bXUXz254^8gh=`M^;GxIXQ^FqN-U9)JuT*Mol~>e9A{u}1}NXjXeD3a$o}&a5w=!;{6a0&aeDP*_wX zcM&pP$yt{p1>A*ywkIp(^Ph2#e)eNeWs+1Y!;Aggcd=0$?SGZm%subwDJ2T>A#T;a z^X=tp(XY*Jh=m=fj{(I9-P!%(keo?^3U$Vk0PFTCMK+b^(d|$x<3nWL{0~2@SS(pq zebZ+@0OZ?>W>*_H)MxuJ)hl8e-181VDx0~5uvTcab?)l>FF;1vgC=?MH^E@INtg5h zRD)#POx0A%a3m~S?z2!LbY#jemw;Sox=c#~AK5aqp#GFC^)Pb6G*mKja*e3LtgOmm zy1t}(;FA$I-Kr3|D-m=>LJ?NbL&QZl-H>MBj7et^Ciy<3V%tIc1v=!5dxs|!|Mx(B1Zp9kg# zysX|;KjMG)7?NdI31YL^Jug4buvzv0gL;fNE4pca${ zIA=9_%3I@RCR6XcNq=qqycka*WI5m!L6gT<9O$1$aj5sQULMFSDd7c8dPGW`wW`pT z@Qz2!G^zQr!kwD}WCJPwfMf8Y!4~&#(wSv0WVTaDLO>SaTtHFMo~tJSfYMRv?;H5X zR-3mb7yKqz4dKIks0PtE5$caRt9@qEa85-CuD3sH`maYuv9)zM{aWh+-DDdh!4q{X z2+h!f@L6-w*l#C;g`YZOEerLN8%ieMWm@OoOlnDu8J8@mEwVdasPkWq2`luH6Vsc* zLbtks6zf&uKe1R@ZT}!;ctO~=(x>3!o5@1)(I~*05k6CuHsHOofD}Zea%aRiq2R-j zBZAKiUFc9YiG&=5!faEm%r>-9^Me}6S$#FD?@P&@3mVQL$Ec%Ez9-PX%9vfy=3l-T zBEQ-qjRS)cI0yT%vvQT`5Aka7Q0@rax>fo4h|iZ)?<987$kzpeNx0p?fgF z5qB;72yTNAd9LE+X^Aw0MyFl@-LxA2 z8e6Z+_GG68)%cDt&^PS~O~_46NOXkMd1~?lZFoY9V3C?p_!RmebrfFIZ)jdLzP6zF zq$~vAB}($+$F@wb%RKkl7&A*A2Q)7eD7h6h;Ckcr1wKSC4qXXs=rm#w!=(+Mv-uy8UP(qU)nNMN|FfRyl-TDkB8psG}W0p z>CJ9&tzUQ;#xK$1Gt|2pp+QkDpgGXS-LU0s-6i9&SQGM_nC`(&L^siGx3CQM)I>={ zcc7uCa|wR5VetX$oDo&I0pu2la@`=0o;@TQn5V7YElD3=C?vfVL6X@C#8ex-(6X)` zs4Mz`vLJNCaeR#`%ue-e|3dMb2O#;WQfc=H!b));_6I{98^AQWIQAFm6`cDhQdUww zM@J`6=eZVSP%0aLOZmnd6!t5}o#J9D<$JZILK;y@`Zk#th z^08o`&~v<~5|`Ui~=i$YpeS6_zCz+M4vVDk*4U_&1r;W9vSw zAB^vKBpIov-ct(?Z1pAt*W3IdmZyP-^+$Q?j@SPRCoXOQo4N_J8!yIvLEx8cKeTfN zYWBtm8rsZV24{PANg=iGD8q?RM_jUH#ehCCFFbb4IL9bj-!Ba3qRDElx*ZuU|8S+P zZAk}gMv_3WzztZf2EJ>enSfD4e{S#P8^eijy=@_u{qW=EE~AHr?;+O{h2ZgLw0+GC zTayG9u=KNDzD!H6825E-s5)_^ImCxBX1?|_L1vIrf!8U~mU(*LNAcbh@u@~!HU46M z_@_I=Q>Km>MqWAnt-70XWhA-7mw5W>QX3w&p~s5DgvLd#7b<1)Hi(s!n=;Qc8-gj7 z#ZswYz??IE+22aJYOjZO=W18E5?9t?iNL89Wt#&y_j`J-m0H|e9b0-75&doDJF)fX z$6+6rYnE9o)P~N4`(0r+nb?H?`ucCf#L%Jqo;6@@N|qep2;OMQI&683Z|BYy(@9!W z+Bg%|5AaRP>SoYC8}QO|6zM1ZS7*gf8b%AT5-p_ZVix@qAT-&8as}LDjwnMsX@lKOchm;rt~4U8h=n$ z21oqvY=i_j_B$ckVu3h6v*k-oCixs)))FvAzH1u|9xZk7)~-1MFf86oA3BBv+~7MH zo4UEP5{Uz2!tmlC)0O{v>^Y4zz80ja7**@oDkz6&^q?*7=)=#?=%locqdlBNj?2IYBSAhX2AZDda7O zHW=>QylO+wZqhl2xHVo#Vu7u3`=~($m6$nRy~$L~1Fs85yxQAADW-9YAH{gD%M=(@Vrm8k8nSS5jhs@E)7p51(H6Mpt^U=rp1X%Ea-hs&pl+ zU?Y=6G+Q@geP#$-3v!_M*@8Ql6GxTlbvV8|e-(h9Vha)@SBYlFX}r&pHGM<5;lYNQ z_h*=-PUqyP;ryhW7j>8Q_2K9@%AAX@|5BU}T`vngCCVys!IW@v<|lT) zFEeF08Evo3d|+t!tBiD5S~S&Nz&2R?xcB4D0W^oInlJd2{uAmnZkW1byV?vhklkKjktT;K~C7F62igrw$IRCRPUk45Ql;w!tr*R+#bE`|NG^ zCm5K3K;YKLF=;H;$t*L@^$~835-x6H1wfC1Ljscx?z;0WWlBw_TGq%-OBLc@shgkD z9gAD}l9lPF9&AW|<$t|rKVKhwKk5>vas;R?zyi8w;7dCo-D>dT@C^aqLL0rA-VXrI zlrq;uiof#<2I>`u1Ct^2ZuRICG4+PyY9dMnpU7PQFe8!7%?OrK>l<~KSSyj~t56Vc z(J?}|`Lj}^uoUp2@>XrolKv2nxg>~dB*rIb^~FI#bB1vOo zJ8yPn8(V`O$*JLeLUzkO2zd4_;zs{D6q#3Hvb8FTFD?R z+{gFU(MteTde`L7N38SqqyE0pQQ0Dh2^al3_+w9GLE>DiUH``sJ2`+hUZelXz4pO) z*R@Xd?ozXj_FeuTMrgfHjY2$!VgYT(>W;7&-esSbc^=Hi#s7xgJ6kSyCCfPne^(CoSsp7K4hkD=`Ewd$PydHn6)Qn`XLSSe{dC0)1wVmjHLDbBTrjiTmpnT+=_Dz zBr`jtuN@4e59q)weS_Y$6B^t}zPMAM6xua8>=+PplsGKf^%OVFbE@dt{>8it`z-Y7 zeMr>&<{;27N$&W=MG-F^>4P^H(xf;_jv#Oy?vRy@xW&74i|&xwz({l794cdy(*^01 zJn+=`vTa$sox*GIlb#GY(m~^(_+PnZw?9WA1p6+N?{)H`5EU}LoF{160K~%hYk8W8 zNRnz@eDGZa?1HSTuJ0W69%`l^gf%q=dq$3dw0&Vr>jPi5gYCAeH#*24^6=2~WDXm9 zaojLTt1ouAVr-9uXkpzn8@g>uVJc!{Lcn5WP(eY=i`{3+^iC(VZZO)G5^DQTTe+Wn zZF(jRE)5W7;N4-avRO@FVm-|E>y8adNxsv~$J1v7r%Z5Qx-BSX_n<8*unJ7fP zIknzrG`5~1`uh6Q$7Qzy$AgHHY6!Refh7leu7`Fgxw#7aSrsi~6oOy+KKJ!0Y2^_Pt?^^3&*Pj{C3%~gCTxzBlHm|j7&qr7 zo&CB((Sw3a%fVM4POlFj{H;Pbi=6}qF{8zo{1=g^md+fyAwjHuAb~)tPn{VzSiCtx zYTCnw5{B=Xv%j-XHcs*!Ws~VTO54{Z4pV54CIcW zJG*fsEeooz9UzPk#6|lDysb_;e=iv<D-Su>`bNInK<+qIUr7AnUz8Iu z;fJ%&<5{5QJ9H#7^K(+#k42qK%o-&j7nEgQjvQuR&AXAEbL(%_1~0kg6dUy8GD%6_ z##zx!Xi^#CLh%uUF`!hYPX)D0j^6Mn4G=`gw|x(?`qIH znYe4Xf2Bv$Et0S*r97&B`PooJ}N0DYaPhsX_ov=p>s- z_W9hWk*jg}A&g9%xciEU(Z+Yq0(r|De*ZX~uxKdM6l9D3Mc<*)C=5fE$0LFIbt9 zKFzUvE+J*i3&49N019u1*Pe4sF7;;}fgf!1Z2Ua@Ok;16SKz~%FDbfu1Kztz#G7F8 z*#_y%ik)S-pEGhhne(T(U!=D*nh2nLNAmO>`l@#G{nQ67=KZbnI;2_v_gUmD1*oO7 zT0fk>RnL@yZ|a^B+wSImde>LUIH&$zYfQc*uol+NZ(-eF4I<6lJn~n;SH14CTR5do zO)YGh1dQLnTL7hXM_LlIfg~nwzs$6k8_2SWyn1TIj<&E zwLY0voX?;9uwY#C)hDxrfqb4wG#d7C(&2QML)tGibN@xdzbQ)Sx^3{K_kynm{Sk(x z8ANdv2cGHqCz^FF&q-cM%?$x#z$0W&|-fTj@1=<@`7? zO%y-y>ePc+8NBkynpp84_75J=aBGcqo8Mrf9!|Scb+dB5RH;v4;Q1*f6O|VU1&E;O zuCqn&4(OoBdh|{tz#A**xCK#GWU|@H0z@FsK1Z6ks-f#AgiY8Stu1tjFL&*xX&}P@rDe<6{ zd%?l|reagQbvKnat{?)okP63*CsqkR5cqO!FXB>?^8d4&a@+be?*_G)Tt0^vKs+sPa zqmUosfN~J2(v%Syc6vIK6LGhjZGRZ>ffE6F9~|E7YX5(Q07-kw3tQYC78(rq1rE&d zaQySCOG@bz9zB8V!o;t&)B2-vMToM9l^u|zv17ysC{~&W<+QW0iY^aB=qc(c&l<)) zhAkJTmz?F2uq0dMdK|7{6t=tJ&qejK0530~!rOyGZ5k^m zyxVJ0_w_e%q&IOt@-Ib4*#3k$$Rl`I2K%mYeD*sVTk1k#XMS=BRF=a#ZN#*JYfG=S zJ&9>u1u`a1>B(V~;kQI~`w5EjR%|5`QQf+Bw@o8Q)z474{5bC`CjCEp`?5d1m zL~W!nM?SCJ#5E$n6Qrq<7HIH_oYNXLtoMk)G2>%XF%1tesagb)bNsJdnQ9B+WekDV`_9W(gQB1*dDmqgfl*pj6QjJVk*38VFx#Ygbx7>YOhTst~+U z=-CcCG(Z5knASd6s|ycniy8*l^*hn7PY)~MIz7jc2K;j;&IN7CB2pz6&isf~B`w!d zWpcJo<{{;LJ1#k>W7`en)%J|~mbt?IZOy#*N!ag-4DV8Cz?C5L4pl-arR2>SsKib| zn!Rl@Wt698dmNK5F%w}ejD7#;k2=_{;i6k#rCi$_(hb8v-pYseYCtLrVK!1G?8DB1 zpR2W{S!=M5pX)|X8R#^FPi{WhniiI&q(o&78ZK;Y$SDzF0&d*zbd1McZUtv59crtW zQ(|T^jp^8+sd$a#eVW|Kt0o81ZwF!5Sg>6?c?E#=2R;F`(ig%5r5&GdQay%Wa4JAL z`I`-hZWrQ4=KGb*l-1BQf2ZYpQ4WPBhF&ddO^+wH0Zl+OjUXY&z0X)#^quYLg~MUP zwfXOuIpOObisb>qG?{YZqYd|ZyiT8Zppz@KJDXf+(2pgt#Ys_(b6+`v%M+^`m`ntm zY)o$hdna`-l#+GaH)rW)z=oC!{OLl2o?cRT*t8g071YS}!*SJZyAu{#Z?iuIl0+N@ zPQL7J1Cj_{{PknHdsz27N8FvP6g_R$TUTZ}JYBkF+$S>x?qoKjI0x32yywFzXql$G zMmP!tq$WJvX<~*QiYE&+h}=(dQYQj^HIGxBgr2%bpT%<jxqIbR~b zkC-Yqj`O?C^TV#?Z*C9!@^jEgkw>v~LPibuj< z&`yd;YOllu`kp9z+@{{9jNN=GJjATACRB|-M9#wy5U1=bod0$4_p zh(8a!4F421x5PsfSQ*(xKYn|SW4*YsO?X7|adB3b8=Ge)0X|EakQ^WX8tFc=pZERa zKI{C$NRH4!dp0zO4F~4EYgpgq z8$^HYkHQGASi7%tbW%bvOgDP8Fa>~fm6qV1T37%aA zH+55TD|}%clHi0m5!x$+7l%!4BruK`mKQnw@T?`S0R@M-ET>mqcr9MTIiu0dbvYNkM_3pKF&wPTy@ zNrR6sD=h&t1KpJ7@~YjBAPpAl!%1o&$*jZ#LeHu0nqia=n~ZbUz`t1YWfhoExS8Ev zAoP_YbplRy?iu4%PDVn0c%8}A75MQ4o#QcW7fcY;Fc2+|83@e%t9t7Tm^S$3_i#{M zcRBO4(LPSCuN7uO2X&Y<^kqFv(4&S(R&QAxs0mn$xAqBr86jz6t5~7)suJs^!_x}8 z!G7cXw^&cqx~MH6&2K2NU`!jtN5WHS6HPYPD~+$fcsaG6?SrQDI%${_+AT!-J<7PQ zkD(WXeFg%!^oOlf`(DZ>vo!_g+MH=06^%Q#^^q(PX%7L9{UHhNHkvcJm}vZkqECQd zF$*2ZJWTimx`02)vESjScXz5@2kxZJ8Dx@%eM-qIlF|@fo|<~Oi`J+cMzHzfnNFiS zYAS|4FtQ`-<@ZAdFrC1KbH6%c--@itUNN*<{CUc>h05?ttAx*B9HvG`>(p#dnf zquh6YjNZ?OUSBH6m)w8r#18d!Ya5#d(>EKVeszoQuW2?{)hMvd;TzZ2hjz@F=_m;{yD~|vm)Y6rS}u#WfcXSrC*0|@A8P~o6MHY?VjSV@E;?5- zn~c*jwaN3G8O&bAK!=Q%ts5_gGi%IdX14b|xtB*;)!|DzV_2H>3V46c)Gx&K>BC7Han5hbzweX~mFEoraFmd- z*Dwj{+34C*{~c#mo)8y!6LT8E?!T%$sGi}8m(|LtrAX3qA}rNfJ~Hk2^2F4qjuYv& zM>C_84E4H7I4&nqH|qH$7kVuaeW9~U3|{0Yl_T+(#`Ey6-`k&aISd~`)LJ4a?J>do zU^b&o))x31dQlc)Yh(4J)gBQC{&xBD_*f($kz<(~(7B*%U_i@z00XS_rl+y8WQ;NXOX8-j-Ei@V?D;bZJ~N(G0k#DmX8h-^kj zOd&ogPnj~Uas9c*QI?A=Q`V-mPKG`CMi^lSFy(^dC)KA9N#DCL7h!9~`8{AFOSZ&l zM~Ri;887Q!WwIj82^FOd$&FuaLrL0()bBH-Z91XC9tl;!|6%RD-P^Z zLlY3`Aksm)2-2h_Kqx_wl0ZPJQl*FpNE2zHcM_UN?;wQUQ|O>b4I(1aLH6?YdA{#` z_pyK6e?X44*34wCnRCu_F78Q7f8-mYCrO~V50|z7^ysAhJje27*B;l50zhYtD>Ij1 zXt{8`CwCNSkn!iMyv%rA;CAYlwBujWPw~CC*-eN{tVnmP?zj1>b>i z-u`Og8h?Ja&im0$LG-+Mo+imyU+K|bT9Nu-A`gkY1dyxMuPc^PQ|q6tz*;maTE91Q z-IH~RU(YH(=o=czY!X4n3o21nnm$W=#G0Yjo;dY#A6ES|%vhYSd2PLk%jK&NKoEa> z`$eZ~EfboYH@`&8x6|+Nj%*adyo@Ou_ELs`)F2c`E1G?6|O$k2Ef0cEn;{dcM_P+A?<3F6>d}h+otua*XQp z5E=}qsD7VRChXXbMU$EQLuNw8@twCo2FO=00aK!!3t}0S-#bgYzDq-UfBf=R^UwJv zD|hgg20#)9dxREv#J+r{=X%Fo!&gYYR7}FpFAmN=kv`EsRUMH5X_S3MzRJ5 zowBlEKxXtWDPRA4)24nan(eb zYjJH=souF;?flTkW${k4&lYjbYI`?{TBXpeb)^zos}1nAvUTMzyPtP=NtEy|Pj0{awIsy9yv+Szi}h%p<2=9mU%VgCxU^=joUl)K zM_rX$?+AOU(ICk>?cXu2{~_>T-t^D5nGUjR@6Mh{;|IVWZU0QJzrE!@EQRPJTCX7* z18S<3wESO0Dt{F2i1zh623FxXr_D8Aa#Lbs;QQ}T9gw_94E|EMXPJdWkgV@-hR0v{ zq;r~JPxhN-Zqu5g4}hbRdzHzpG~dR1zR&o-Z}tuOY8#t8{SR&1^SPJjQpbw7fh9XE z_W{MlU{v7#wCOeZbsNICvc`?`?!&nMGr<7%lOTmFx3NDIzb-4CtRKWS+-K$bD>qSi zW~`R+$orP7q>S7KH`}E@D|7yvI=u!{%TO8kZI)u9?#4mvtN1USrflYQWz>_QR?R-; zbAua7i6?!)Sg(v3_T_ilv$6lF6%2nSXKjcwDqdlw!0YXppHU13qOTLXkApG1_; z?VtU(xc%?*SlD_w>$Cp1bPafu_%Hu|>y7?>g$ooMH~xpD^6xj3_s)HL%bDZ14g1|r z#~tW6G#x4?7`<<0(|OtCi-Jz=1*-M1r%viJv?nj!`5o zsDPy_(Ux^;^Z z+!|_U{%bYvnJ3|)j=$w4Rd(!>9L|7WZ3_n|&G0S%ajUj(O`_Mc8)?wEE2jbW4c2ZDV#I+=n z>tZ?x{dneHU*M|g{&|uCh#EN9qkSXWFZy%j238vcqeLcpLujqDt>@q4`aJO11N{#% zc9-2*vJM7^`kqVd?L?>`=x}dCEa#|~m#Qw{e16ST)Y@n6smzp(jG|fAduU%zMqlK# zMODo?le-&BctxIdZu4x>?_w?(cbzCLru>|d%9M90ju{%(^~QBRs*QYNU7VHCL#}J* zu?M%d3bYD3c%YcW>{g#)3ps@H4|bleUTY&$@_|w7iwuqKo@M_VU&1Kg=elyrYq26> zl@Y13r}(c1FR4jqf8J~8O55w4Nb@14P{`2j_d*~GvzJ(p8N0>$^{U$Hk9?ZTz2q%D z)|nw@65h~nWlgP=45K!Js?NY=HMjO#**fU_i)5AoML$R$j#`AJJgGXAA zbX~uut)TG*!zzKsyn59|jW2Fqc5l44+)a+EaYjye2g6w>-peZ&JLIq`M@?_$&ToD~ zeO5P92xvXYsk7n4&ux}N&XyJ3I`CRwi_92_(>2Z{dDlESG6ru6W8PvpvMoZL1>aJc zB~`a@7RJyNma?d5NDK9PZ?ucKqR8xbntdiVWV!F!)9yB*}Ac zn2^!}LCg=G)|r4V3yKegGR)o)X5!MiQk*_wIUK^f*P5_T2~}`S28Vj%jwR)gcq2|q zI>#pavXLjF@t3#K?p0>ghYAnwH=XixZY<%~>y@Jlwl%9n{3jx8e%s=QwCv_F3CBF$ z9&>#AEf+BQJegaP(s${+tCL^&I=l~sK_rdDtTwH$as<>vv_p2Ko(iBZ;6M|S&ZuY^ z!juX|Zb=&8hFm`w({QW1(RGim2FL0%CmfV`;2^#!dTQNs+2H3E22nUM$oi)Cjq_px ztrvTX0K_m~6qj=Hm^EEpAK zV)&yduJ`EpXh0)s)Qo+sA`}%e6=%0TdE#Ck_o6I=SWNEGW(xa>$X1hDb!e*q3oXC1 z?Ha1fMm{T6qEBYRNG9jLyE%{}+O9~{@*ha;)jpbmxXj;`xEv%Fd1c2){le1FNP$re zvz&Z{tt)BOb+r`|;E<6y$_gIvw$1>Z?WeFFwd%R8hV!FFZm)~k@@yoZ*EChs`PEw zWRO!{RfvgaHxjih!}A>tcM~0*h_Js!0L>KF6OWC2?NYp`CV1(uvdvL&k`vfSO$LOIO}egA9m zhr^+9=j<5L@j1`kIY*jykH5;;r{Y6jhR}^a(kOZ}jk4qlvn75t;7>5##h;wUo+~0?SBv%P3rD?aA+@u!4Y-EMU@5MZjz^sp)={-j`v8Pi*Ym6)um6-g9V})g~s`OFU?D~txBWSj8 z7S4eJ{0qP4Bcw}?j$f*LEv!6e%i!vzUalDlhVP8tskl<)4XInZgE938dJLj85b3$< zaBJU=46Il8ELXt2bV@~kQ@H1}Mu`xMm-?_3PAO7nOdm>)E1n zDv4rg!K1NhnBF;Q5K~#*yWWpy;@$S$D?u4+W?24lMA3>3h;oy`WVji*gUoBWb|jL6 zbTR=C=?#`7TXs&q*wpHCgD=+{mX!zb(^LJx=;$*L7_f79){X>4icNlcW&Qg%5GCB% zJs_8A+GC1-dG1=9z!liA`aB=_Qou}rBY5^AhDsDC*>bcyJ#VI+33AtRQ<%x^Q+ZdH zWgF8sfdR)7lfK7v&*nsiysay>z8$?K?K)Y5M6n(u@C`d*XjGe6oYs1)
C=pvM9 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

F@0bL@Dyk8^AHtd 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≺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%0Ad4G1C)Y1P>98o^e3>7qX0J@+>*E}b4$n+Jqi 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 AtM1NyDGw|3e$nt|@>cqH1*%41pwm^&Fjn_COc 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 bF1>G5E9iD4f2h9!{K!fxy(@ZS@csV*ok2fX diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index 5ebb5d5b0a8..b4798561121 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -27,7 +27,7 @@ yarn global add @pnp/cli-microsoft365 ## Getting started -Start, by configuring CLI for Microsoft 365 to your preferences. Configuration includes specifying an Entra app registration that the CLI should use. You can choose between using an existing app registration or creating a new one. To configure the CLI, run the [setup](./cmd/setup) command: +Start, by configuring CLI for Microsoft 365 to your preferences. Configuration includes specifying an Microsoft Entra application registration that the CLI should use. You can choose between using an existing application registration or creating a new one. To configure the CLI, run the [setup](./cmd/setup) command: ```sh m365 setup diff --git a/docs/docs/user-guide/connecting-microsoft-365.mdx b/docs/docs/user-guide/connecting-microsoft-365.mdx index 5a51a8bc308..593dfc39a16 100644 --- a/docs/docs/user-guide/connecting-microsoft-365.mdx +++ b/docs/docs/user-guide/connecting-microsoft-365.mdx @@ -24,13 +24,21 @@ CLI for Microsoft 365 offers you a number of ways to log in to Microsoft 365. The default way to log in to Microsoft 365 using the CLI for Microsoft 365 is through the device code flow. To log in to Microsoft 365, use the `login` command. -After executing the `login` command, you will be prompted to navigate to _https://aka.ms/devicelogin_ in your web browser and enter the login code presented to you by the CLI for Microsoft 365 in the command line. After entering the code, you will see the prompt that you are about to authenticate the _PnP Microsoft 365 Management Shell_ application to access your tenant on your behalf. +When logging in, you need to specify the `appId` and `tenant` of your Microsoft Entra application registration. This is required to authenticate your own application registration to access your tenant on your behalf. -If you are using the CLI for Microsoft 365 for the first time, you will be also prompted to verify the permissions you are about to grant the CLI for Microsoft 365. This is referred to as _consent_. +:::info + +If you don't have an application registration yet, you can create one using the [m365 setup](../cmd/setup.mdx) command. This command will guide you through the process of creating a new application registration and granting it the necessary permissions in your tenant. It will also set the `appId` and `tenant` environment variables for you, allowing you to log in to Microsoft 365 without specifying these values when using the `login` command. + +Alternatively, you can manually set the `CLIMICROSOFT365_ENTRAAPPID` and `CLIMICROSOFT365_TENANT` environment variables to avoid specifying the `appId` and `tenant` values during login. + +For more details on creating an application registration or setting the environment variables, refer to the article: [Use your own Microsoft Entra identity](./using-own-identity.mdx). + +::: -[![Granting the CLI for Microsoft 365 the necessary permissions](../images/consent.png)](../images/consent.png) +After executing the `login` command, you will be prompted to navigate to _https://aka.ms/devicelogin_ in your web browser and enter the login code presented to you by the CLI for Microsoft 365 in the command line. After entering the code, you will see the prompt that you are about to authenticate your own application registration to access your tenant on your behalf. After accepting the prompt, you can go back to the CLI for Microsoft 365 and you will be logged. -The device code flow is the recommended approach for command-line tools to authenticate with resources secured with Microsoft Entra ID. Because the authentication process is handled in the browser by Microsoft Entra ID itself, it allows you to benefit of rich security features such as multi-factor authentication or conditional access. The device code flow is interactive and requires user interaction which might be limiting if you want to use the CLI for Microsoft 365 in your continuous deployment setup which is fully automated and doesn't involve user interaction. +The default method to log in to Microsoft 365 using the CLI for Microsoft 365 is through the device code flow, which you can initiate by executing the `login` command. However, if possible, opt for browser authentication as it is more convenient and provides the same security benefits. If you are logging in from a different system (e.g., inside a Docker container, Azure Cloud Shell, etc.) where the CLI cannot open a browser, use the device code flow instead. The device code flow is interactive and requires user interaction, which might be limiting if you want to use the CLI for Microsoft 365 in a fully automated continuous deployment setup that doesn't involve user interaction. #### Log in using user name and password diff --git a/docs/docs/user-guide/manage-microsoft-365-apps.mdx b/docs/docs/user-guide/manage-microsoft-365-apps.mdx index f482b3343b8..f1bb400ce09 100644 --- a/docs/docs/user-guide/manage-microsoft-365-apps.mdx +++ b/docs/docs/user-guide/manage-microsoft-365-apps.mdx @@ -5,9 +5,9 @@ sidebar_position: 14 # Manage Microsoft 365 apps with the CLI for Microsoft 365 -When developing Microsoft 365 apps, you need to register them with the Microsoft cloud. You do this by creating a Microsoft Entra app registration. A Microsoft Entra app registration contains information about your app such as its name, type (for example if it's a client app or a web app) or API permissions. Typically, you manage these settings through the Azure portal. CLI for Microsoft 365 contains a set of commands that simplify managing Microsoft Entra app registrations for your Microsoft 365 apps. What's more, using CLI for Microsoft 365 you can automate create Microsoft Entra apps to allow developers in your team share their configuration without blocking each other. +When developing Microsoft 365 apps, you need to register them with the Microsoft cloud. You do this by creating a Microsoft Entra application registration. An application registration contains information about your app such as its name, type (for example if it's a client app or a web app) or API permissions. Typically, you manage these settings through the Azure portal. CLI for Microsoft 365 contains a set of commands that simplify managing application registrations for your Microsoft 365 apps. What's more, using CLI for Microsoft 365 you can automate create Microsoft Entra apps to allow developers in your team share their configuration without blocking each other. -## Step 1: Create Microsoft Entra app registration +## Step 1: Create Microsoft Entra application registration You start bringing your app to Microsoft 365 by creating an application registration in Microsoft Entra ID. Using CLI for Microsoft 365, you can do this using the `m365 entra app add` command. For example, if you're building a single-page application, you'd execute: @@ -19,27 +19,27 @@ With this one command, CLI for Microsoft 365 will create a new Microsoft Entra a :::tip -There are many settings that you can configure for Microsoft Entra app registrations, so be sure to check the [documentation for the `m365 entra app add` command](../cmd/entra/app/app-add.mdx) for more examples. +There are many settings that you can configure for application registrations, so be sure to check the [documentation for the `m365 entra app add` command](../cmd/entra/app/app-add.mdx) for more examples. ::: -This one-liner is great to share with your dev team so that each developer can create their own app registration that they can manage as they work on the app. If your app's configuration is complex, you can also choose to export the existing manifest and create a new Microsoft Entra app registration from it! But there's more. +This one-liner is great to share with your dev team so that each developer can create their own application registration that they can manage as they work on the app. If your app's configuration is complex, you can also choose to export the existing manifest and create a new application registration from it! But there's more. ## Step 2: Store information about your Microsoft Entra app in your project As you work with Microsoft 365 apps, you'll be creating quite a few application registrations in Microsoft Entra ID. Over time, it might be hard for you to keep track of which one is which and where you need to apply changes. -To help you, CLI for Microsoft 365 offers you two things. First, when creating a Microsoft Entra app registration for your Microsoft 365 app, store a reference to it. You do this, by extending the `m365 entra app add` command with the `--save` flag: +To help you, CLI for Microsoft 365 offers you two things. First, when creating a application registration for your Microsoft 365 app, store a reference to it. You do this, by extending the `m365 entra app add` command with the `--save` flag: ```sh m365 entra app add --name 'My single-page app' --platform spa --redirectUris 'https://myspa.azurewebsites.net,http://localhost' --save ``` -When you use the `--save` flag, CLI for Microsoft 365 will create the `.m365rc.json` file in the current working directory and write to it the ID and name of the newly created Microsoft Entra app registration. If the file exists already, CLI for Microsoft 365 will add the new information to it. That way you can track which Microsoft Entra app registration belongs to your project without having to manually locate them in the Azure Portal! And if you're building complex solutions with multiple Microsoft Entra apps, you can keep track of all of them in one place too! +When you use the `--save` flag, CLI for Microsoft 365 will create the `.m365rc.json` file in the current working directory and write to it the ID and name of the newly created application registration. If the file exists already, CLI for Microsoft 365 will add the new information to it. That way you can track which application registration belongs to your project without having to manually locate them in the Azure Portal! And if you're building complex solutions with multiple Microsoft Entra apps, you can keep track of all of them in one place too! After you stored the reference to your Microsoft Entra apps in your projects, you're ready to use the `app` commands from CLI for Microsoft 365. -## Step 3: Manage Microsoft Entra app registrations for Microsoft 365 apps +## Step 3: Manage Microsoft Entra application registrations for Microsoft 365 apps CLI for Microsoft 365 exposes a set of `app` commands (`m365 app *`) that let you manage your Microsoft 365 app projects. For example, using the [`m365 app permission list`](../cmd/app/permission/permission-list.mdx) command, you can easily retrieve API permissions for your Entra app. diff --git a/docs/docs/user-guide/using-own-identity.mdx b/docs/docs/user-guide/using-own-identity.mdx index a1afbba72ed..25bd307d8d7 100644 --- a/docs/docs/user-guide/using-own-identity.mdx +++ b/docs/docs/user-guide/using-own-identity.mdx @@ -5,13 +5,13 @@ sidebar_position: 10 # Use your own Microsoft Entra identity -When you use the CLI for Microsoft 365 to connect to your tenant for the first time, you are presented with a `Permissions requested` prompt from Azure, by accepting this prompt you are consenting to using the `PnP Microsoft 365 Management Shell` Microsoft Entra application with your tenant as well as the permissions that it requires. +When you use the CLI for Microsoft 365 to connect to your tenant for the first time, you can either start with the [m365 setup](../cmd/setup.mdx) command or define the `appId` and `tenant` options directly during login. -We ask for a wide range to permissions upfront, including permissions that require administrative level consent, so that it is easy to get started with the CLI and try out the commands across many CLI for Microsoft 365 workloads in your tenant without having to handle the complexity of managing the permissions for the different commands manually in Azure. +To simplify the process of creating an Microsoft Entra application registration with all the necessary permission scopes, you can use the [m365 setup](../cmd/setup.mdx) command. This command can set up an application registration with a wide range of permissions, including those that require administrative consent. This makes it easy to get started with the CLI and try out commands across many CLI for Microsoft 365 workloads in your tenant without having to manually manage permissions for different commands in Azure. -Whilst this is fine for working against development and test environments, using these levels of permissions against production environments is inconvenient and administrators are not comfortable with granting such permissions to a multi-tenant application within their environment. +While this approach is convenient for development and test environments, using these levels of permissions against production environments may be inconvenient. Administrators might be uncomfortable granting such permissions to an application within their environment. Therefore, it's important to carefully consider the permissions you grant when setting up your application registration for production use. -In this scenario, administrators will want to provide their own Microsoft Entra app registration to use with the CLI to enable greater control over the permissions that are granted. +In this scenario, administrators will want to provide their own Microsoft Entra application registration to use with the CLI to enable greater control over the permissions that are granted. This tutorial will walk you through how to create your own Microsoft Entra application with permissions restricted to only read information about SharePoint Online Site Collections and how to use this custom application with the CLI for Microsoft 365. @@ -19,11 +19,11 @@ This tutorial will walk you through how to create your own Microsoft Entra appli We first need to register a new Microsoft Entra application in your tenant, to do this we will need to navigate to the [Azure Portal](https://portal.azure.com). -Select `Microsoft Entra ID` from the global menu, select `App registrations` in the Microsoft Entra ID blade and then select the `New registration` action button to open the `Register an application` form. +Select `Microsoft Entra ID` from the global menu, select `app registrations` in the Microsoft Entra ID blade and then select the `New registration` action button to open the `Register an application` form. -[![New app registration](../images/using-own-identity/new-app-registration.png)](../images/using-own-identity/new-app-registration.png) +[![New application registration](../images/using-own-identity/new-app-registration.png)](../images/using-own-identity/new-app-registration.png) -In the form, enter a name for your new application, for the purpose of this tutorial let's use `Custom PnP CLI for Microsoft 365`. You can always change this later if you want. Leave the `Supported account types` and `Redirect URI` values as they are and select the `Register` button at the foot of the form to create your custom application. +In the form, enter a name for your new application, for the purpose of this tutorial let's use `Custom PnP CLI for M365`. You can always change this later if you want. Leave the `Supported account types` and `Redirect URI` values as they are and select the `Register` button at the foot of the form to create your custom application. [![Register an application](../images/using-own-identity/register-an-application.png)](../images/using-own-identity/register-an-application.png) @@ -35,7 +35,7 @@ Take a copy of both the `Application (client) ID` and `Directory (tenant) ID` va ## Configure Authentication settings -We next need to configure our new application so that it can be used with the CLI for Microsoft 365. To do this we need to select `Authentication` in the `Custom PnP CLI for Microsoft 365` blade menu. +We next need to configure our new application so that it can be used with the CLI for Microsoft 365. To do this we need to select `Authentication` in the `Custom PnP CLI for M365` blade menu. This will present you with three sections: `Platform configuration`, `Supported account type` and `Advanced settings`. @@ -65,7 +65,7 @@ To make sure all these changes are applied, select the `Save` button before movi ## Configure API Permissions -Now that we have configured the application to work with the CLI for Microsoft 365, we next need to grant what permissions the CLI will have within our tenant. Select the `API permissions` in the `Custom PnP CLI for Microsoft 365` blade menu. +Now that we have configured the application to work with the CLI for Microsoft 365, we next need to grant what permissions the CLI will have within our tenant. Select the `API permissions` in the `Custom PnP CLI for M365` blade menu. You will see a section called `Configured permissions` with one permission already granted. This is the default permission which allows the application to sign in the user account used when authenticating to the Microsoft Graph. @@ -145,7 +145,7 @@ For the purpose of this tutorial, we will be using the `Device code flow` to int At your terminal session, execute `m365 login` to start the authentication process, a login device code will be displayed along with a link to a web page where it needs to be entered. Navigate to [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin), enter the code into the input field and select `Next`. You will then be presented with either a login screen or accounts that you have already logged in to Microsoft 365 with. Login with or choose the account from the list that you want to use with CLI for Microsoft 365. -You will now be prompted to consent that the custom application, `Custom PnP CLI for Microsoft 365`, can use the two permissions that we configure earlier, `Read items in all site collections` and `Sign you in and read your profile` on your behalf. Select `Accept` to consent and complete the sign-in process. +You will now be prompted to consent that the custom application, `Custom PnP CLI for M365`, can use the two permissions that we configure earlier, `Read items in all site collections` and `Sign you in and read your profile` on your behalf. Select `Accept` to consent and complete the sign-in process. Returning back to your command line, you can now verify that the sign in has been successful by executing the `m365 status` command. From db83805d8badb2eec0aa192168084af0d2487422 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Fri, 27 Sep 2024 20:54:43 +0200 Subject: [PATCH 29/42] Enhances 'graph subscription add' command with additional options. Closes #6158 --- .../graph/subscription/subscription-add.mdx | 35 ++- .../subscription/subscription-add.spec.ts | 268 +++++++++++++++++- .../commands/subscription/subscription-add.ts | 69 ++++- 3 files changed, 353 insertions(+), 19 deletions(-) diff --git a/docs/docs/cmd/graph/subscription/subscription-add.mdx b/docs/docs/cmd/graph/subscription/subscription-add.mdx index e59d7a3b3d4..f15ee5d4788 100644 --- a/docs/docs/cmd/graph/subscription/subscription-add.mdx +++ b/docs/docs/cmd/graph/subscription/subscription-add.mdx @@ -29,6 +29,24 @@ m365 graph subscription add [options] `-s, --clientState [clientState]` : The value of the clientState property sent by the service in each notification. The maximum length is 128 characters. + +`--lifecycleNotificationUrl [lifecycleNotificationUrl]` +: The URL of the endpoint that will receive the lifecycle notifications. This URL must use the HTTPS protocol. + +`--notificationUrlAppId [notificationUrlAppId]` +: The app ID that the subscription service can use to generate the validation token. The value allows the client to validate the authenticity of the notification received. + +`--latestTLSVersion [latestTLSVersion]` +: The latest version of TLS that the notification endpoint supports. Allowed values are `v1_0`, `v1_1`, `v1_2`, `v1_3`. Default is `v1_2`. + +`--includeResourceData` +: When set, the change notifications will include the changed resource data. + +`--encryptionCertificate [encryptionCertificate]` +: A base64-encoded representation of a certificate with a public key used to encrypt resource data in change notifications. Required when using `includeResourceData`. + +`--encryptionCertificateId [encryptionCertificateId]` +: A custom app-provided identifier to help identify the certificate needed to decrypt resource data. Required when using `includeResourceData`. ``` @@ -47,6 +65,13 @@ To subscribe to change notifications of Outlook contacts, events, or messages in - Use the corresponding application permission to subscribe to changes of items in a folder or mailbox of any user in the tenant. - Do not use the Outlook sharing permissions (Contacts.Read.Shared, Calendars.Read.Shared, Mail.Read.Shared, and their read/write counterparts), as they do not support subscribing to change notifications on items in shared or delegated folders. +:::info + +For subscribers whose notification endpoint supports a version lower than the currently recommended version (TLS 1.2), specifying this property by a set timeline allows them to temporarily use their deprecated version of TLS before completing their upgrade to TLS 1.2. +For these subscribers, not setting this property per the timeline would result in subscription operations failing. + +::: + ## Examples Create a subscription @@ -55,16 +80,16 @@ Create a subscription m365 graph subscription add --resource "me/mailFolders('Inbox')/messages" --changeTypes "updated" --notificationUrl "https://webhook.azurewebsites.net/api/send/myNotifyClient" --expirationDateTime "2016-11-20T18:23:45.935Z" --clientState "secretClientState" ``` -Create a subscription on multiple change types +Create a subscription on multiple change types and include resource data ```sh -m365 graph subscription add --resource groups --changeTypes "updated,deleted" --notificationUrl "https://webhook.azurewebsites.net/api/send/myNotifyClient" --expirationDateTime "2016-11-20T18:23:45.935Z" --clientState "secretClientState" +m365 graph subscription add --resource "me/messages" --changeTypes "updated,deleted" --includeResourceData --encryptionCertificate 'Q0xJIGZvciBNaWNyb3NvZnQgMzY1' --encryptionCertificateId 'myCert' --notificationUrl "https://webhook.azurewebsites.net/api/send/myNotifyClient" --expirationDateTime "2016-11-20T18:23:45.935Z" --clientState "secretClientState" ``` Create a subscription using the maximum allowed expiration for Group resources ```sh -m365 graph subscription add --resource groups --changeTypes "updated" --notificationUrl "https://webhook.azurewebsites.net/api/send/myNotifyClient" +m365 graph subscription add --resource groups --changeTypes "updated" --notificationUrl "https://webhook.azurewebsites.net/api/send/myNotifyClient" --lifecycleNotificationUrl "https://webhook.azurewebsites.net/api/send/lifecycleNotifications" ``` Create a subscription for Event Hub location if you are using Key Vault @@ -79,10 +104,10 @@ Create a subscription for Event Hub location if you are using role-based access m365 graph subscription add --resource user --changeTypes "created,updated,deleted" --notificationUrl "EventHub:https://eventHubNamespace.servicebus.windows.net/eventhubname/eventHubName?tenantId=contoso.com" ``` -Create a subscription for Event Grid Partner Topic. +Create a subscription for Event Grid Partner Topic and use TLS 1.3 ```sh -m365 graph subscription add --resource user --changeTypes "created,updated,deleted" --notificationUrl "EventGrid:?azuresubscriptionid=8A8A8A8A-4B4B-4C4C-4D4D-12E12E12E12E&resourcegroup=resourceGroupName&partnertopic=partnerTopicName&location=partnerTopicAzureRegionName" +m365 graph subscription add --resource user --changeTypes "created,updated,deleted" --latestTLSVersion 'v1_3' --notificationUrl "EventGrid:?azuresubscriptionid=8A8A8A8A-4B4B-4C4C-4D4D-12E12E12E12E&resourcegroup=resourceGroupName&partnertopic=partnerTopicName&location=partnerTopicAzureRegionName" ``` ## Response diff --git a/src/m365/graph/commands/subscription/subscription-add.spec.ts b/src/m365/graph/commands/subscription/subscription-add.spec.ts index 3c2eb44003e..e8b2cde528a 100644 --- a/src/m365/graph/commands/subscription/subscription-add.spec.ts +++ b/src/m365/graph/commands/subscription/subscription-add.spec.ts @@ -70,7 +70,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "me/mailFolders('Inbox')/messages", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -95,7 +95,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { } }); assert.strictEqual(JSON.stringify(log[0]), JSON.stringify({ - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "me/mailFolders('Inbox')/messages", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -112,7 +112,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "groups", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -144,7 +144,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "groups", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -175,7 +175,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "groups", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -210,7 +210,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { requestBodyArg = opts.data; if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", "resource": "groups", "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", @@ -242,7 +242,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", // NOTE Teams is not a supported resource and has no default maximum expiration delay "resource": "teams", @@ -274,7 +274,7 @@ describe(commands.SUBSCRIPTION_ADD, () => { sinon.stub(request, 'post').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { return { - "@odata.context": "https://graph.microsoft.com/beta/$metadata#subscriptions/$entity", + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", // NOTE Teams is not a supported resource and has no default maximum expiration delay "resource": "teams", @@ -302,6 +302,100 @@ describe(commands.SUBSCRIPTION_ADD, () => { assert(loggerLogToStderrSpy.calledWith("Actual expiration date time: 2019-01-03T22:29:00.000Z")); }); + it('adds subscription and set specific TLS version', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { + return { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", + "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", + "resource": "me/mailFolders('Inbox')/messages", + "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", + "changeType": "updated", + "clientState": "secretClientValue", + "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient", + "expirationDateTime": "2016-11-20T18:23:45.935Z", + "creatorId": "8ee44408-0679-472c-bc2a-692812af3437", + "latestSupportedTlsVersion": "v1_3" + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + expirationDateTime: '2016-11-20T18:23:45.935Z', + latestTLSVersion: 'v1_3' + } + }); + assert.strictEqual(JSON.stringify(log[0]), JSON.stringify({ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", + "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", + "resource": "me/mailFolders('Inbox')/messages", + "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", + "changeType": "updated", + "clientState": "secretClientValue", + "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient", + "expirationDateTime": "2016-11-20T18:23:45.935Z", + "creatorId": "8ee44408-0679-472c-bc2a-692812af3437", + "latestSupportedTlsVersion": "v1_3" + })); + }); + + it('adds subscription and includes resource data', async () => { + sinon.stub(request, 'post').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/subscriptions`) { + return { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", + "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", + "resource": "me/mailFolders('Inbox')/messages", + "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", + "changeType": "updated", + "clientState": "secretClientValue", + "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient", + "expirationDateTime": "2016-11-20T18:23:45.935Z", + "creatorId": "8ee44408-0679-472c-bc2a-692812af3437", + "includeResourceData": true, + "encryptionCertificateId": "MyCert", + "encryptionCertificate": "Q0xJIGZvciBNaWNyb3NvZnQgMzY1" + }; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + expirationDateTime: '2016-11-20T18:23:45.935Z', + includeResourceData: true, + encryptionCertificate: 'Q0xJIGZvciBNaWNyb3NvZnQgMzY1', + encryptionCertificateId: 'MyCert' + } + }); + assert.strictEqual(JSON.stringify(log[0]), JSON.stringify({ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity", + "id": "7f105c7d-2dc5-4530-97cd-4e7ae6534c07", + "resource": "me/mailFolders('Inbox')/messages", + "applicationId": "24d3b144-21ae-4080-943f-7067b395b913", + "changeType": "updated", + "clientState": "secretClientValue", + "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient", + "expirationDateTime": "2016-11-20T18:23:45.935Z", + "creatorId": "8ee44408-0679-472c-bc2a-692812af3437", + "includeResourceData": true, + "encryptionCertificateId": "MyCert", + "encryptionCertificate": "Q0xJIGZvciBNaWNyb3NvZnQgMzY1" + })); + }); + it('handles error correctly', async () => { sinon.stub(request, 'post').rejects(new Error('An error has occurred')); @@ -368,6 +462,78 @@ describe(commands.SUBSCRIPTION_ADD, () => { assert.notStrictEqual(actual, true); }); + it('fails validation if the notificationUrlAppId is not a valid GUID', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + notificationUrlAppId: 'foo', + expirationDateTime: null + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if latestTLSVersion is not valid', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + expirationDateTime: '2016-11-20T18:23:45.935Z', + latestTLSVersion: 'foo' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if lifecycleNotificationUrl is not valid', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: 'https://webhook.azurewebsites.net/api/send/myNotifyClient', + expirationDateTime: '2016-11-20T18:23:45.935Z', + lifecycleNotificationUrl: 'foo' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if resource data should be included, but encryptionCertificate is not set', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: 'https://webhook.azurewebsites.net/api/send/myNotifyClient', + expirationDateTime: '2016-11-20T18:23:45.935Z', + includeResourceData: true, + encryptionCertificateId: 'MyCert' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if resource data should be included, but encryptionCertificateId is not set', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: 'https://webhook.azurewebsites.net/api/send/myNotifyClient', + expirationDateTime: '2016-11-20T18:23:45.935Z', + includeResourceData: true, + encryptionCertificate: 'Q0xJIGZvciBNaWNyb3NvZnQgMzY1' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + it('passes validation if the expirationDateTime is not specified', async () => { const actual = await command.validate({ options: { @@ -419,4 +585,90 @@ describe(commands.SUBSCRIPTION_ADD, () => { }, commandInfo); assert.strictEqual(actual, true); }); + + it('passes validation if the lifecycleNotificationUrl points to valid https URL', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + lifecycleNotificationUrl: "https://webhook.azurewebsites.net/api/lifecycleNotifications", + expirationDateTime: null + } + }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if the lifecycleNotificationUrl points to valid Azure Event Hub location', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "EventHub:https://exchangenotifications.servicebus.windows.net/eventhubname/inboxmessages?tenantId=contoso.com", + lifecycleNotificationUrl: "EventHub:https://exchangenotifications.servicebus.windows.net/eventhubname/inboxmessages?tenantId=contoso.com", + expirationDateTime: null + } + }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if the lifecycleNotificationUrl points to valid Azure Event Grid Partner Topic', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "EventGrid:?azuresubscriptionid=b07a45b3-f7b7-489b-9269-da6f3f93dff0&resourcegroup=rg-graph-api&partnertopic=messages&location=germanywestcentral", + lifecycleNotificationUrl: "EventGrid:?azuresubscriptionid=b07a45b3-f7b7-489b-9269-da6f3f93dff0&resourcegroup=rg-graph-api&partnertopic=messages&location=germanywestcentral", + expirationDateTime: null + } + }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if the notificationUrlAppId is a valid GUID', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + notificationUrlAppId: '24d3b144-21ae-4080-943f-7067b395b913', + expirationDateTime: null + } + }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if latestTLSVersion is valid', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + expirationDateTime: '2016-11-20T18:23:45.935Z', + latestTLSVersion: 'v1_3' + } + }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation if resource data should be included and encryptionCertificate is specified together with encryptionCertificateId', async () => { + const actual = await command.validate({ + options: { + resource: "me/mailFolders('Inbox')/messages", + changeTypes: 'updated', + clientState: 'secretClientValue', + notificationUrl: "https://webhook.azurewebsites.net/api/send/myNotifyClient", + expirationDateTime: '2016-11-20T18:23:45.935Z', + includeResourceData: true, + encryptionCertificate: 'Q0xJIGZvciBNaWNyb3NvZnQgMzY1', + encryptionCertificateId: 'MyCert' + } + }, commandInfo); + assert.strictEqual(actual, true); + }); }); diff --git a/src/m365/graph/commands/subscription/subscription-add.ts b/src/m365/graph/commands/subscription/subscription-add.ts index 2569dab2d1b..ee2aa9b3fa6 100644 --- a/src/m365/graph/commands/subscription/subscription-add.ts +++ b/src/m365/graph/commands/subscription/subscription-add.ts @@ -15,6 +15,12 @@ interface Options extends GlobalOptions { notificationUrl: string; expirationDateTime?: string; clientState?: string; + lifecycleNotificationUrl?: string; + notificationUrlAppId?: string; + latestTLSVersion?: string; + includeResourceData?: boolean; + encryptionCertificate?: string; + encryptionCertificateId?: string; } const DEFAULT_EXPIRATION_DELAY_IN_MINUTES_PER_RESOURCE_TYPE = { @@ -38,6 +44,7 @@ const DEFAULT_EXPIRATION_DELAY_IN_MINUTES = 4230; const SAFE_MINUTES_DELTA = 1; class GraphSubscriptionAddCommand extends GraphCommand { + private allowedTlsVersions: string[] = ['v1_0', 'v1_1', 'v1_2', 'v1_3']; public get name(): string { return commands.SUBSCRIPTION_ADD; } @@ -59,7 +66,13 @@ class GraphSubscriptionAddCommand extends GraphCommand { Object.assign(this.telemetryProperties, { changeTypes: args.options.changeTypes, expirationDateTime: typeof args.options.expirationDateTime !== 'undefined', - clientState: typeof args.options.clientState !== 'undefined' + clientState: typeof args.options.clientState !== 'undefined', + lifecycleNotificationUrl: typeof args.options.lifecycleNotificationUrl !== 'undefined', + notificationUrlAppId: typeof args.options.notificationUrlAppId !== 'undefined', + latestTLSVersion: typeof args.options.latestTLSVersion !== 'undefined', + includeResourceData: !!args.options.includeResourceData, + encryptionCertificate: typeof args.options.encryptionCertificate !== 'undefined', + encryptionCertificateId: typeof args.options.encryptionCertificateId !== 'undefined' }); }); } @@ -81,6 +94,25 @@ class GraphSubscriptionAddCommand extends GraphCommand { }, { option: '-s, --clientState [clientState]' + }, + { + option: '--lifecycleNotificationUrl [lifecycleNotificationUrl]' + }, + { + option: '--notificationUrlAppId [notificationUrlAppId]' + }, + { + option: '--latestTLSVersion [latestTLSVersion]', + autocomplete: this.allowedTlsVersions + }, + { + option: '--includeResourceData [includeResourceData]' + }, + { + option: '--encryptionCertificate [encryptionCertificate]' + }, + { + option: '--encryptionCertificateId [encryptionCertificateId]' } ); } @@ -106,6 +138,28 @@ class GraphSubscriptionAddCommand extends GraphCommand { return 'The clientState value exceeds the maximum length of 128 characters'; } + if (args.options.lifecycleNotificationUrl && !args.options.lifecycleNotificationUrl.toLowerCase().startsWith('https://') + && !args.options.lifecycleNotificationUrl.toLowerCase().startsWith('eventhub:https://') + && !args.options.lifecycleNotificationUrl.toLowerCase().startsWith('eventgrid:?azuresubscriptionid=')) { + return `The lifecycle notification URL '${args.options.lifecycleNotificationUrl}' does not start with either 'https://' or 'EventHub:https://' or 'EventGrid:?azuresubscriptionid='`; + } + + if (args.options.latestTLSVersion && this.allowedTlsVersions.map(x => x.toLowerCase()).indexOf(args.options.latestTLSVersion.toLowerCase()) === -1) { + return `${args.options.latestTLSVersion} is not a valid TLS version. Allowed values are ${this.allowedTlsVersions.join(', ')}`; + } + + if (args.options.includeResourceData && !args.options.encryptionCertificate) { + return `The 'encryptionCertificate' options is required to include the changed resource data`; + } + + if (args.options.includeResourceData && !args.options.encryptionCertificateId) { + return `The 'encryptionCertificateId' options is required to include the changed resource data`; + } + + if (args.options.notificationUrlAppId && !validation.isValidGuid(args.options.notificationUrlAppId)) { + return `${args.options.notificationUrlAppId} is not a valid GUID for the 'notificationUrlAppId'`; + } + return true; } ); @@ -116,13 +170,16 @@ class GraphSubscriptionAddCommand extends GraphCommand { changeType: args.options.changeTypes, resource: args.options.resource, notificationUrl: args.options.notificationUrl, - expirationDateTime: await this.getExpirationDateTimeOrDefault(logger, args) + expirationDateTime: await this.getExpirationDateTimeOrDefault(logger, args), + clientState: args.options.clientState, + includeResourceData: args.options.includeResourceData, + encryptionCertificate: args.options.encryptionCertificate, + encryptionCertificateId: args.options.encryptionCertificateId, + lifecycleNotificationUrl: args.options.lifecycleNotificationUrl, + notificationUrlAppId: args.options.notificationUrlAppId, + latestSupportedTlsVersion: args.options.latestTLSVersion }; - if (args.options.clientState) { - data["clientState"] = args.options.clientState; - } - const requestOptions: CliRequestOptions = { url: `${this.resource}/v1.0/subscriptions`, headers: { From 2c0cb7ae3114f34a4c6794149539c9459f630835 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Fri, 25 Oct 2024 22:55:40 +0200 Subject: [PATCH 30/42] Fixes syntax highlighting --- docs/src/config/darkCodeTheme.ts | 7 +++++++ docs/src/config/lightCodeTheme.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/docs/src/config/darkCodeTheme.ts b/docs/src/config/darkCodeTheme.ts index 185fb649801..3ee9f4a950b 100644 --- a/docs/src/config/darkCodeTheme.ts +++ b/docs/src/config/darkCodeTheme.ts @@ -128,6 +128,13 @@ export default { style: { color: '#E3E3E3' } + }, + { + types: ['boolean'], + languages: ['json'], + style: { + color: '#569CD6' + } } ] } satisfies PrismTheme; \ No newline at end of file diff --git a/docs/src/config/lightCodeTheme.ts b/docs/src/config/lightCodeTheme.ts index 0e6878ccd8a..1623f6bbc5a 100644 --- a/docs/src/config/lightCodeTheme.ts +++ b/docs/src/config/lightCodeTheme.ts @@ -116,6 +116,13 @@ export default { style: { color: '#A0A0A0' } + }, + { + types: ['boolean'], + languages: ['json'], + style: { + color: '#0000FF' + } } ] } satisfies PrismTheme; \ No newline at end of file From 55df91e010ef9d96d5879fc9b6766b663b05486f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Sat, 16 Nov 2024 00:36:34 +0100 Subject: [PATCH 31/42] Updates release notes --- docs/docs/about/release-notes.mdx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 90fea8bacf2..4842ad43b9f 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -13,11 +13,22 @@ sidebar_position: 3 - [spe container list](../cmd/spe/container/container-list.mdx) - lists containers of a specific Container Type [#6082](https://github.com/pnp/cli-microsoft365/issues/6082) - [spe containertype get](../cmd/spe/containertype/containertype-get.mdx) - retrieves a specific Container Type [#5991](https://github.com/pnp/cli-microsoft365/issues/5991) +**Viva:** + +- [viva engage community set](../cmd/viva/engage/engage-community-set.mdx) - updates an existing Viva Engage community [#6279](https://github.com/pnp/cli-microsoft365/issues/6279) + ### Changes - fixed using env var for login command [#6469](https://github.com/pnp/cli-microsoft365/issues/6469) - updated v10 upgrade guidance [#6363](https://github.com/pnp/cli-microsoft365/issues/6363) -- fixed missing 'sp' aliases in some 'enterpriseapp' commands. [#6476](https://github.com/pnp/cli-microsoft365/issues/6476) +- fixed missing 'sp' aliases in some 'enterpriseapp' commands [#6476](https://github.com/pnp/cli-microsoft365/issues/6476) +- updated `entra m365group` commands to accept displayName option [#6147](https://github.com/pnp/cli-microsoft365/issues/6147) +- updated linting to make it faster in GitHub workflows [#6348](https://github.com/pnp/cli-microsoft365/issues/6348) +- enhanced `entra m365group get` with additional properties [#6148](https://github.com/pnp/cli-microsoft365/issues/6148) +- enhanced `spo contenttype` commands with Parent expand [#6329](https://github.com/pnp/cli-microsoft365/issues/6329) +- updated docs on how to log in to CLI [#6343](https://github.com/pnp/cli-microsoft365/issues/6343) +- enhanced `graph subscription add` command with additional options [#6158](https://github.com/pnp/cli-microsoft365/issues/6158) +- fixed syntax highlighting in docs ## [v10.0.0](https://github.com/pnp/cli-microsoft365/releases/tag/v10.0.0) From e212386ac68351c08275658f524aada2097744a3 Mon Sep 17 00:00:00 2001 From: brugh Date: Fri, 9 Aug 2024 14:22:23 +0200 Subject: [PATCH 32/42] Replaces csv parser. Closes #5825 --- npm-shrinkwrap.json | 6 ++++ package.json | 1 + .../report-office365activationcounts.ts | 11 ++----- .../report-office365activationsusercounts.ts | 10 ++---- .../report-office365activationsuserdetail.ts | 10 ++---- src/utils/formatting.ts | 33 +++++-------------- 6 files changed, 22 insertions(+), 49 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 63d467c61c0..6ae3618cda0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -24,6 +24,7 @@ "chalk": "^5.3.0", "clipboardy": "^4.0.0", "configstore": "^7.0.0", + "csv-parse": "^5.5.6", "csv-stringify": "^6.5.1", "easy-table": "^1.2.0", "jmespath": "^0.16.0", @@ -3401,6 +3402,11 @@ "node": ">= 8" } }, + "node_modules/csv-parse": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz", + "integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==" + }, "node_modules/csv-stringify": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.1.tgz", diff --git a/package.json b/package.json index a52a5c6de65..1a7fec23d5c 100644 --- a/package.json +++ b/package.json @@ -267,6 +267,7 @@ "chalk": "^5.3.0", "clipboardy": "^4.0.0", "configstore": "^7.0.0", + "csv-parse": "^5.5.6", "csv-stringify": "^6.5.1", "easy-table": "^1.2.0", "jmespath": "^0.16.0", diff --git a/src/m365/tenant/commands/report/report-office365activationcounts.ts b/src/m365/tenant/commands/report/report-office365activationcounts.ts index 9beee115480..dd89340ab86 100644 --- a/src/m365/tenant/commands/report/report-office365activationcounts.ts +++ b/src/m365/tenant/commands/report/report-office365activationcounts.ts @@ -39,13 +39,12 @@ class TenantReportOffice365ActivationCountsCommand extends GraphCommand { try { const res: any = await request.get(requestOptions); let content: string = ''; - const cleanResponse = this.removeEmptyLines(res); if (output && output.toLowerCase() === 'json') { - content = formatting.parseCsvToJson(cleanResponse); + content = formatting.parseCsvToJson(res); } else { - content = cleanResponse; + content = res; } await logger.log(content); @@ -54,12 +53,6 @@ class TenantReportOffice365ActivationCountsCommand extends GraphCommand { this.handleRejectedODataJsonPromise(err); } } - - private removeEmptyLines(input: string): string { - const rows: string[] = input.split('\n'); - const cleanRows = rows.filter(Boolean); - return cleanRows.join('\n'); - } } export default new TenantReportOffice365ActivationCountsCommand(); \ No newline at end of file diff --git a/src/m365/tenant/commands/report/report-office365activationsusercounts.ts b/src/m365/tenant/commands/report/report-office365activationsusercounts.ts index 897c0e6c45e..8fcb853a32a 100644 --- a/src/m365/tenant/commands/report/report-office365activationsusercounts.ts +++ b/src/m365/tenant/commands/report/report-office365activationsusercounts.ts @@ -39,13 +39,12 @@ class TenantReportOffice365ActivationsUserCountsCommand extends GraphCommand { try { const res: any = await request.get(requestOptions); let content: string = ''; - const cleanResponse = this.removeEmptyLines(res); if (output && output.toLowerCase() === 'json') { - content = formatting.parseCsvToJson(cleanResponse); + content = formatting.parseCsvToJson(res); } else { - content = cleanResponse; + content = res; } await logger.log(content); @@ -55,11 +54,6 @@ class TenantReportOffice365ActivationsUserCountsCommand extends GraphCommand { } } - private removeEmptyLines(input: string): string { - const rows: string[] = input.split('\n'); - const cleanRows = rows.filter(Boolean); - return cleanRows.join('\n'); - } } export default new TenantReportOffice365ActivationsUserCountsCommand(); \ No newline at end of file diff --git a/src/m365/tenant/commands/report/report-office365activationsuserdetail.ts b/src/m365/tenant/commands/report/report-office365activationsuserdetail.ts index 125e0e33068..9158aeedd75 100644 --- a/src/m365/tenant/commands/report/report-office365activationsuserdetail.ts +++ b/src/m365/tenant/commands/report/report-office365activationsuserdetail.ts @@ -39,13 +39,12 @@ class TenantReportOffice365ActivationsUserDetailCommand extends GraphCommand { try { const res: any = await request.get(requestOptions); let content: string = ''; - const cleanResponse = this.removeEmptyLines(res); if (output && output.toLowerCase() === 'json') { - content = formatting.parseCsvToJson(cleanResponse); + content = formatting.parseCsvToJson(res); } else { - content = cleanResponse; + content = res; } await logger.log(content); @@ -56,11 +55,6 @@ class TenantReportOffice365ActivationsUserDetailCommand extends GraphCommand { } } - private removeEmptyLines(input: string): string { - const rows: string[] = input.split('\n'); - const cleanRows = rows.filter(Boolean); - return cleanRows.join('\n'); - } } export default new TenantReportOffice365ActivationsUserDetailCommand(); \ No newline at end of file diff --git a/src/utils/formatting.ts b/src/utils/formatting.ts index c7c7fb95f38..ef36bb2eb4e 100644 --- a/src/utils/formatting.ts +++ b/src/utils/formatting.ts @@ -1,3 +1,4 @@ +import { parse } from 'csv-parse/sync'; import chalk from 'chalk'; import stripJsonComments from 'strip-json-comments'; import { BasePermissions } from '../m365/spo/base-permissions.js'; @@ -87,30 +88,14 @@ export const formatting = { }, parseCsvToJson(s: string, quoteChar: string = '"', delimiter: string = ','): any { - const regex = new RegExp(`\\s*(${quoteChar})?(.*?)\\1\\s*(?:${delimiter}|$)`, 'gs'); - const lines: string[] = s.split('\n'); - - const match = (line: string): string[] => [...line.matchAll(regex)] - .map(m => m[2]); // we only want the second capture group - - const heads = match(lines[0]).slice(0, -1); - - return lines.slice(1) - .filter(text => text.trim() !== '') - .map(line => { - const lineMatch: string[] = match(line); - const obj: any = {}; - heads.forEach((key, index) => { - const value = parseInt(lineMatch[index]); - if (isNaN(value) || value.toString() !== lineMatch[index]) { - obj[key] = lineMatch[index]; - } - else { - obj[key] = parseInt(lineMatch[index]); - } - }); - return obj; - }); + return parse(s, { + quote: quoteChar, + delimiter: delimiter, + columns: true, + skipEmptyLines: true, + ltrim: true, + cast: true + }); }, encodeQueryParameter(value: string): string { From ff5cef714364d5866fb02cbc3b2a2b10f3f5380b Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sun, 24 Nov 2024 21:41:27 +0100 Subject: [PATCH 33/42] Adds new contributor --- docs/docs/about/team.mdx | 6 ++++++ package.json | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/docs/about/team.mdx b/docs/docs/about/team.mdx index 50b13661dbd..cfeaa228dc8 100644 --- a/docs/docs/about/team.mdx +++ b/docs/docs/about/team.mdx @@ -335,6 +335,12 @@ Do you want to know the curious and bright minds behind the CLI for Microsoft 36 github: 'gavdgavd', x: '' }, + { + name: 'Herco van brug', + company: '', + github: 'brugh', + x: '' + }, { name: 'Hugo Bernier', company: 'Microsoft', diff --git a/package.json b/package.json index 1a7fec23d5c..c9dbe74986c 100644 --- a/package.json +++ b/package.json @@ -237,6 +237,7 @@ "Trinder, Garry ", "Tripathi, Saurabh ", "Vaghasia, Siddharth ", + "van Brug, Herco ", "Van de Voorde, Tim ", "van Dijk, Mark ", "van Hunen, Erwin ", From 5b3a3c1b5f4bae0de47656d297ec44ccb886de7a Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Tue, 13 Aug 2024 15:43:19 +0200 Subject: [PATCH 34/42] Replaces deprecated methods. Closes #6244 --- src/AuthServer.ts | 2 +- src/Command.ts | 2 +- src/cli/cli.ts | 4 +-- .../entra/commands/m365group/m365group-add.ts | 2 +- .../commands/m365group/m365group-list.ts | 2 +- .../entra/commands/m365group/m365group-set.ts | 2 +- src/m365/file/commands/convert/convert-pdf.ts | 7 ++--- src/m365/file/commands/file-add.ts | 11 ++++--- .../spfx/commands/package/package-generate.ts | 2 +- src/m365/spfx/commands/project/JsonRule.ts | 4 +-- ...016004_TS_property_pane_property_import.ts | 4 +-- src/m365/spfx/commands/spfx-doctor.ts | 2 +- src/m365/spo/commands/file/file-add.ts | 2 +- .../file/file-retentionlabel-ensure.ts | 3 +- .../file/file-retentionlabel-remove.ts | 3 +- .../folder/folder-retentionlabel-ensure.ts | 3 +- .../folder/folder-retentionlabel-remove.ts | 3 +- .../commands/list/list-webhook-add.spec.ts | 2 +- .../listitem-retentionlabel-ensure.ts | 3 +- .../listitem-retentionlabel-remove.ts | 3 +- src/m365/spo/commands/page/page-add.ts | 2 +- src/m365/spo/commands/site/site-set.ts | 2 +- src/m365/teams/commands/user/user-app-list.ts | 2 +- src/m365/todo/commands/list/list-remove.ts | 3 +- src/m365/todo/commands/list/list-set.ts | 3 +- src/m365/todo/commands/task/task-add.ts | 3 +- src/m365/todo/commands/task/task-get.ts | 3 +- src/m365/todo/commands/task/task-list.ts | 3 +- src/m365/todo/commands/task/task-remove.ts | 3 +- src/m365/todo/commands/task/task-set.ts | 2 +- src/utils/spo.ts | 9 +++--- src/utils/urlUtil.ts | 30 ++++++++----------- 32 files changed, 62 insertions(+), 69 deletions(-) diff --git a/src/AuthServer.ts b/src/AuthServer.ts index 20f276241c6..6492f8d442a 100644 --- a/src/AuthServer.ts +++ b/src/AuthServer.ts @@ -38,7 +38,7 @@ export class AuthServer { }; private httpListener = async (): Promise => { - const requestState = Math.random().toString(16).substr(2, 20); + const requestState = Math.random().toString(16).substring(2, 22); const address = this.httpServer.address() as AddressInfo; this.generatedServerUrl = `http://localhost:${address.port}`; const url = `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.connection.cloudType)}/${this.connection.tenant}/oauth2/authorize?response_type=code&client_id=${this.connection.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`; diff --git a/src/Command.ts b/src/Command.ts index 21a579905b6..e728d79d708 100644 --- a/src/Command.ts +++ b/src/Command.ts @@ -350,7 +350,7 @@ export default abstract class Command { pos = pos1; } - commandName = commandName.substr(0, pos).trim(); + commandName = commandName.substring(0, pos).trim(); } return commandName; diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 33d7e25dbeb..be5a5e60394 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -830,7 +830,7 @@ function printAvailableCommands(): void { commandsToPrint[commandName] = command; } else { - const subCommandsGroup: string = commandName.substr(0, pos); + const subCommandsGroup: string = commandName.substring(0, pos); if (!commandGroupsToPrint[subCommandsGroup]) { commandGroupsToPrint[subCommandsGroup] = 0; } @@ -1034,7 +1034,7 @@ function loadOptionValuesFromFiles(args: { options: yargs.Arguments }): void { return; } - const filePath: string = value.substr(1); + const filePath: string = value.substring(1); // if the file doesn't exist, leave as-is, if it exists replace with // contents from the file if (fs.existsSync(filePath)) { diff --git a/src/m365/entra/commands/m365group/m365group-add.ts b/src/m365/entra/commands/m365group/m365group-add.ts index 8238e6ca1e0..c8d25956e40 100644 --- a/src/m365/entra/commands/m365group/m365group-add.ts +++ b/src/m365/entra/commands/m365group/m365group-add.ts @@ -326,7 +326,7 @@ class EntraM365GroupAddCommand extends GraphCommand { } private getImageContentType(imagePath: string): string { - const extension: string = imagePath.substr(imagePath.lastIndexOf('.')).toLowerCase(); + const extension: string = imagePath.substring(imagePath.lastIndexOf('.')).toLowerCase(); switch (extension) { case '.png': diff --git a/src/m365/entra/commands/m365group/m365group-list.ts b/src/m365/entra/commands/m365group/m365group-list.ts index 20a777b1bac..cb1dc1553ad 100644 --- a/src/m365/entra/commands/m365group/m365group-list.ts +++ b/src/m365/entra/commands/m365group/m365group-list.ts @@ -122,7 +122,7 @@ class EntraM365GroupListCommand extends GraphCommand { const res = await request.get<{ webUrl: string }>(requestOptions); return { id: groupId, - url: res.webUrl ? res.webUrl.substr(0, res.webUrl.lastIndexOf('/')) : '' + url: res.webUrl ? res.webUrl.substring(0, res.webUrl.lastIndexOf('/')) : '' }; } } diff --git a/src/m365/entra/commands/m365group/m365group-set.ts b/src/m365/entra/commands/m365group/m365group-set.ts index 65194e77951..4d0d876d257 100644 --- a/src/m365/entra/commands/m365group/m365group-set.ts +++ b/src/m365/entra/commands/m365group/m365group-set.ts @@ -331,7 +331,7 @@ class EntraM365GroupSetCommand extends GraphCommand { } private getImageContentType(imagePath: string): string { - const extension: string = imagePath.substr(imagePath.lastIndexOf('.')).toLowerCase(); + const extension: string = imagePath.substring(imagePath.lastIndexOf('.')).toLowerCase(); switch (extension) { case '.png': diff --git a/src/m365/file/commands/convert/convert-pdf.ts b/src/m365/file/commands/convert/convert-pdf.ts index 668ecfc3bc5..eb982f12828 100644 --- a/src/m365/file/commands/convert/convert-pdf.ts +++ b/src/m365/file/commands/convert/convert-pdf.ts @@ -2,7 +2,6 @@ import { AxiosResponse } from 'axios'; import fs from 'fs'; import os from 'os'; import path from 'path'; -import url from 'url'; import { v4 } from 'uuid'; import auth from '../../../../Auth.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -243,13 +242,13 @@ class FileConvertPdfCommand extends GraphCommand { return fileGraphUrl; } - const _url = url.parse(fileWebUrl); + const _url = new URL(fileWebUrl); let siteId: string = ''; let driveRelativeFileUrl: string = ''; - const siteInfo = await this.getGraphSiteInfoFromFullUrl(_url.host as string, _url.path as string); + const siteInfo = await this.getGraphSiteInfoFromFullUrl(_url.hostname, _url.pathname); siteId = siteInfo.id; - let siteRelativeFileUrl: string = (_url.path as string).replace(siteInfo.serverRelativeUrl, ''); + let siteRelativeFileUrl: string = _url.pathname.replace(siteInfo.serverRelativeUrl, ''); // normalize site-relative URLs for root site collections and root sites if (!siteRelativeFileUrl.startsWith('/')) { siteRelativeFileUrl = '/' + siteRelativeFileUrl; diff --git a/src/m365/file/commands/file-add.ts b/src/m365/file/commands/file-add.ts index 2636f70e0c0..c92714a91e3 100644 --- a/src/m365/file/commands/file-add.ts +++ b/src/m365/file/commands/file-add.ts @@ -1,6 +1,5 @@ import fs from 'fs'; import path from 'path'; -import url from 'url'; import { Logger } from '../../../cli/Logger.js'; import GlobalOptions from '../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../request.js'; @@ -73,7 +72,7 @@ class FileAddCommand extends GraphCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { let folderUrlWithoutTrailingSlash = args.options.folderUrl; if (folderUrlWithoutTrailingSlash.endsWith('/')) { - folderUrlWithoutTrailingSlash = folderUrlWithoutTrailingSlash.substr(0, folderUrlWithoutTrailingSlash.length - 1); + folderUrlWithoutTrailingSlash = folderUrlWithoutTrailingSlash.substring(0, folderUrlWithoutTrailingSlash.length - 1); } try { @@ -148,15 +147,15 @@ class FileAddCommand extends GraphCommand { await logger.logToStderr(`Resolving Graph drive item URL for ${fileWebUrl}`); } - const _fileWebUrl = url.parse(fileWebUrl); - const _siteUrl = url.parse(siteUrl || fileWebUrl); + const _fileWebUrl = new URL(fileWebUrl); + const _siteUrl = new URL(siteUrl || fileWebUrl); const isSiteUrl = typeof siteUrl !== 'undefined'; let siteId: string = ''; let driveRelativeFileUrl: string = ''; - const siteInfo = await this.getGraphSiteInfoFromFullUrl(_siteUrl.host as string, _siteUrl.path as string, isSiteUrl); + const siteInfo = await this.getGraphSiteInfoFromFullUrl(_siteUrl.host, _siteUrl.pathname, isSiteUrl); siteId = siteInfo.id; - let siteRelativeFileUrl: string = (_fileWebUrl.path as string).replace(siteInfo.serverRelativeUrl, ''); + let siteRelativeFileUrl: string = _fileWebUrl.pathname.replace(siteInfo.serverRelativeUrl, ''); // normalize site-relative URLs for root site collections and root sites if (!siteRelativeFileUrl.startsWith('/')) { diff --git a/src/m365/spfx/commands/package/package-generate.ts b/src/m365/spfx/commands/package/package-generate.ts index 1430c824deb..07a07c6aff2 100644 --- a/src/m365/spfx/commands/package/package-generate.ts +++ b/src/m365/spfx/commands/package/package-generate.ts @@ -254,7 +254,7 @@ class SpfxPackageGenerateCommand extends AnonymousCommand { } private static getWebPartAlias(webPartName: string): string { - return 'AutoWP' + webPartName.replace(/[^a-zA-Z0-9]/g, '').substr(0, 40); + return 'AutoWP' + webPartName.replace(/[^a-zA-Z0-9]/g, '').substring(0, 40); } private static generateNewId = (): string => { diff --git a/src/m365/spfx/commands/project/JsonRule.ts b/src/m365/spfx/commands/project/JsonRule.ts index abf3a68b94b..7188258a56e 100644 --- a/src/m365/spfx/commands/project/JsonRule.ts +++ b/src/m365/spfx/commands/project/JsonRule.ts @@ -65,9 +65,9 @@ export abstract class JsonRule extends Rule { isArray = true; const pos = currentProperty.indexOf('[') + 1; // get array element from the property name - arrayElement = currentProperty.substr(pos, currentProperty.length - pos - 1); + arrayElement = currentProperty.substring(pos, currentProperty.length - 1); // remove array element from the property name - currentProperty = currentProperty.substr(0, pos - 1); + currentProperty = currentProperty.substring(0, pos - 1); } for (let i = 0; i < node.children.length; i++) { diff --git a/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts b/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts index bc953d0f369..7c1955ceb72 100644 --- a/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts +++ b/src/m365/spfx/commands/project/project-upgrade/rules/FN016004_TS_property_pane_property_import.ts @@ -54,8 +54,8 @@ export class FN016004_TS_property_pane_property_import extends TsRule { obj.forEach(n => { const resource: string = n.getText(); - const importsText: string = resource.replace(/\s/g, '').substr(resource.indexOf('{')); - const imports: string[] = importsText.substr(0, importsText.indexOf('}')).split(','); + const importsText: string = resource.replace(/\s/g, '').substring(resource.indexOf('{')); + const imports: string[] = importsText.substring(0, importsText.indexOf('}')).split(','); const importsToStay: string[] = []; const importsToBeMoved: string[] = []; diff --git a/src/m365/spfx/commands/spfx-doctor.ts b/src/m365/spfx/commands/spfx-doctor.ts index 6c6bab45f3d..51802947968 100644 --- a/src/m365/spfx/commands/spfx-doctor.ts +++ b/src/m365/spfx/commands/spfx-doctor.ts @@ -1050,7 +1050,7 @@ class SpfxDoctorCommand extends BaseProjectCommand { } private getNodeVersion(): string { - return process.version.substr(1); + return process.version.substring(1); } private async checkStatus(what: string, versionFound: string, versionCheck: VersionCheck): Promise { diff --git a/src/m365/spo/commands/file/file-add.ts b/src/m365/spo/commands/file/file-add.ts index 0140f102e55..5f218bd81fc 100644 --- a/src/m365/spo/commands/file/file-add.ts +++ b/src/m365/spo/commands/file/file-add.ts @@ -449,7 +449,7 @@ class SpoFileAddCommand extends SpoCommand { const isLastChunk: boolean = info.Position >= info.Size; if (isLastChunk) { // trim buffer for last chunk - fileBuffer = fileBuffer.slice(0, readCount); + fileBuffer = fileBuffer.subarray(0, readCount); } const requestOptions: CliRequestOptions = { diff --git a/src/m365/spo/commands/file/file-retentionlabel-ensure.ts b/src/m365/spo/commands/file/file-retentionlabel-ensure.ts index 95a6d9f622d..734fc036777 100644 --- a/src/m365/spo/commands/file/file-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/file/file-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -100,7 +99,7 @@ class SpoFileRetentionLabelEnsureCommand extends SpoCommand { public async commandAction(logger: Logger, args: CommandArgs): Promise { try { const fileProperties = await this.getFileProperties(logger, args); - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, fileProperties.listServerRelativeUrl); diff --git a/src/m365/spo/commands/file/file-retentionlabel-remove.ts b/src/m365/spo/commands/file/file-retentionlabel-remove.ts index c157b0e4ec0..2063dfc190d 100644 --- a/src/m365/spo/commands/file/file-retentionlabel-remove.ts +++ b/src/m365/spo/commands/file/file-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; @@ -111,7 +110,7 @@ class SpoFileRetentionLabelRemoveCommand extends SpoCommand { private async removeFileRetentionLabel(logger: Logger, args: CommandArgs): Promise { try { const fileProperties = await this.getFileProperties(logger, args); - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, fileProperties.listServerRelativeUrl); diff --git a/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts b/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts index c3b0f8a87ed..198ec747504 100644 --- a/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/folder/folder-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import GlobalOptions from '../../../../GlobalOptions.js'; import { Logger } from '../../../../cli/Logger.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -97,7 +96,7 @@ class SpoFolderRetentionLabelEnsureCommand extends SpoCommand { const folderProperties = await this.getFolderProperties(logger, args); if (folderProperties.ListItemAllFields) { - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, folderProperties.ListItemAllFields.ParentList.RootFolder.ServerRelativeUrl); diff --git a/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts b/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts index 8120f97d890..dbad1ad577b 100644 --- a/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts +++ b/src/m365/spo/commands/folder/folder-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import GlobalOptions from '../../../../GlobalOptions.js'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; @@ -113,7 +112,7 @@ class SpoFolderRetentionLabelRemoveCommand extends SpoCommand { const folderProperties = await this.getFolderProperties(logger, args); if (folderProperties.ListItemAllFields) { - const parsedUrl = url.parse(args.options.webUrl); + const parsedUrl = new URL(args.options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; const listAbsoluteUrl = urlUtil.urlCombine(tenantUrl, folderProperties.ListItemAllFields.ParentList.RootFolder.ServerRelativeUrl); diff --git a/src/m365/spo/commands/list/list-webhook-add.spec.ts b/src/m365/spo/commands/list/list-webhook-add.spec.ts index 398fac6f58e..71d5ca4a219 100644 --- a/src/m365/spo/commands/list/list-webhook-add.spec.ts +++ b/src/m365/spo/commands/list/list-webhook-add.spec.ts @@ -383,7 +383,7 @@ describe(commands.LIST_WEBHOOK_ADD, () => { const currentDate: Date = new Date(); currentDate.setMonth(currentDate.getMonth() + 4); currentDate.setDate(1); - const dateString: string = currentDate.toISOString().substr(0, 10); + const dateString: string = currentDate.toISOString().substring(0, 10); const actual = await command.validate({ options: diff --git a/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts b/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts index 5da14157c77..3b92944aedf 100644 --- a/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts +++ b/src/m365/spo/commands/listitem/listitem-retentionlabel-ensure.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; @@ -161,7 +160,7 @@ class SpoListItemRetentionLabelEnsureCommand extends SpoCommand { } private async getListAbsoluteUrl(options: Options, logger: Logger): Promise { - const parsedUrl = url.parse(options.webUrl); + const parsedUrl = new URL(options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (options.listUrl) { diff --git a/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts b/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts index e9a7a35ce11..8e212f0a91f 100644 --- a/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts +++ b/src/m365/spo/commands/listitem/listitem-retentionlabel-remove.ts @@ -1,4 +1,3 @@ -import * as url from 'url'; import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; @@ -127,7 +126,7 @@ class SpoListItemRetentionLabelRemoveCommand extends SpoCommand { } private async getListAbsoluteUrl(options: Options, logger: Logger): Promise { - const parsedUrl = url.parse(options.webUrl); + const parsedUrl = new URL(options.webUrl); const tenantUrl: string = `${parsedUrl.protocol}//${parsedUrl.hostname}`; if (options.listUrl) { diff --git a/src/m365/spo/commands/page/page-add.ts b/src/m365/spo/commands/page/page-add.ts index aa948a192a8..92f82853de4 100644 --- a/src/m365/spo/commands/page/page-add.ts +++ b/src/m365/spo/commands/page/page-add.ts @@ -128,7 +128,7 @@ class SpoPageAddCommand extends SpoCommand { let bannerImageUrl: string = ''; let canvasContent1: string = ''; let layoutWebpartsContent: string = ''; - const pageTitle: string = args.options.title ? args.options.title : (args.options.name.indexOf('.aspx') > -1 ? args.options.name.substr(0, args.options.name.indexOf('.aspx')) : args.options.name); + const pageTitle: string = args.options.title ? args.options.title : (args.options.name.indexOf('.aspx') > -1 ? args.options.name.substring(0, args.options.name.indexOf('.aspx')) : args.options.name); let pageId: number | null = null; const pageDescription: string = args.options.description || ""; diff --git a/src/m365/spo/commands/site/site-set.ts b/src/m365/spo/commands/site/site-set.ts index 7ee9c417616..446f997f577 100644 --- a/src/m365/spo/commands/site/site-set.ts +++ b/src/m365/spo/commands/site/site-set.ts @@ -678,7 +678,7 @@ class SpoSiteSetCommand extends SpoCommand { headers: { 'X-RequestDigest': this.context.FormDigestValue }, - data: `${payload.join('')}` + data: `${payload.join('')}` }; response = await request.post(requestOptions); diff --git a/src/m365/teams/commands/user/user-app-list.ts b/src/m365/teams/commands/user/user-app-list.ts index 73dfb2dc1bc..901cd44029c 100644 --- a/src/m365/teams/commands/user/user-app-list.ts +++ b/src/m365/teams/commands/user/user-app-list.ts @@ -84,7 +84,7 @@ class TeamsUserAppListCommand extends GraphCommand { const items = await odata.getAllItems(endpoint); items.forEach(i => { const userAppId: string = Buffer.from(i.id as string, 'base64').toString('ascii'); - const appId: string = userAppId.substr(userAppId.indexOf("##") + 2, userAppId.length - userId.length - 2); + const appId: string = userAppId.substring(userAppId.indexOf("##") + 2, userAppId.length); (i as any).appId = appId; }); diff --git a/src/m365/todo/commands/list/list-remove.ts b/src/m365/todo/commands/list/list-remove.ts index f047bb4e7f9..fefaf8d8a07 100644 --- a/src/m365/todo/commands/list/list-remove.ts +++ b/src/m365/todo/commands/list/list-remove.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -79,7 +80,7 @@ class TodoListRemoveCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.name!)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.name!)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/list/list-set.ts b/src/m365/todo/commands/list/list-set.ts index 66a749de11c..a6edee4b594 100644 --- a/src/m365/todo/commands/list/list-set.ts +++ b/src/m365/todo/commands/list/list-set.ts @@ -1,6 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -95,7 +96,7 @@ class TodoListSetCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${endpoint}/me/todo/lists?$filter=displayName eq '${escape(args.options.name as string)}'`, + url: `${endpoint}/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.name!)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/task/task-add.ts b/src/m365/todo/commands/task/task-add.ts index 12e47f44ad9..edeb26cd683 100644 --- a/src/m365/todo/commands/task/task-add.ts +++ b/src/m365/todo/commands/task/task-add.ts @@ -1,6 +1,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import { validation } from '../../../../utils/validation.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -206,7 +207,7 @@ class TodoTaskAddCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-get.ts b/src/m365/todo/commands/task/task-get.ts index acb81dbc4a2..c4964bfd3fa 100644 --- a/src/m365/todo/commands/task/task-get.ts +++ b/src/m365/todo/commands/task/task-get.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; import { ToDoTask } from '../../ToDoTask.js'; @@ -70,7 +71,7 @@ class TodoTaskGetCommand extends DelegatedGraphCommand { } const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-list.ts b/src/m365/todo/commands/task/task-list.ts index add477c383f..9e7d96e154b 100644 --- a/src/m365/todo/commands/task/task-list.ts +++ b/src/m365/todo/commands/task/task-list.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import { odata } from '../../../../utils/odata.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -67,7 +68,7 @@ class TodoTaskListCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(args.options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/m365/todo/commands/task/task-remove.ts b/src/m365/todo/commands/task/task-remove.ts index c0d2d2bd7bf..df76db0e939 100644 --- a/src/m365/todo/commands/task/task-remove.ts +++ b/src/m365/todo/commands/task/task-remove.ts @@ -2,6 +2,7 @@ import { cli } from '../../../../cli/cli.js'; import { Logger } from '../../../../cli/Logger.js'; import GlobalOptions from '../../../../GlobalOptions.js'; import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; import DelegatedGraphCommand from '../../../base/DelegatedGraphCommand.js'; import commands from '../../commands.js'; @@ -81,7 +82,7 @@ class TodoTaskRemoveCommand extends DelegatedGraphCommand { if (options.listName) { // Search list by its name const requestOptions: CliRequestOptions = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(options.listName)}'`, headers: { accept: "application/json;odata.metadata=none" }, diff --git a/src/m365/todo/commands/task/task-set.ts b/src/m365/todo/commands/task/task-set.ts index a730036376a..179a0d93717 100644 --- a/src/m365/todo/commands/task/task-set.ts +++ b/src/m365/todo/commands/task/task-set.ts @@ -185,7 +185,7 @@ class TodoTaskSetCommand extends DelegatedGraphCommand { } const requestOptions: any = { - url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName as string)}'`, + url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${formatting.encodeQueryParameter(options.listName!)}'`, headers: { accept: 'application/json;odata.metadata=none' }, diff --git a/src/utils/spo.ts b/src/utils/spo.ts index d88e4f486ca..38d33c35fa5 100644 --- a/src/utils/spo.ts +++ b/src/utils/spo.ts @@ -1,5 +1,4 @@ import os from 'os'; -import url from 'url'; import { urlUtil } from "./urlUtil.js"; import { validation } from "./validation.js"; import auth from '../Auth.js'; @@ -480,8 +479,10 @@ export const spo = { * @param siteAccessToken a valid access token for the site specified in the webFullUrl param */ async ensureFolder(webFullUrl: string, folderToEnsure: string, logger: Logger, debug: boolean): Promise { - const webUrl = url.parse(webFullUrl); - if (!webUrl.protocol || !webUrl.hostname) { + try { + new URL(webFullUrl); + } + catch { throw new Error('webFullUrl is not a valid URL'); } @@ -1495,7 +1496,7 @@ export const spo = { headers: { 'X-RequestDigest': context.FormDigestValue }, - data: `${payload.join('')}` + data: `${payload.join('')}` }; diff --git a/src/utils/urlUtil.ts b/src/utils/urlUtil.ts index 6f4e98f73ef..5d9a83aa784 100644 --- a/src/utils/urlUtil.ts +++ b/src/utils/urlUtil.ts @@ -1,5 +1,3 @@ -import url from 'url'; - export const urlUtil = { /** * Returns server relative path. @@ -16,13 +14,7 @@ export const urlUtil = { * urlUtil.getServerRelativePath("/sites/team1/", "/Shared Documents"); */ getServerRelativePath(webUrl: string, folderRelativePath: string): string { - const tenantUrl: string = `${url.parse(webUrl).protocol}//${url.parse(webUrl).hostname}`; - // if webUrl is a server-relative URL then tenantUrl will resolve to null//null - // in which case we should keep webUrl - let webRelativePath: string = tenantUrl !== 'null//null' ? webUrl.substr(tenantUrl.length) : webUrl; - - // will be used to remove relative path from the folderRelativePath - // in case the web relative url is included + let webRelativePath = this.getUrlRelativePath(webUrl); let relativePathToRemove: string = webRelativePath; // add '/' at 0 @@ -105,14 +97,9 @@ export const urlUtil = { * Utils.getWebRelativePath("/sites/team1/", "/sites/team1/Shared Documents"); */ getWebRelativePath(webUrl: string, folderUrl: string): string { - + let webRelativePath = this.getUrlRelativePath(webUrl); let folderWebRelativePath: string = ''; - const tenantUrl: string = `${url.parse(webUrl).protocol}//${url.parse(webUrl).hostname}`; - // if webUrl is a server-relative URL then tenantUrl will resolve to null//null - // in which case we should keep webUrl - let webRelativePath: string = tenantUrl !== 'null//null' ? webUrl.substr(tenantUrl.length) : webUrl; - // will be used to remove relative path from the folderRelativePath // in case the web relative url is included let relativePathToRemove: string = webRelativePath; @@ -168,12 +155,11 @@ export const urlUtil = { * urlUtil.getAbsoluteUrl("https://contoso.sharepoint.com/sites/team1/", "/sites/team1/Lists/MyList"); */ getAbsoluteUrl(webUrl: string, serverRelativeUrl: string): string { - const uri: url.UrlWithStringQuery = url.parse(webUrl); - const tenantUrl: string = `${uri.protocol}//${uri.hostname}`; + const parsedUrl = new URL(webUrl); if (serverRelativeUrl[0] !== '/') { serverRelativeUrl = `/${serverRelativeUrl}`; } - return `${tenantUrl}${serverRelativeUrl}`; + return `${parsedUrl.origin}${serverRelativeUrl}`; }, /** @@ -251,5 +237,13 @@ export const urlUtil = { */ removeTrailingSlashes(url: string): string { return url.replace(/\/+$/, ''); + }, + + getUrlRelativePath(url: string): string { + if (url.includes('://')) { + const parsedUrl = new URL(url); + return url.substring(parsedUrl.origin.length); + } + return url; } }; \ No newline at end of file From d4fadc1222e1563f20a23d53813a024c1335ffa0 Mon Sep 17 00:00:00 2001 From: Shantha Kumar T Date: Wed, 25 Sep 2024 21:05:46 +0530 Subject: [PATCH 35/42] Adds copilot alias to chatbot commands. Closes #6261 --- docs/docs/about/release-notes.mdx | 6 +-- .../copilot-get.mdx} | 42 +++++++++++-------- .../copilot-list.mdx} | 34 ++++++++------- .../copilot-remove.mdx} | 26 +++++++----- docs/docs/v7-upgrade-guidance.mdx | 8 ++-- docs/docusaurus.config.ts | 3 ++ docs/src/config/sidebars.ts | 14 +++---- src/index.spec.ts | 3 ++ src/m365/pp/commands.ts | 3 ++ .../copilot-get.spec.ts} | 29 +++++++------ .../chatbot-get.ts => copilot/copilot-get.ts} | 23 ++++++---- .../copilot-list.spec.ts} | 22 ++++++---- .../copilot-list.ts} | 15 ++++--- .../copilot-remove.spec.ts} | 27 +++++++----- .../copilot-remove.ts} | 33 ++++++++------- 15 files changed, 173 insertions(+), 115 deletions(-) rename docs/docs/cmd/pp/{chatbot/chatbot-get.mdx => copilot/copilot-get.mdx} (80%) rename docs/docs/cmd/pp/{chatbot/chatbot-list.mdx => copilot/copilot-list.mdx} (73%) rename docs/docs/cmd/pp/{chatbot/chatbot-remove.mdx => copilot/copilot-remove.mdx} (57%) rename src/m365/pp/commands/{chatbot/chatbot-get.spec.ts => copilot/copilot-get.spec.ts} (92%) rename src/m365/pp/commands/{chatbot/chatbot-get.ts => copilot/copilot-get.ts} (81%) rename src/m365/pp/commands/{chatbot/chatbot-list.spec.ts => copilot/copilot-list.spec.ts} (93%) rename src/m365/pp/commands/{chatbot/chatbot-list.ts => copilot/copilot-list.ts} (90%) rename src/m365/pp/commands/{chatbot/chatbot-remove.spec.ts => copilot/copilot-remove.spec.ts} (90%) rename src/m365/pp/commands/{chatbot/chatbot-remove.ts => copilot/copilot-remove.ts} (77%) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 4842ad43b9f..9892e7b9dd9 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -1286,8 +1286,8 @@ sidebar_position: 3 **Power Platform:** -- [pp chatbot get](../cmd/pp/chatbot/chatbot-get.mdx) - get information about the specified chatbot [#4107](https://github.com/pnp/cli-microsoft365/issues/4107) -- [pp chatbot remove](../cmd/pp/chatbot/chatbot-remove.mdx) - removes the specified chatbot [#4106](https://github.com/pnp/cli-microsoft365/issues/4106) +- [pp chatbot get](../cmd/pp/copilot/copilot-get.mdx) - get information about the specified chatbot [#4107](https://github.com/pnp/cli-microsoft365/issues/4107) +- [pp chatbot remove](../cmd/pp/copilot/copilot-remove.mdx) - removes the specified chatbot [#4106](https://github.com/pnp/cli-microsoft365/issues/4106) - [pp dataverse table row list](../cmd/pp/dataverse/dataverse-table-row-list.mdx) - lists table rows for the given Dataverse table [#3791](https://github.com/pnp/cli-microsoft365/issues/3791) - [pp solution publisher add](../cmd/pp/solution/solution-publisher-add.mdx) - adds a specified publisher in a given environment [#3977](https://github.com/pnp/cli-microsoft365/issues/3977) - [pp dataverse table row remove](../cmd/pp/dataverse/dataverse-table-row-remove.mdx) - removes a row from a dataverse table in a given environment [#4030](https://github.com/pnp/cli-microsoft365/issues/4030) @@ -1369,7 +1369,7 @@ sidebar_position: 3 - [pp card clone](../cmd/pp/card/card-clone.mdx) - clones a specific Microsoft Power Platform card in the specified Power Platform environment [#3790](https://github.com/pnp/cli-microsoft365/issues/3790) - [pp card remove](../cmd/pp/card/card-remove.mdx) - removes the specified Microsoft Power Platform card in the specified Power Platform environment [#3781](https://github.com/pnp/cli-microsoft365/issues/3781) -- [pp chatbot list](../cmd/pp/chatbot/chatbot-list.mdx) - lists Microsoft Power Platform chatbot in the specified Power Platform environment [#3650](https://github.com/pnp/cli-microsoft365/issues/3650) +- [pp chatbot list](../cmd/pp/copilot/copilot-list.mdx) - lists Microsoft Power Platform chatbot in the specified Power Platform environment [#3650](https://github.com/pnp/cli-microsoft365/issues/3650) - [pp dataverse table get](../cmd/pp/dataverse/dataverse-table-get.mdx) - lists a dataverse table in a given environment [#3726](https://github.com/pnp/cli-microsoft365/issues/3726) - [pp dataverse table remove](../cmd/pp/dataverse/dataverse-table-remove.mdx) - removes a dataverse table in a given environment [#4031](https://github.com/pnp/cli-microsoft365/issues/4031) - [pp solution remove](../cmd/pp/solution/solution-remove.mdx) - removes the specified solution in the specified Power Platform environment [#3723](https://github.com/pnp/cli-microsoft365/issues/3723) diff --git a/docs/docs/cmd/pp/chatbot/chatbot-get.mdx b/docs/docs/cmd/pp/copilot/copilot-get.mdx similarity index 80% rename from docs/docs/cmd/pp/chatbot/chatbot-get.mdx rename to docs/docs/cmd/pp/copilot/copilot-get.mdx index 0d140ffdb8e..9a24edcaa35 100644 --- a/docs/docs/cmd/pp/chatbot/chatbot-get.mdx +++ b/docs/docs/cmd/pp/copilot/copilot-get.mdx @@ -2,12 +2,18 @@ import Global from '/docs/cmd/_global.mdx'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# pp chatbot get +# pp copilot get -Get information about the specified chatbot +Get information about the specified copilot ## Usage +```sh +m365 pp copilot get [options] +``` + +## Alias + ```sh m365 pp chatbot get [options] ``` @@ -19,10 +25,10 @@ m365 pp chatbot get [options] : The name of the environment. `-i, --id [id]` -: The id of the chatbot. Specify either `id` or `name` but not both. +: The id of the copilot. Specify either `id` or `name` but not both. `-n, --name [name]` -: The name of the chatbot. Specify either `id` or `name` but not both. +: The name of the copilot. Specify either `id` or `name` but not both. `--asAdmin` : Run the command as admin for environments you do not have explicitly assigned permissions to. @@ -32,28 +38,28 @@ m365 pp chatbot get [options] ## Examples -Get a specific chatbot in a specific environment based on name. +Get a specific copilot in a specific environment based on name. ```sh -m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --name "CLI 365 Chatbot" +m365 pp copilot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --name "CLI 365 Copilot" ``` -Get a specific chatbot in a specific environment based on name as admin. +Get a specific copilot in a specific environment based on name as admin. ```sh -m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --name "CLI 365 Chatbot" --asAdmin +m365 pp copilot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --name "CLI 365 Copilot" --asAdmin ``` -Get a specific chatbot in a specific environment based on id. +Get a specific copilot in a specific environment based on id. ```sh -m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" +m365 pp copilot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" ``` -Get a specific chatbot in a specific environment based on id as admin. +Get a specific copilot in a specific environment based on id as admin. ```sh -m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" --asAdmin +m365 pp copilot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" --asAdmin ``` ## Response @@ -71,7 +77,7 @@ m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd5 "schemaname": "new_bot_23f5f58697fd43d595eb451c9797a53d", "_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "overwritetime": "1900-01-01T00:00:00Z", - "name": "CLI 365 Chatbot", + "name": "CLI 365 Copilot", "solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238", "ismanaged": false, "versionnumber": 1421457, @@ -119,7 +125,7 @@ m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd5 botid : 3a081d91-5ea8-40a7-8ac9-abbaa3fcb893 createdon : 2022-11-19T10:42:22Z modifiedon : 2022-11-19T10:42:24Z - name : CLI 365 Chatbot + name : CLI 365 Copilot publishedon: 2022-11-19T10:43:24Z ``` @@ -128,18 +134,18 @@ m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd5 ```csv name,botid,publishedon,createdon,modifiedon - CLI 365 Chatbot,3a081d91-5ea8-40a7-8ac9-abbaa3fcb893,2022-11-19T10:43:24Z,2022-11-19T10:42:22Z,2022-11-19T10:42:24Z + CLI 365 Copilot,3a081d91-5ea8-40a7-8ac9-abbaa3fcb893,2022-11-19T10:43:24Z,2022-11-19T10:42:22Z,2022-11-19T10:42:24Z ``` ```md - # pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" + # pp copilot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --id "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893" Date: 9/1/2023 - ## CLI 365 Chatbot + ## CLI 365 Copilot Property | Value ---------|------- @@ -151,7 +157,7 @@ m365 pp chatbot get --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd5 schemaname | new\_bot\_23f5f58697fd43d595eb451c9797a53d \_ownerid\_value | 5fa787c1-1c4d-ed11-bba1-000d3a2caf7f overwritetime | 1900-01-01T00:00:00Z - name | CLI 365 Chatbot + name | CLI 365 Copilot solutionid | fd140aae-4df4-11dd-bd17-0019b9312238 ismanaged | false versionnumber | 1445843 diff --git a/docs/docs/cmd/pp/chatbot/chatbot-list.mdx b/docs/docs/cmd/pp/copilot/copilot-list.mdx similarity index 73% rename from docs/docs/cmd/pp/chatbot/chatbot-list.mdx rename to docs/docs/cmd/pp/copilot/copilot-list.mdx index 0bbc5a96c58..20cc6476913 100644 --- a/docs/docs/cmd/pp/chatbot/chatbot-list.mdx +++ b/docs/docs/cmd/pp/copilot/copilot-list.mdx @@ -2,12 +2,18 @@ import Global from '/docs/cmd/_global.mdx'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -# pp chatbot list +# pp copilot list -Lists Microsoft Power Platform chatbot in the specified Power Platform environment +Lists Microsoft Power Platform copilots in the specified Power Platform environment ## Usage +```sh +m365 pp copilot list [options] +``` + +## Alias + ```sh m365 pp chatbot list [options] ``` @@ -26,16 +32,16 @@ m365 pp chatbot list [options] ## Examples -List chatbots in a specific environment. +List copilots in a specific environment. ```sh -m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" +m365 pp copilot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" ``` -List chatbots in a specific environment as admin. +List copilots in a specific environment as admin. ```sh -m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --asAdmin +m365 pp copilot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --asAdmin ``` ## Response @@ -59,7 +65,7 @@ m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd "isManaged": false, "versionNumber": 1429641, "timezoneRuleVersionNumber": 0, - "name": "CLI Chatbot", + "name": "CLI Copilot", "statusCode": 1, "owner": "Doe, John", "overwriteTime": "1900-01-01T00:00:00Z", @@ -77,9 +83,9 @@ m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd ```text - name botid publishedOn createdOn botModifiedOn - ------------ ------------------------------------ -------------------- -------------------- -------------------- - CLI Chatbot 23f5f586-97fd-43d5-95eb-451c9797a53d 2022-11-19T19:19:53Z 2022-11-19T10:42:22Z 2022-11-19T20:19:57Z + name botid publishedOn createdOn botModifiedOn + ----------- ------------------------------------ -------------------- -------------------- -------------------- + CLI Copilot 23f5f586-97fd-43d5-95eb-451c9797a53d 2022-11-19T19:19:53Z 2022-11-19T10:42:22Z 2022-11-19T20:19:57Z ``` @@ -87,18 +93,18 @@ m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd ```csv name,botid,publishedOn,createdOn,botModifiedOn - CLI Chatbot,23f5f586-97fd-43d5-95eb-451c9797a53d,2022-11-19T19:19:53Z,2022-11-19T10:42:22Z,2022-11-19T20:19:57Z + CLI Copilot,23f5f586-97fd-43d5-95eb-451c9797a53d,2022-11-19T19:19:53Z,2022-11-19T10:42:22Z,2022-11-19T20:19:57Z ``` ```md - # pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" + # pp copilot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" Date: 9/1/2023 - ## CLI Chatbot + ## CLI Copilot Property | Value ---------|------- @@ -115,7 +121,7 @@ m365 pp chatbot list --environmentName "Default-d87a7535-dd31-4437-bfe1-95340acd isManaged | false versionNumber | 1429641 timezoneRuleVersionNumber | 0 - name | CLI Chatbot + name | CLI Copilot statusCode | 1 owner | Doe, John overwriteTime | 1900-01-01T00:00:00Z diff --git a/docs/docs/cmd/pp/chatbot/chatbot-remove.mdx b/docs/docs/cmd/pp/copilot/copilot-remove.mdx similarity index 57% rename from docs/docs/cmd/pp/chatbot/chatbot-remove.mdx rename to docs/docs/cmd/pp/copilot/copilot-remove.mdx index ceb2eb30ad6..c0d16948b43 100644 --- a/docs/docs/cmd/pp/chatbot/chatbot-remove.mdx +++ b/docs/docs/cmd/pp/copilot/copilot-remove.mdx @@ -1,11 +1,17 @@ import Global from '/docs/cmd/_global.mdx'; -# pp chatbot remove +# pp copilot remove -Removes the specified chatbot +Removes the specified copilot ## Usage +```sh +m365 pp copilot remove [options] +``` + +## Alias + ```sh m365 pp chatbot remove [options] ``` @@ -17,10 +23,10 @@ m365 pp chatbot remove [options] : The name of the environment. `-i, --id [id]` -: The id of the chatbot. Specify either `id` or `name` but not both. +: The id of the copilot. Specify either `id` or `name` but not both. `-n, --name [name]` -: The name of the chatbot. Specify either `id` or `name` but not both. +: The name of the copilot. Specify either `id` or `name` but not both. `--asAdmin` : Run the command as admin for environments you do not have explicitly assigned permissions to. @@ -33,22 +39,22 @@ m365 pp chatbot remove [options] ## Examples -Removes the specified Microsoft Power Platform chatbot owned by the currently signed-in user based on name +Removes the specified Microsoft Power Platform copilot owned by the currently signed-in user based on name ```sh -m365 pp chatbot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --name "Chatbot Name" +m365 pp copilot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --name "Chatbot Name" ``` -Removes the specified Microsoft Power Platform chatbot owned by the currently signed-in user based on id without confirmation +Removes the specified Microsoft Power Platform copilot owned by the currently signed-in user based on id without confirmation ```sh -m365 pp chatbot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --id 9d9a13d0-6255-ed11-bba2-000d3adf774e --force +m365 pp copilot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --id 9d9a13d0-6255-ed11-bba2-000d3adf774e --force ``` -Removes the specified Microsoft Power Platform chatbot owned by another user based on name +Removes the specified Microsoft Power Platform copilot owned by another user based on name ```sh -m365 pp chatbot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --name "Chatbot Name" --asAdmin +m365 pp copilot remove --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --name "Chatbot Name" --asAdmin ``` ## Response diff --git a/docs/docs/v7-upgrade-guidance.mdx b/docs/docs/v7-upgrade-guidance.mdx index 7ceb7f36255..7aecd4d61cb 100644 --- a/docs/docs/v7-upgrade-guidance.mdx +++ b/docs/docs/v7-upgrade-guidance.mdx @@ -93,7 +93,7 @@ Besides that, when using JSON output, we had duplicate properties in the result. ### Updated output of `pp chatbot list` -The output of [pp chatbot list](./cmd/pp/chatbot/chatbot-list.mdx) contained a `displayName` property. However, the actual property coming from the API was called `name`. We changed `displayName` back to `name` so that it now aligns with the other chatbot commands where a `name` option can be used and a `name` property may be returned. +The output of [pp chatbot list](./cmd/pp/copilot/copilot-list.mdx) contained a `displayName` property. However, the actual property coming from the API was called `name`. We changed `displayName` back to `name` so that it now aligns with the other chatbot commands where a `name` option can be used and a `name` property may be returned. #### What action do I need to take? @@ -447,9 +447,9 @@ Command|Old option|New option [pp card get](./cmd/pp/card/card-get.mdx)|`environment`|`environmentName` [pp card list](./cmd/pp/card/card-list.mdx)|`environment`|`environmentName` [pp card remove](./cmd/pp/card/card-remove.mdx)|`environment`|`environmentName` -[pp chatbot get](./cmd/pp/chatbot/chatbot-get.mdx)|`environment`|`environmentName` -[pp chatbot list](./cmd/pp/chatbot/chatbot-list.mdx)|`environment`|`environmentName` -[pp chatbot remove](./cmd/pp/chatbot/chatbot-remove.mdx)|`environment`|`environmentName` +[pp chatbot get](./cmd/pp/copilot/copilot-get.mdx)|`environment`|`environmentName` +[pp chatbot list](./cmd/pp/copilot/copilot-list.mdx)|`environment`|`environmentName` +[pp chatbot remove](./cmd/pp/copilot/copilot-remove.mdx)|`environment`|`environmentName` [pp dataverse table get](./cmd/pp/dataverse/dataverse-table-get.mdx)|`environment`|`environmentName` [pp dataverse table list](./cmd/pp/dataverse/dataverse-table-list.mdx)|`environment`|`environmentName` [pp dataverse table remove](./cmd/pp/dataverse/dataverse-table-remove.mdx)|`environment`|`environmentName` diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 27e2b9e0dea..6b9747588ab 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -49,6 +49,9 @@ const config: Config = { 'client-redirects', { createRedirects(routePath) { + if (routePath.includes('/copilot/copilot-')) { + return [routePath.replace('/copilot/copilot-', '/chatbot/chatbot-')]; + } if (routePath.includes('/entra')) { return [routePath.replace('/entra', '/aad')]; } diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 4dbf41867ee..17f519a1d37 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1653,21 +1653,21 @@ const sidebars: SidebarsConfig = { ] }, { - chatbot: [ + copilot: [ { type: 'doc', - label: 'chatbot get', - id: 'cmd/pp/chatbot/chatbot-get' + label: 'copilot get', + id: 'cmd/pp/copilot/copilot-get' }, { type: 'doc', - label: 'chatbot list', - id: 'cmd/pp/chatbot/chatbot-list' + label: 'copliot list', + id: 'cmd/pp/copilot/copilot-list' }, { type: 'doc', - label: 'chatbot remove', - id: 'cmd/pp/chatbot/chatbot-remove' + label: 'copilot remove', + id: 'cmd/pp/copilot/copilot-remove' } ] }, diff --git a/src/index.spec.ts b/src/index.spec.ts index 1606309b0ad..3df5180a914 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -77,6 +77,9 @@ describe('Lazy loading commands', () => { 'consent', 'flow connector export', 'flow connector list', + 'pp chatbot get', + 'pp chatbot list', + 'pp chatbot remove', 'search externalconnection add', 'search externalconnection get', 'search externalconnection list', diff --git a/src/m365/pp/commands.ts b/src/m365/pp/commands.ts index dae86ad2616..57132bca30c 100644 --- a/src/m365/pp/commands.ts +++ b/src/m365/pp/commands.ts @@ -11,6 +11,9 @@ export default { CHATBOT_GET: `${prefix} chatbot get`, CHATBOT_LIST: `${prefix} chatbot list`, CHATBOT_REMOVE: `${prefix} chatbot remove`, + COPILOT_GET: `${prefix} copilot get`, + COPILOT_LIST: `${prefix} copilot list`, + COPILOT_REMOVE: `${prefix} copilot remove`, DATAVERSE_TABLE_LIST: `${prefix} dataverse table list`, DATAVERSE_TABLE_GET: `${prefix} dataverse table get`, DATAVERSE_TABLE_REMOVE: `${prefix} dataverse table remove`, diff --git a/src/m365/pp/commands/chatbot/chatbot-get.spec.ts b/src/m365/pp/commands/copilot/copilot-get.spec.ts similarity index 92% rename from src/m365/pp/commands/chatbot/chatbot-get.spec.ts rename to src/m365/pp/commands/copilot/copilot-get.spec.ts index f464aa0c12b..1490dcd13eb 100644 --- a/src/m365/pp/commands/chatbot/chatbot-get.spec.ts +++ b/src/m365/pp/commands/copilot/copilot-get.spec.ts @@ -13,16 +13,16 @@ import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './chatbot-get.js'; +import command from './copilot-get.js'; import { settingsNames } from '../../../../settingsNames.js'; import { accessToken } from '../../../../utils/accessToken.js'; -describe(commands.CHATBOT_GET, () => { +describe(commands.COPILOT_GET, () => { let commandInfo: CommandInfo; //#region Mocked Responses const validEnvironment = '4be50206-9576-4237-8b17-38d8aadfaa36'; const validId = '3a081d91-5ea8-40a7-8ac9-abbaa3fcb893'; - const validName = 'CLI 365 Chatbot'; + const validName = 'CLI 365 Copilot'; const envUrl = "https://contoso-dev.api.crm4.dynamics.com"; const botResponse = { "value": [ @@ -35,7 +35,7 @@ describe(commands.CHATBOT_GET, () => { "schemaname": "new_bot_23f5f58697fd43d595eb451c9797a53d", "_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "overwritetime": "1900-01-01T00:00:00Z", - "name": "CLI 365 Chatbot", + "name": "CLI 365 Copilot", "solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238", "ismanaged": false, "versionnumber": 1421457, @@ -130,13 +130,18 @@ describe(commands.CHATBOT_GET, () => { }); it('has correct name', () => { - assert.strictEqual(command.name, commands.CHATBOT_GET); + assert.strictEqual(command.name, commands.COPILOT_GET); }); it('has a description', () => { assert.notStrictEqual(command.description, null); }); + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.CHATBOT_GET]); + }); + it('defines correct properties for the default output', () => { assert.deepStrictEqual(command.defaultProperties(), ['name', 'botid', 'publishedon', 'createdon', 'modifiedon']); }); @@ -161,7 +166,7 @@ describe(commands.CHATBOT_GET, () => { assert.strictEqual(actual, true); }); - it('throws error when multiple chatbots found with the same name', async () => { + it('throws error when multiple copilots found with the same name', async () => { sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => { if (settingName === settingsNames.prompt) { return false; @@ -193,10 +198,10 @@ describe(commands.CHATBOT_GET, () => { environmentName: validEnvironment, name: validName } - }), new CommandError("Multiple chatbots with name 'CLI 365 Chatbot' found. Found: 69703efe-4149-ed11-bba2-000d3adf7537, 3a081d91-5ea8-40a7-8ac9-abbaa3fcb893.")); + }), new CommandError("Multiple copilots with name 'CLI 365 Copilot' found. Found: 69703efe-4149-ed11-bba2-000d3adf7537, 3a081d91-5ea8-40a7-8ac9-abbaa3fcb893.")); }); - it('handles selecting single result when multiple chatbots with the specified name found and cli is set to prompt', async () => { + it('handles selecting single result when multiple copilots with the specified name found and cli is set to prompt', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); const multipleBotsResponse = { @@ -221,7 +226,7 @@ describe(commands.CHATBOT_GET, () => { assert(loggerLogSpy.calledWith(botResponse.value[0])); }); - it('throws error when no chatbot with name was found', async () => { + it('throws error when no copilot with name was found', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(request, 'get').callsFake(async (opts) => { @@ -239,10 +244,10 @@ describe(commands.CHATBOT_GET, () => { environmentName: validEnvironment, name: validName } - }), new CommandError(`The specified chatbot '${validName}' does not exist.`)); + }), new CommandError(`The specified copilot '${validName}' does not exist.`)); }); - it('retrieves a specific chatbot with the name parameter', async () => { + it('retrieves a specific copilot with the name parameter', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(request, 'get').callsFake(async opts => { @@ -259,7 +264,7 @@ describe(commands.CHATBOT_GET, () => { assert(loggerLogSpy.calledWith(botResponse.value[0])); }); - it('retrieves a specific chatbot with the id parameter', async () => { + it('retrieves a specific copilot with the id parameter', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(request, 'get').callsFake(async opts => { diff --git a/src/m365/pp/commands/chatbot/chatbot-get.ts b/src/m365/pp/commands/copilot/copilot-get.ts similarity index 81% rename from src/m365/pp/commands/chatbot/chatbot-get.ts rename to src/m365/pp/commands/copilot/copilot-get.ts index 966aeefe2a1..b774d6f6f2c 100644 --- a/src/m365/pp/commands/chatbot/chatbot-get.ts +++ b/src/m365/pp/commands/copilot/copilot-get.ts @@ -19,14 +19,18 @@ export interface Options extends GlobalOptions { asAdmin?: boolean; } -class PpChatbotGetCommand extends PowerPlatformCommand { +class PpCopilotGetCommand extends PowerPlatformCommand { public get name(): string { - return commands.CHATBOT_GET; + return commands.COPILOT_GET; } public get description(): string { - return 'Get information about the specified chatbot'; + return 'Get information about the specified copilot'; + } + + public alias(): string[] | undefined { + return [commands.CHATBOT_GET]; } public defaultProperties(): string[] | undefined { @@ -88,14 +92,15 @@ class PpChatbotGetCommand extends PowerPlatformCommand { } public async commandAction(logger: Logger, args: CommandArgs): Promise { + await this.showDeprecationWarning(logger, commands.CHATBOT_GET, commands.COPILOT_GET); if (this.verbose) { - await logger.logToStderr(`Retrieving chatbot '${args.options.id || args.options.name}'...`); + await logger.logToStderr(`Retrieving copilot '${args.options.id || args.options.name}'...`); } try { const dynamicsApiUrl = await powerPlatform.getDynamicsInstanceApiUrl(args.options.environmentName, args.options.asAdmin); - const res = await this.getChatbot(dynamicsApiUrl, args.options); + const res = await this.getCopilot(dynamicsApiUrl, args.options); await logger.log(res); } catch (err: any) { @@ -103,7 +108,7 @@ class PpChatbotGetCommand extends PowerPlatformCommand { } } - private async getChatbot(dynamicsApiUrl: string, options: Options): Promise { + private async getCopilot(dynamicsApiUrl: string, options: Options): Promise { const requestOptions: CliRequestOptions = { headers: { accept: 'application/json;odata.metadata=none' @@ -122,15 +127,15 @@ class PpChatbotGetCommand extends PowerPlatformCommand { if (result.value.length > 1) { const resultAsKeyValuePair = formatting.convertArrayToHashTable('botid', result.value); - return await cli.handleMultipleResultsFound(`Multiple chatbots with name '${options.name}' found.`, resultAsKeyValuePair); + return await cli.handleMultipleResultsFound(`Multiple copilots with name '${options.name}' found.`, resultAsKeyValuePair); } if (result.value.length === 0) { - throw `The specified chatbot '${options.name}' does not exist.`; + throw `The specified copilot '${options.name}' does not exist.`; } return result.value[0]; } } -export default new PpChatbotGetCommand(); \ No newline at end of file +export default new PpCopilotGetCommand(); \ No newline at end of file diff --git a/src/m365/pp/commands/chatbot/chatbot-list.spec.ts b/src/m365/pp/commands/copilot/copilot-list.spec.ts similarity index 93% rename from src/m365/pp/commands/chatbot/chatbot-list.spec.ts rename to src/m365/pp/commands/copilot/copilot-list.spec.ts index c52ae40bd3b..d8858f6d164 100644 --- a/src/m365/pp/commands/chatbot/chatbot-list.spec.ts +++ b/src/m365/pp/commands/copilot/copilot-list.spec.ts @@ -10,10 +10,10 @@ import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import command from './chatbot-list.js'; +import command from './copilot-list.js'; import { accessToken } from '../../../../utils/accessToken.js'; -describe(commands.CHATBOT_LIST, () => { +describe(commands.COPILOT_LIST, () => { const envUrl = "https://contoso-dev.api.crm4.dynamics.com"; const validEnvironment = '4be50206-9576-4237-8b17-38d8aadfaa36'; const fetchXml: string = ` @@ -57,7 +57,7 @@ describe(commands.CHATBOT_LIST, () => { `; - const chatbotResponse: any = { + const copilotResponse: any = { "value": [ { "language": 1033, @@ -73,7 +73,7 @@ describe(commands.CHATBOT_LIST, () => { "isManaged": false, "versionNumber": 1429641, "timezoneRuleVersionNumber": 0, - "name": "CLI Chatbot", + "name": "CLI Copilot", "statusCode": 1, "owner": "Doe, John", "overwriteTime": "1900-01-01T00:00:00Z", @@ -129,7 +129,7 @@ describe(commands.CHATBOT_LIST, () => { }); it('has correct name', () => { - assert.strictEqual(command.name, commands.CHATBOT_LIST); + assert.strictEqual(command.name, commands.COPILOT_LIST); }); it('has a description', () => { @@ -140,7 +140,7 @@ describe(commands.CHATBOT_LIST, () => { assert.deepStrictEqual(command.defaultProperties(), ['name', 'botid', 'publishedOn', 'createdOn', 'botModifiedOn']); }); - it('retrieves chatbots', async () => { + it('retrieves copilot bots', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(request, 'get').callsFake(async (opts) => { @@ -148,7 +148,7 @@ describe(commands.CHATBOT_LIST, () => { if (opts.headers && opts.headers.accept && (opts.headers.accept as string).indexOf('application/json') === 0) { - return chatbotResponse; + return copilotResponse; } } @@ -156,7 +156,7 @@ describe(commands.CHATBOT_LIST, () => { }); await command.action(logger, { options: { debug: true, environmentName: validEnvironment } }); - assert(loggerLogSpy.calledWith(chatbotResponse.value)); + assert(loggerLogSpy.calledWith(copilotResponse.value)); }); it('correctly handles API OData error', async () => { @@ -182,4 +182,10 @@ describe(commands.CHATBOT_LIST, () => { await assert.rejects(command.action(logger, { options: { environmentName: validEnvironment } } as any), new CommandError(`Resource '' does not exist or one of its queried reference-property objects are not present`)); }); + + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.CHATBOT_LIST]); + }); + }); diff --git a/src/m365/pp/commands/chatbot/chatbot-list.ts b/src/m365/pp/commands/copilot/copilot-list.ts similarity index 90% rename from src/m365/pp/commands/chatbot/chatbot-list.ts rename to src/m365/pp/commands/copilot/copilot-list.ts index 934c9b70f3f..fe8d0661d21 100644 --- a/src/m365/pp/commands/chatbot/chatbot-list.ts +++ b/src/m365/pp/commands/copilot/copilot-list.ts @@ -14,13 +14,17 @@ interface Options extends GlobalOptions { asAdmin?: boolean; } -class PpChatbotListCommand extends PowerPlatformCommand { +class PpCopilotListCommand extends PowerPlatformCommand { public get name(): string { - return commands.CHATBOT_LIST; + return commands.COPILOT_LIST; } public get description(): string { - return 'Lists Microsoft Power Platform chatbots in the specified Power Platform environment'; + return 'Lists Microsoft Power Platform copilots in the specified Power Platform environment'; + } + + public alias(): string[] | undefined { + return [commands.CHATBOT_LIST]; } public defaultProperties(): string[] | undefined { @@ -54,8 +58,9 @@ class PpChatbotListCommand extends PowerPlatformCommand { } public async commandAction(logger: Logger, args: CommandArgs): Promise { + await this.showDeprecationWarning(logger, commands.CHATBOT_LIST, commands.COPILOT_LIST); if (this.verbose) { - await logger.logToStderr(`Retrieving list of chatbots for environment '${args.options.environmentName}'.`); + await logger.logToStderr(`Retrieving list of copilots for environment '${args.options.environmentName}'.`); } const fetchXml: string = ` @@ -111,4 +116,4 @@ class PpChatbotListCommand extends PowerPlatformCommand { } } -export default new PpChatbotListCommand(); \ No newline at end of file +export default new PpCopilotListCommand(); \ No newline at end of file diff --git a/src/m365/pp/commands/chatbot/chatbot-remove.spec.ts b/src/m365/pp/commands/copilot/copilot-remove.spec.ts similarity index 90% rename from src/m365/pp/commands/chatbot/chatbot-remove.spec.ts rename to src/m365/pp/commands/copilot/copilot-remove.spec.ts index 0275363096f..39b9714adab 100644 --- a/src/m365/pp/commands/chatbot/chatbot-remove.spec.ts +++ b/src/m365/pp/commands/copilot/copilot-remove.spec.ts @@ -12,16 +12,16 @@ import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { session } from '../../../../utils/session.js'; import { sinonUtil } from '../../../../utils/sinonUtil.js'; import commands from '../../commands.js'; -import ppChatbotGetCommand from './chatbot-get.js'; -import command from './chatbot-remove.js'; +import ppCopilotGetCommand from './copilot-get.js'; +import command from './copilot-remove.js'; import { accessToken } from '../../../../utils/accessToken.js'; -describe(commands.CHATBOT_REMOVE, () => { +describe(commands.COPILOT_REMOVE, () => { let commandInfo: CommandInfo; //#region Mocked Responses const validEnvironment = '4be50206-9576-4237-8b17-38d8aadfaa36'; const validId = '3a081d91-5ea8-40a7-8ac9-abbaa3fcb893'; - const validName = 'CLI 365 Chatbot'; + const validName = 'CLI 365 Copilot'; const envUrl = "https://contoso-dev.api.crm4.dynamics.com"; //#endregion @@ -77,13 +77,18 @@ describe(commands.CHATBOT_REMOVE, () => { }); it('has correct name', () => { - assert.strictEqual(command.name, commands.CHATBOT_REMOVE); + assert.strictEqual(command.name, commands.COPILOT_REMOVE); }); it('has a description', () => { assert.notStrictEqual(command.description, null); }); + it('defines correct alias', () => { + const alias = command.alias(); + assert.deepStrictEqual(alias, [commands.CHATBOT_REMOVE]); + }); + it('fails validation if id is not a valid guid.', async () => { const actual = await command.validate({ options: { @@ -104,7 +109,7 @@ describe(commands.CHATBOT_REMOVE, () => { assert.strictEqual(actual, true); }); - it('prompts before removing the specified chatbot owned by the currently signed-in user when force option not passed', async () => { + it('prompts before removing the specified copilot owned by the currently signed-in user when force option not passed', async () => { await command.action(logger, { options: { environmentName: validEnvironment, @@ -115,7 +120,7 @@ describe(commands.CHATBOT_REMOVE, () => { assert(promptIssued); }); - it('aborts removing the specified chatbot owned by the currently signed-in user when force option not passed and prompt not confirmed', async () => { + it('aborts removing the specified copilot owned by the currently signed-in user when force option not passed and prompt not confirmed', async () => { const postSpy = sinon.spy(request, 'post'); await command.action(logger, { @@ -127,13 +132,13 @@ describe(commands.CHATBOT_REMOVE, () => { assert(postSpy.notCalled); }); - it('removes the specified chatbot owned by the currently signed-in user when prompt confirmed by name', async () => { + it('removes the specified copilot owned by the currently signed-in user when prompt confirmed by name', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(cli, 'executeCommandWithOutput').callsFake(async (command): Promise => { - if (command === ppChatbotGetCommand) { + if (command === ppCopilotGetCommand) { return ({ - stdout: `{ "authenticationtrigger": 0, "_owningbusinessunit_value": "6da087c1-1c4d-ed11-bba1-000d3a2caf7f", "statuscode": 1, "createdon": "2022-11-19T10:42:22Z", "statecode": 0, "schemaname": "new_bot_23f5f58697fd43d595eb451c9797a53d", "_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "name": "CLI 365 Chatbot", "solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238", "ismanaged": false, "versionnumber": 1429641, "publishedon": "2022-11-19T19:19:53Z", "timezoneruleversionnumber": 0, "language": 1033, "_modifiedby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "overwritetime": "1900-01-01T00:00:00Z", "modifiedon": "2022-11-19T20:19:57Z", "componentstate": 0, "botid": "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893", "_createdby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "componentidunique": "cdcd6496-e25d-4ad1-91cf-3f4d547fdd23", "authenticationmode": 1, "_owninguser_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "accesscontrolpolicy": 0, "runtimeprovider": 0, "_publishedby_value": null, "authenticationconfiguration": null, "authorizedsecuritygroupids": null, "overriddencreatedon": null, "applicationmanifestinformation": null, "importsequencenumber": null, "synchronizationstatus": null, "_modifiedonbehalfby_value": null, "template": null, "_providerconnectionreferenceid_value": null, "configuration": null, "utcconversiontimezonecode": null, "_createdonbehalfby_value": null, "iconbase64": null, "supportedlanguages": null, "_owningteam_value": null, "iscustomizable": { "Value": true, "CanBeChanged": true, "ManagedPropertyLogicalName": "iscustomizableanddeletable" } }` + stdout: `{ "authenticationtrigger": 0, "_owningbusinessunit_value": "6da087c1-1c4d-ed11-bba1-000d3a2caf7f", "statuscode": 1, "createdon": "2022-11-19T10:42:22Z", "statecode": 0, "schemaname": "new_bot_23f5f58697fd43d595eb451c9797a53d", "_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "name": "CLI 365 Copilot", "solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238", "ismanaged": false, "versionnumber": 1429641, "publishedon": "2022-11-19T19:19:53Z", "timezoneruleversionnumber": 0, "language": 1033, "_modifiedby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "overwritetime": "1900-01-01T00:00:00Z", "modifiedon": "2022-11-19T20:19:57Z", "componentstate": 0, "botid": "3a081d91-5ea8-40a7-8ac9-abbaa3fcb893", "_createdby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "componentidunique": "cdcd6496-e25d-4ad1-91cf-3f4d547fdd23", "authenticationmode": 1, "_owninguser_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f", "accesscontrolpolicy": 0, "runtimeprovider": 0, "_publishedby_value": null, "authenticationconfiguration": null, "authorizedsecuritygroupids": null, "overriddencreatedon": null, "applicationmanifestinformation": null, "importsequencenumber": null, "synchronizationstatus": null, "_modifiedonbehalfby_value": null, "template": null, "_providerconnectionreferenceid_value": null, "configuration": null, "utcconversiontimezonecode": null, "_createdonbehalfby_value": null, "iconbase64": null, "supportedlanguages": null, "_owningteam_value": null, "iscustomizable": { "Value": true, "CanBeChanged": true, "ManagedPropertyLogicalName": "iscustomizableanddeletable" } }` }); } @@ -160,7 +165,7 @@ describe(commands.CHATBOT_REMOVE, () => { assert(postStub.called); }); - it('removes the specified chatbot without confirmation prompt by id', async () => { + it('removes the specified copilot without confirmation prompt by id', async () => { sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl); sinon.stub(request, 'post').callsFake(async (opts) => { diff --git a/src/m365/pp/commands/chatbot/chatbot-remove.ts b/src/m365/pp/commands/copilot/copilot-remove.ts similarity index 77% rename from src/m365/pp/commands/chatbot/chatbot-remove.ts rename to src/m365/pp/commands/copilot/copilot-remove.ts index 66615db12aa..fda86dbe555 100644 --- a/src/m365/pp/commands/chatbot/chatbot-remove.ts +++ b/src/m365/pp/commands/copilot/copilot-remove.ts @@ -7,7 +7,7 @@ import { powerPlatform } from '../../../../utils/powerPlatform.js'; import { validation } from '../../../../utils/validation.js'; import PowerPlatformCommand from '../../../base/PowerPlatformCommand.js'; import commands from '../../commands.js'; -import ppChatbotGetCommand, { Options as PpChatbotGetCommandOptions } from './chatbot-get.js'; +import ppCopilotGetCommand, { Options as PpCopilotGetCommandOptions } from './copilot-get.js'; interface CommandArgs { options: Options; @@ -21,14 +21,18 @@ interface Options extends GlobalOptions { force?: boolean; } -class PpChatbotRemoveCommand extends PowerPlatformCommand { +class PpCopilotRemoveCommand extends PowerPlatformCommand { public get name(): string { - return commands.CHATBOT_REMOVE; + return commands.COPILOT_REMOVE; } public get description(): string { - return 'Removes the specified chatbot'; + return 'Removes the specified copilot'; + } + + public alias(): string[] | undefined { + return [commands.CHATBOT_REMOVE]; } constructor() { @@ -90,28 +94,29 @@ class PpChatbotRemoveCommand extends PowerPlatformCommand { } public async commandAction(logger: Logger, args: CommandArgs): Promise { + await this.showDeprecationWarning(logger, commands.CHATBOT_REMOVE, commands.COPILOT_REMOVE); if (this.verbose) { - await logger.logToStderr(`Removing chatbot '${args.options.id || args.options.name}'...`); + await logger.logToStderr(`Removing copilot '${args.options.id || args.options.name}'...`); } if (args.options.force) { - await this.deleteChatbot(args); + await this.deleteCopilot(args); } else { - const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove chatbot '${args.options.id || args.options.name}'?` }); + const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove copilot '${args.options.id || args.options.name}'?` }); if (result) { - await this.deleteChatbot(args); + await this.deleteCopilot(args); } } } - private async getChatbotId(args: CommandArgs): Promise { + private async getCopilotId(args: CommandArgs): Promise { if (args.options.id) { return args.options.id; } - const options: PpChatbotGetCommandOptions = { + const options: PpCopilotGetCommandOptions = { environmentName: args.options.environmentName, name: args.options.name, output: 'json', @@ -119,16 +124,16 @@ class PpChatbotRemoveCommand extends PowerPlatformCommand { verbose: this.verbose }; - const output = await cli.executeCommandWithOutput(ppChatbotGetCommand as Command, { options: { ...options, _: [] } }); + const output = await cli.executeCommandWithOutput(ppCopilotGetCommand as Command, { options: { ...options, _: [] } }); const getBotOutput = JSON.parse(output.stdout); return getBotOutput.botid; } - private async deleteChatbot(args: CommandArgs): Promise { + private async deleteCopilot(args: CommandArgs): Promise { try { const dynamicsApiUrl = await powerPlatform.getDynamicsInstanceApiUrl(args.options.environmentName, args.options.asAdmin); - const botId = await this.getChatbotId(args); + const botId = await this.getCopilotId(args); const requestOptions: CliRequestOptions = { url: `${dynamicsApiUrl}/api/data/v9.1/bots(${botId})/Microsoft.Dynamics.CRM.PvaDeleteBot?tag=deprovisionbotondelete`, headers: { @@ -146,4 +151,4 @@ class PpChatbotRemoveCommand extends PowerPlatformCommand { } } -export default new PpChatbotRemoveCommand(); \ No newline at end of file +export default new PpCopilotRemoveCommand(); \ No newline at end of file From c6a5de6a8e22e77c5093fe0fecdf6e14483a9ff9 Mon Sep 17 00:00:00 2001 From: mkm17 Date: Thu, 3 Oct 2024 18:39:34 +0200 Subject: [PATCH 36/42] Adds command 'spp model remove'. Closes #6118 --- docs/docs/cmd/spp/model/model-remove.mdx | 57 +++++ docs/src/config/sidebars.ts | 5 + src/m365/spp/commands.ts | 3 +- .../spp/commands/model/model-remove.spec.ts | 202 ++++++++++++++++++ src/m365/spp/commands/model/model-remove.ts | 141 ++++++++++++ src/utils/spp.ts | 11 +- 6 files changed, 415 insertions(+), 4 deletions(-) create mode 100644 docs/docs/cmd/spp/model/model-remove.mdx create mode 100644 src/m365/spp/commands/model/model-remove.spec.ts create mode 100644 src/m365/spp/commands/model/model-remove.ts diff --git a/docs/docs/cmd/spp/model/model-remove.mdx b/docs/docs/cmd/spp/model/model-remove.mdx new file mode 100644 index 00000000000..71fe9562857 --- /dev/null +++ b/docs/docs/cmd/spp/model/model-remove.mdx @@ -0,0 +1,57 @@ +import Global from '/docs/cmd/_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# spp model remove + +Deletes a document understanding model + +## Usage + +```sh +m365 spp model remove [options] +``` + +## Options + +```md definition-list +`-u, --siteUrl ` +: The URL of the content center site. + +`-i, --id [id]` +: The unique ID of the model. Specify either `id` or `title` but not both. + +`-t, --title [title]` +: The display name of the model. Specify either `id` or `title` but not both. + +`-f, --force` +: Don't prompt for confirmation. +``` + + + +## Remarks + +:::note + +The model must be removed from all libraries before it can be deleted. + +::: + +## Examples + +Delete a SharePoint Premium document understanding model using the model’s UniqueId. + +```sh +m365 spp model remove --siteUrl "https://contoso.sharepoint.com/sites/ContentCenter" --id "7645e69d-21fb-4a24-a17a-9bdfa7cb63dc" +``` + +Delete a SharePoint Premium document understanding model using the model’s title. + +```sh +m365 spp model remove --siteUrl "https://contoso.sharepoint.com/sites/ContentCenter" --title "climicrosoft365Model.classifier" +``` + +## Response + +The command won't return a response on success. diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index 17f519a1d37..f0a515347c3 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -4002,6 +4002,11 @@ const sidebars: SidebarsConfig = { type: 'doc', label: 'model list', id: 'cmd/spp/model/model-list' + }, + { + type: 'doc', + label: 'model remove', + id: 'cmd/spp/model/model-remove' } ] } diff --git a/src/m365/spp/commands.ts b/src/m365/spp/commands.ts index 97f9c21b5bb..a657043c662 100644 --- a/src/m365/spp/commands.ts +++ b/src/m365/spp/commands.ts @@ -2,5 +2,6 @@ const prefix: string = 'spp'; export default { CONTENTCENTER_LIST: `${prefix} contentcenter list`, - MODEL_LIST: `${prefix} model list` + MODEL_LIST: `${prefix} model list`, + MODEL_REMOVE: `${prefix} model remove` }; \ No newline at end of file diff --git a/src/m365/spp/commands/model/model-remove.spec.ts b/src/m365/spp/commands/model/model-remove.spec.ts new file mode 100644 index 00000000000..107d4b92199 --- /dev/null +++ b/src/m365/spp/commands/model/model-remove.spec.ts @@ -0,0 +1,202 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { cli } from '../../../../cli/cli.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { CommandError } from '../../../../Command.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.js'; +import { pid } from '../../../../utils/pid.js'; +import { session } from '../../../../utils/session.js'; +import { sinonUtil } from '../../../../utils/sinonUtil.js'; +import commands from '../../commands.js'; +import command from './model-remove.js'; +import { spp } from '../../../../utils/spp.js'; + +describe(commands.MODEL_REMOVE, () => { + let log: string[]; + let logger: Logger; + let commandInfo: CommandInfo; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').returns(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + sinon.stub(spp, 'assertSiteIsContentCenter').resolves(); + auth.connection.active = true; + commandInfo = cli.getCommandInfo(command); + }); + + beforeEach(() => { + log = []; + logger = { + log: async (msg: string) => { + log.push(msg); + }, + logRaw: async (msg: string) => { + log.push(msg); + }, + logToStderr: async (msg: string) => { + log.push(msg); + } + }; + }); + + afterEach(() => { + sinonUtil.restore([ + request.delete, + cli.promptForConfirmation + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.MODEL_REMOVE); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('passes validation when required parameters are valid with id', async () => { + const actual = await command.validate({ options: { siteUrl: 'https://contoso.sharepoint.com/sites/sales', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation when required parameters are valid with title', async () => { + const actual = await command.validate({ options: { siteUrl: 'https://contoso.sharepoint.com/sites/sales', title: 'ModelName' } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('passes validation when required parameters are valid with id and force', async () => { + const actual = await command.validate({ options: { siteUrl: 'https://contoso.sharepoint.com/sites/sales', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f', force: true } }, commandInfo); + assert.strictEqual(actual, true); + }); + + it('fails validation when siteUrl is not valid', async () => { + const actual = await command.validate({ options: { siteUrl: 'invalidUrl', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation when id is not valid', async () => { + const actual = await command.validate({ options: { siteUrl: 'https://contoso.sharepoint.com/sites/sales', id: 'foo' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('correctly handles an error when the model id is not found', async () => { + sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('9b1b1e42-794b-4c71-93ac-5ed92488b67f')`) { + throw { + error: { + "odata.error": { + code: "-1, Microsoft.Office.Server.ContentCenter.ModelNotFoundException", + message: { + lang: "en-US", + value: "File Not Found." + } + } + } + }; + } + }); + + await assert.rejects(command.action(logger, { options: { verbose: true, siteUrl: 'https://contoso.sharepoint.com/sites/portal', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f', force: true } }), + new CommandError('File Not Found.')); + }); + + it('correctly handles an error when the model title is not found', async () => { + sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbytitle('modeltitle.classifier')`) { + return { + "odata.null": true + }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', title: 'modelTitle.classifier', force: true } }), + new CommandError('Model not found.')); + }); + + it('is the confirmation prompt called with id information', async () => { + const confirmationStub = sinon.stub(cli, 'promptForConfirmation').resolves(false); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } }); + assert(confirmationStub.args[0][0].message.startsWith(`Are you sure you want to remove model '9b1b1e42-794b-4c71-93ac-5ed92488b67f'?`)); + }); + + it('is the confirmation prompt called with title information', async () => { + const confirmationStub = sinon.stub(cli, 'promptForConfirmation').resolves(false); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', title: 'modelTitle' } }); + assert(confirmationStub.args[0][0].message.startsWith(`Are you sure you want to remove model 'modelTitle'?`)); + }); + + it('deletes model by id', async () => { + const stubDelete = sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('9b1b1e42-794b-4c71-93ac-5ed92488b67f')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f', force: true } }); + assert(stubDelete.calledOnce); + }); + + it('does not delete model when confirmation is not accepted', async () => { + sinon.stub(cli, 'promptForConfirmation').resolves(false); + + const stubDelete = sinon.stub(request, 'delete').resolves(); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f' } }); + assert(stubDelete.notCalled); + }); + + it('deletes model when the the site URL has trailing slash', async () => { + const stubDelete = sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbyuniqueid('9b1b1e42-794b-4c71-93ac-5ed92488b67f')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal/', id: '9b1b1e42-794b-4c71-93ac-5ed92488b67f', force: true } }); + assert(stubDelete.calledOnce); + }); + + it('deletes model by title', async () => { + const stubDelete = sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbytitle('modelname.classifier')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', title: 'ModelName', force: true } }); + assert(stubDelete.calledOnce); + }); + + it('deletes model by title with .classifier suffix', async () => { + const stubDelete = sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://contoso.sharepoint.com/sites/portal/_api/machinelearning/models/getbytitle('modelname.classifier')`) { + return; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { options: { siteUrl: 'https://contoso.sharepoint.com/sites/portal', title: 'ModelName.classifier', force: true } }); + assert(stubDelete.calledOnce); + }); +}); \ No newline at end of file diff --git a/src/m365/spp/commands/model/model-remove.ts b/src/m365/spp/commands/model/model-remove.ts new file mode 100644 index 00000000000..fcef4acbd46 --- /dev/null +++ b/src/m365/spp/commands/model/model-remove.ts @@ -0,0 +1,141 @@ +import { cli } from '../../../../cli/cli.js'; +import { Logger } from '../../../../cli/Logger.js'; +import GlobalOptions from '../../../../GlobalOptions.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { formatting } from '../../../../utils/formatting.js'; +import { spp } from '../../../../utils/spp.js'; +import { urlUtil } from '../../../../utils/urlUtil.js'; +import { validation } from '../../../../utils/validation.js'; +import SpoCommand from '../../../base/SpoCommand.js'; +import commands from '../../commands.js'; + +interface CommandArgs { + options: Options; +} + +interface Options extends GlobalOptions { + siteUrl: string; + id?: string; + title?: string; + force?: boolean; +} + +class SppModelRemoveCommand extends SpoCommand { + public get name(): string { + return commands.MODEL_REMOVE; + } + + public get description(): string { + return 'Deletes a document understanding model'; + } + + constructor() { + super(); + + this.#initTelemetry(); + this.#initOptions(); + this.#initValidators(); + this.#initOptionSets(); + this.#initTypes(); + } + + #initTelemetry(): void { + this.telemetry.push((args: CommandArgs) => { + Object.assign(this.telemetryProperties, { + id: typeof args.options.id !== 'undefined', + title: typeof args.options.title !== 'undefined', + force: !!args.options.force + }); + }); + } + + #initOptions(): void { + this.options.unshift( + { + option: '-u, --siteUrl ' + }, + { + option: '-i, --id [id]' + }, + { + option: '-t, --title [title]' + }, + { + option: '-f, --force' + } + ); + } + + #initValidators(): void { + this.validators.push( + async (args: CommandArgs) => { + if (args.options.id && !validation.isValidGuid(args.options.id)) { + return `${args.options.id} is not a valid GUID for option 'id'.`; + } + + return validation.isValidSharePointUrl(args.options.siteUrl); + } + ); + } + + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'title'] }); + } + + #initTypes(): void { + this.types.string.push('siteUrl', 'id', 'title'); + this.types.boolean.push('force'); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + try { + if (!args.options.force) { + const confirmationResult = await cli.promptForConfirmation({ message: `Are you sure you want to remove model '${args.options.title ?? args.options.id}'?` }); + + if (!confirmationResult) { + return; + } + } + + if (this.verbose) { + await logger.log(`Removing model from ${args.options.siteUrl}...`); + } + + const siteUrl = urlUtil.removeTrailingSlashes(args.options.siteUrl); + await spp.assertSiteIsContentCenter(siteUrl); + let requestUrl = `${siteUrl}/_api/machinelearning/models/`; + + if (args.options.title) { + let requestTitle = args.options.title.toLowerCase(); + + if (!requestTitle.endsWith('.classifier')) { + requestTitle += '.classifier'; + } + + requestUrl += `getbytitle('${formatting.encodeQueryParameter(requestTitle)}')`; + } + else { + requestUrl += `getbyuniqueid('${args.options.id}')`; + } + + const requestOptions: CliRequestOptions = { + url: requestUrl, + headers: { + accept: 'application/json;odata=nometadata', + 'if-match': '*' + }, + responseType: 'json' + }; + + const result = await request.delete(requestOptions); + if (result?.['odata.null'] === true) { + throw "Model not found."; + } + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new SppModelRemoveCommand(); \ No newline at end of file diff --git a/src/utils/spp.ts b/src/utils/spp.ts index 83544d92284..0b6dff3454b 100644 --- a/src/utils/spp.ts +++ b/src/utils/spp.ts @@ -1,10 +1,15 @@ import request, { CliRequestOptions } from '../request.js'; +export interface SppModel { + UniqueId: string; + Publications?: any[]; +} + export const spp = { /** - * Asserts whether the specified site is a content center. - * @param siteUrl The URL of the site to check. - * @throws Error when the site is not a content center. + * Asserts whether the specified site is a content center + * @param siteUrl The URL of the site to check + * @throws error when site is not a content center. */ async assertSiteIsContentCenter(siteUrl: string): Promise { const requestOptions: CliRequestOptions = { From 8c29adc8204f8f1ffe1c8e32c31ccb32d48c59ab Mon Sep 17 00:00:00 2001 From: Saurabh Date: Sat, 12 Oct 2024 21:09:45 +0200 Subject: [PATCH 37/42] Allows case-insensitive column names in 'spo listitem batch remove'. Closes #6419 --- .../listitem/listitem-batch-remove.spec.ts | 16 ++++++++++++++ .../listitem/listitem-batch-remove.ts | 21 ++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/m365/spo/commands/listitem/listitem-batch-remove.spec.ts b/src/m365/spo/commands/listitem/listitem-batch-remove.spec.ts index 99b57f71ff1..22da1148b8e 100644 --- a/src/m365/spo/commands/listitem/listitem-batch-remove.spec.ts +++ b/src/m365/spo/commands/listitem/listitem-batch-remove.spec.ts @@ -122,6 +122,22 @@ describe(commands.LISTITEM_BATCH_REMOVE, () => { assert(postStub.calledOnce); }); + it('removes items in batch from a SharePoint list retrieved by id when using a csv file with different casing for the ID column', async () => { + sinonUtil.restore(cli.promptForConfirmation); + sinon.stub(cli, 'promptForConfirmation').resolves(true); + + sinon.stub(fs, 'readFileSync').returns(`id\n1`); + const postStub = sinon.stub(request, 'post').callsFake(async (opts: any) => { + if (opts.url === `${webUrl}/_api/$batch`) { + return mockBatchSuccessfulResponse; + } + throw 'Invalid request'; + }); + + await command.action(logger, { options: { webUrl: webUrl, filePath: filePath, listId: listId, recycle: true, verbose: true } }); + assert(postStub.calledOnce); + }); + it('removes items from a sharepoint list retrieved by id when passing a list of ids via string', async () => { const postStub = sinon.stub(request, 'post').callsFake(async (opts: any) => { if (opts.url === `${webUrl}/_api/$batch`) { diff --git a/src/m365/spo/commands/listitem/listitem-batch-remove.ts b/src/m365/spo/commands/listitem/listitem-batch-remove.ts index 20c91eed6c4..de5cba3a35b 100644 --- a/src/m365/spo/commands/listitem/listitem-batch-remove.ts +++ b/src/m365/spo/commands/listitem/listitem-batch-remove.ts @@ -109,22 +109,24 @@ class SpoListItemBatchRemoveCommand extends SpoCommand { const fileContent = fs.readFileSync(args.options.filePath, 'utf-8'); const jsonContent: any[] = formatting.parseCsvToJson(fileContent); - if (!jsonContent[0].hasOwnProperty('ID')) { + const idKey = Object.keys(jsonContent[0]).find(key => key.toLowerCase() === 'id'); + + if (!idKey) { return `The file does not contain the required header row with the column name 'ID'.`; } - const nonNumbers = jsonContent.filter(element => isNaN(Number(element['ID'].toString().trim()))).map(element => element['ID']); + const invalidIDs = validation.isValidPositiveIntegerArray(jsonContent.map(element => element[idKey].toString().trim()).join(',')); - if (nonNumbers.length > 0) { - return `The specified ids '${nonNumbers.join(', ')}' are invalid numbers.`; + if (invalidIDs !== true) { + return `The file contains one or more invalid IDs: '${invalidIDs}'.`; } } if (args.options.ids) { - const nonNumbers = formatting.splitAndTrim(args.options.ids).filter(element => isNaN(Number(element))); + const isValidIntegerArray = validation.isValidPositiveIntegerArray(args.options.ids); - if (nonNumbers.length > 0) { - return `The specified ids '${nonNumbers.join(', ')}' are invalid numbers.`; + if (isValidIntegerArray !== true) { + return `Option 'ids' contains one or more invalid IDs: '${isValidIntegerArray}'.`; } } @@ -167,7 +169,10 @@ class SpoListItemBatchRemoveCommand extends SpoCommand { if (args.options.filePath) { const csvContent = fs.readFileSync(args.options.filePath, 'utf-8'); const jsonContent = formatting.parseCsvToJson(csvContent); - idsToRemove = jsonContent.map((item: { ID: string }) => item['ID']); + + const idKey = Object.keys(jsonContent[0]).find(key => key.toLowerCase() === 'id'); + + idsToRemove = jsonContent.map((item: any) => item[idKey!]); } else { idsToRemove = formatting.splitAndTrim(args.options.ids!); From ff9ac4ea3d58fd191f0482edc26ab22d45513d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kornet?= Date: Tue, 22 Oct 2024 23:51:03 +0200 Subject: [PATCH 38/42] Enhances 'spo site admin' commands to retrieve site ID as admin. Closes #6443 --- .../spo/commands/site/site-admin-add.spec.ts | 51 +++--------- src/m365/spo/commands/site/site-admin-add.ts | 18 +---- .../spo/commands/site/site-admin-list.spec.ts | 54 ++----------- src/m365/spo/commands/site/site-admin-list.ts | 16 +--- .../commands/site/site-admin-remove.spec.ts | 79 ++++--------------- .../spo/commands/site/site-admin-remove.ts | 18 +---- 6 files changed, 43 insertions(+), 193 deletions(-) diff --git a/src/m365/spo/commands/site/site-admin-add.spec.ts b/src/m365/spo/commands/site/site-admin-add.spec.ts index 84e09a726ca..9706d622a53 100644 --- a/src/m365/spo/commands/site/site-admin-add.spec.ts +++ b/src/m365/spo/commands/site/site-admin-add.spec.ts @@ -84,7 +84,8 @@ describe(commands.SITE_ADMIN_ADD, () => { request.patch, entraGroup.getGroupById, entraGroup.getGroupByDisplayName, - entraUser.getUpnByUserId + entraUser.getUpnByUserId, + spo.getSiteAdminPropertiesByUrl ]); }); @@ -126,15 +127,12 @@ describe(commands.SITE_ADMIN_ADD, () => { it('adds a user to site collection admins by userId as admin', async () => { sinon.stub(entraUser, 'getUpnByUserId').resolves(adminToAddUPN); + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -161,6 +159,7 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('adds a user as primary site collection admins by userName as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { return { res: { webUrl: rootUrl } }; @@ -170,10 +169,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { userPrincipalName: adminToAddUPN }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -213,6 +208,7 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('adds a group to site collection admin by groupId as admin - for M365 Group', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupById').resolves({ mail: 'mail', id: groupId @@ -223,10 +219,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -253,6 +245,7 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('adds a group to site collection admin by groupId as admin - for Security Group', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupById').resolves({ mail: undefined, id: groupId @@ -263,10 +256,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -293,6 +282,7 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('adds a group to site collection admin by groupName as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupByDisplayName').resolves( { mail: undefined, @@ -305,10 +295,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -335,6 +321,7 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('adds a group as primary site collection admins by userName as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupByDisplayName').resolves( { mail: undefined, @@ -347,10 +334,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -522,21 +505,11 @@ describe(commands.SITE_ADMIN_ADD, () => { }); it('correctly handles error when site id is not found for specified site URL in admin mode', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').rejects(new Error(`Cannot get site ${siteUrl}`)); sinon.stub(entraUser, 'getUpnByUserId').resolves(adminToAddUPN); - sinon.stub(request, 'get').callsFake(async opts => { - if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { - return { res: { webUrl: rootUrl } }; - } - - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: 'Incorrect ID' }; - } - - throw 'Invalid request: ' + opts.url; - }); await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, userId: adminToAddId, asAdmin: true } }), - new CommandError(`Site with URL ${siteUrl} not found`)); + new CommandError(`Cannot get site ${siteUrl}`)); }); it('correctly handles error when user is not found userId admin mode', async () => { @@ -546,10 +519,6 @@ describe(commands.SITE_ADMIN_ADD, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: 'Incorrect ID' }; - } - throw 'Invalid request: ' + opts.url; }); diff --git a/src/m365/spo/commands/site/site-admin-add.ts b/src/m365/spo/commands/site/site-admin-add.ts index 8121b7d4f9b..2b07072c0f7 100644 --- a/src/m365/spo/commands/site/site-admin-add.ts +++ b/src/m365/spo/commands/site/site-admin-add.ts @@ -135,25 +135,15 @@ class SpoSiteAdminAddCommand extends SpoCommand { } const adminUrl: string = await spo.getSpoAdminUrl(logger, this.debug); - const siteId = await this.getSiteIdBasedOnUrl(args.options.siteUrl, logger); - const siteAdmins = (await this.getSiteAdmins(adminUrl, siteId)).map(u => u.loginName); + const tenantSiteProperties = await spo.getSiteAdminPropertiesByUrl(args.options.siteUrl, false, logger, this.verbose); + const siteAdmins = (await this.getSiteAdmins(adminUrl, tenantSiteProperties.SiteId)).map(u => u.loginName); siteAdmins.push(loginNameToAdd); - await this.setSiteAdminsAsAdmin(adminUrl, siteId, siteAdmins); + await this.setSiteAdminsAsAdmin(adminUrl, tenantSiteProperties.SiteId, siteAdmins); if (args.options.primary) { - await this.setPrimaryAdminAsAdmin(adminUrl, siteId, loginNameToAdd); + await this.setPrimaryAdminAsAdmin(adminUrl, tenantSiteProperties.SiteId, loginNameToAdd); } } - private async getSiteIdBasedOnUrl(siteUrl: string, logger: Logger): Promise { - const siteGraphId = await spo.getSiteId(siteUrl, logger, this.verbose); - const match = siteGraphId.match(/,([a-f0-9\-]{36}),/i); - if (!match) { - throw `Site with URL ${siteUrl} not found`; - } - - return match[1]; - } - private async getSiteAdmins(adminUrl: string, siteId: string): Promise { const requestOptions: CliRequestOptions = { url: `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${siteId}'`, diff --git a/src/m365/spo/commands/site/site-admin-list.spec.ts b/src/m365/spo/commands/site/site-admin-list.spec.ts index b35feee1790..c71b500d938 100644 --- a/src/m365/spo/commands/site/site-admin-list.spec.ts +++ b/src/m365/spo/commands/site/site-admin-list.spec.ts @@ -136,7 +136,8 @@ describe(commands.SITE_ADMIN_LIST, () => { request.post, cli.getSettingWithDefaultValue, spo.getPrimaryAdminLoginNameAsAdmin, - spo.getPrimaryOwnerLoginFromSite + spo.getPrimaryOwnerLoginFromSite, + spo.getSiteAdminPropertiesByUrl ]); }); @@ -169,6 +170,7 @@ describe(commands.SITE_ADMIN_LIST, () => { }); it('gets site collection admins in admin mode', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(spo, 'getPrimaryAdminLoginNameAsAdmin').resolves(primaryAdminLoginName); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -181,10 +183,6 @@ describe(commands.SITE_ADMIN_LIST, () => { }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -223,6 +221,7 @@ describe(commands.SITE_ADMIN_LIST, () => { }); it('correctly handles empty list of site collection admins from API in admin mode', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(spo, 'getPrimaryAdminLoginNameAsAdmin').resolves(''); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -235,10 +234,6 @@ describe(commands.SITE_ADMIN_LIST, () => { }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -270,6 +265,7 @@ describe(commands.SITE_ADMIN_LIST, () => { }); it('handles error when primary admin API returns error in admin mode', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(spo, 'getPrimaryAdminLoginNameAsAdmin').throws("Invalid request"); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -282,10 +278,6 @@ describe(commands.SITE_ADMIN_LIST, () => { }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -303,36 +295,9 @@ describe(commands.SITE_ADMIN_LIST, () => { }); it('handles error when returned siteId is incorrect in admin mode', async () => { - sinon.stub(spo, 'getPrimaryAdminLoginNameAsAdmin').resolves("Invalid request"); - sinon.stub(request, 'get').callsFake(async opts => { - if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { - return { res: { webUrl: rootUrl } }; - } - - if (opts.url === `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${siteId}'`) { - return { - value: listOfAdminsFromAdminSource - }; - } + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').rejects(new Error(`Cannot get site ${siteUrl}`)); - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: 'Incorrect ID' }; - } - - throw 'Invalid request: ' + opts.url; - }); - - sinon.stub(request, 'post').callsFake(async opts => { - if (opts.url === `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${siteId}'`) { - return { - value: listOfAdminsFromAdminSource - }; - } - - throw 'Invalid request: ' + opts.url; - }); - - await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, asAdmin: true } }), new CommandError(`Site with URL ${siteUrl} not found`)); + await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, asAdmin: true } }), new CommandError(`Cannot get site ${siteUrl}`)); }); it('passes validation when only correct siteUrl option specified', async () => { @@ -393,6 +358,7 @@ describe(commands.SITE_ADMIN_LIST, () => { }); it('get additional log when verbose parameter is set in admin mode', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(spo, 'getPrimaryAdminLoginNameAsAdmin').resolves(primaryAdminLoginName); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -405,10 +371,6 @@ describe(commands.SITE_ADMIN_LIST, () => { }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); diff --git a/src/m365/spo/commands/site/site-admin-list.ts b/src/m365/spo/commands/site/site-admin-list.ts index 2d1a945e2df..a62dce474d6 100644 --- a/src/m365/spo/commands/site/site-admin-list.ts +++ b/src/m365/spo/commands/site/site-admin-list.ts @@ -89,9 +89,9 @@ class SpoSiteAdminListCommand extends SpoCommand { } const adminUrl: string = await spo.getSpoAdminUrl(logger, this.debug); - const siteId = await this.getSiteId(args.options.siteUrl, logger); + const tenantSiteProperties = await spo.getSiteAdminPropertiesByUrl(args.options.siteUrl, false, logger, this.verbose); const requestOptions: CliRequestOptions = { - url: `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${siteId}'`, + url: `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${tenantSiteProperties.SiteId}'`, headers: { accept: 'application/json;odata=nometadata' }, @@ -99,7 +99,7 @@ class SpoSiteAdminListCommand extends SpoCommand { }; const response = await request.post(requestOptions); - const primaryAdminLoginName = await spo.getPrimaryAdminLoginNameAsAdmin(adminUrl, siteId, logger, this.verbose); + const primaryAdminLoginName = await spo.getPrimaryAdminLoginNameAsAdmin(adminUrl, tenantSiteProperties.SiteId, logger, this.verbose); const mappedResult = response.value.map((u: AdminUserResult): AdminCommandResultItem => ({ Email: u.email, @@ -110,16 +110,6 @@ class SpoSiteAdminListCommand extends SpoCommand { await logger.log(mappedResult); } - private async getSiteId(siteUrl: string, logger: Logger): Promise { - const siteGraphId = await spo.getSiteId(siteUrl, logger, this.verbose); - const match = siteGraphId.match(/,([a-f0-9\-]{36}),/i); - if (!match) { - throw `Site with URL ${siteUrl} not found`; - } - - return match[1]; - } - private async callAction(logger: Logger, args: CommandArgs): Promise { if (this.verbose) { await logger.logToStderr('Retrieving site administrators...'); diff --git a/src/m365/spo/commands/site/site-admin-remove.spec.ts b/src/m365/spo/commands/site/site-admin-remove.spec.ts index 372b42b9dc8..75ff5aef05f 100644 --- a/src/m365/spo/commands/site/site-admin-remove.spec.ts +++ b/src/m365/spo/commands/site/site-admin-remove.spec.ts @@ -88,7 +88,8 @@ describe(commands.SITE_ADMIN_REMOVE, () => { entraGroup.getGroupByDisplayName, entraUser.getUpnByUserId, cli.getSettingWithDefaultValue, - cli.promptForConfirmation + cli.promptForConfirmation, + spo.getSiteAdminPropertiesByUrl ]); }); @@ -129,6 +130,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('prompts for confirmation before removing site admin if --force option is not passed for user option', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); const promptStub = sinon.stub(cli, 'promptForConfirmation').callsFake(async opts => { if (opts.message === `Are you sure you want to remove specified user from the site administrators list ${siteUrl}?`) { return true; @@ -147,10 +149,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -180,6 +178,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('prompts for confirmation before removing site admin if --force option is not passed for group option', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); const promptStub = sinon.stub(cli, 'promptForConfirmation').callsFake(async opts => { if (opts.message === `Are you sure you want to remove specified group from the site administrators list ${siteUrl}?`) { return true; @@ -198,11 +197,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -248,6 +242,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a user from site collection admins by userId as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraUser, 'getUpnByUserId').resolves(adminToRemoveUPN); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -258,10 +253,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -290,6 +281,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a user from site collection admins by userId as admin with verbose parameter', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraUser, 'getUpnByUserId').resolves(adminToRemoveUPN); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -300,10 +292,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -325,6 +313,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a user from site collection admins by userName as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { return { res: { webUrl: rootUrl } }; @@ -338,10 +327,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { userPrincipalName: adminToRemoveUPN }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -370,6 +355,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('correctly handles an error if trying to remove primary site collection administrator as admin by userId', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraUser, 'getUpnByUserId').resolves(primaryAdminUPN); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -380,10 +366,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -391,6 +373,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('correctly handles an error if trying to remove primary site collection administrator as admin by userName', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { return { res: { webUrl: rootUrl } }; @@ -404,10 +387,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { userPrincipalName: primaryAdminUPN }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -415,6 +394,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a group from site collection admin by groupId as admin - for M365 Group', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupById').resolves({ id: groupId, mail: 'mail' }); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -425,10 +405,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -466,6 +442,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a group from site collection admin by groupId as admin - for Security Group', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupById').resolves({ id: groupId, mail: undefined }); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -476,10 +453,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -517,6 +490,7 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('removes a group from site collection admin by groupName as admin', async () => { + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').resolves({ SiteId: siteId } as any); sinon.stub(entraGroup, 'getGroupByDisplayName').resolves({ id: groupId, mail: undefined }); sinon.stub(request, 'get').callsFake(async opts => { if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { @@ -527,10 +501,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { OwnerLoginName: primaryAdminLoginName }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); @@ -704,27 +674,10 @@ describe(commands.SITE_ADMIN_REMOVE, () => { }); it('correctly handles incorrect site Id guid in admin mode', async () => { - const incorrectSiteId = 'foo'; sinon.stub(entraUser, 'getUpnByUserId').resolves(adminToRemoveUPN); - sinon.stub(request, 'get').callsFake(async opts => { - if (opts.url === `https://graph.microsoft.com/v1.0/sites/root?$select=webUrl`) { - return { res: { webUrl: rootUrl } }; - } - - if (opts.url === `${adminUrl}/_api/SPO.Tenant/GetSiteAdministrators?siteId='${incorrectSiteId}'`) { - return { - value: listOfAdminsFromAdminSource - }; - } + sinon.stub(spo, 'getSiteAdminPropertiesByUrl').rejects(new Error(`Cannot get site ${siteUrl}`)); - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: 'Incorrect ID' }; - } - - throw 'Invalid request: ' + opts.url; - }); - - await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, userId: adminToRemoveId, asAdmin: true, force: true } }), new CommandError(`Site with URL ${siteUrl} not found`)); + await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl, userId: adminToRemoveId, asAdmin: true, force: true } }), new CommandError(`Cannot get site ${siteUrl}`)); }); it('correctly handles error when user is not found userId admin mode', async () => { @@ -734,10 +687,6 @@ describe(commands.SITE_ADMIN_REMOVE, () => { return { res: { webUrl: rootUrl } }; } - if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) { - return { id: `contoso.sharepoint.com,${siteId},fb0a066f-c10f-4734-94d1-f896de4aa484` }; - } - throw 'Invalid request: ' + opts.url; }); diff --git a/src/m365/spo/commands/site/site-admin-remove.ts b/src/m365/spo/commands/site/site-admin-remove.ts index 5136679d77f..d2b500b5602 100644 --- a/src/m365/spo/commands/site/site-admin-remove.ts +++ b/src/m365/spo/commands/site/site-admin-remove.ts @@ -143,26 +143,16 @@ class SpoSiteAdminRemoveCommand extends SpoCommand { } const adminUrl: string = await spo.getSpoAdminUrl(logger, this.debug); - const siteId = await this.getSiteId(args.options.siteUrl, logger); - const primaryAdminLoginName = await spo.getPrimaryAdminLoginNameAsAdmin(adminUrl, siteId, logger, this.verbose); + const tenantSiteProperties = await spo.getSiteAdminPropertiesByUrl(args.options.siteUrl, false, logger, this.verbose); + const primaryAdminLoginName = await spo.getPrimaryAdminLoginNameAsAdmin(adminUrl, tenantSiteProperties.SiteId, logger, this.verbose); if (loginNameToRemove === primaryAdminLoginName) { throw 'You cannot remove the primary site collection administrator.'; } - const existingAdmins = await this.getSiteAdmins(adminUrl, siteId); + const existingAdmins = await this.getSiteAdmins(adminUrl, tenantSiteProperties.SiteId); const adminsToSet = existingAdmins.filter(u => u.loginName.toLowerCase() !== loginNameToRemove.toLowerCase()); - await this.setSiteAdminsAsAdmin(adminUrl, siteId, adminsToSet); - } - - private async getSiteId(siteUrl: string, logger: Logger): Promise { - const siteGraphId = await spo.getSiteId(siteUrl, logger, this.verbose); - const match = siteGraphId.match(/,([a-f0-9\-]{36}),/i); - if (!match) { - throw `Site with URL ${siteUrl} not found`; - } - - return match[1]; + await this.setSiteAdminsAsAdmin(adminUrl, tenantSiteProperties.SiteId, adminsToSet); } private async getSiteAdmins(adminUrl: string, siteId: string): Promise { From 5b471f3076b6fd2f2504e318bca8b6dffeafa77f Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 30 Oct 2024 12:25:36 +0100 Subject: [PATCH 39/42] Fixes XML character escaping in 'spo listitem batch set'. Closes #6430 --- src/m365/spo/commands/listitem/listitem-batch-set.spec.ts | 5 +++-- src/m365/spo/commands/listitem/listitem-batch-set.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/m365/spo/commands/listitem/listitem-batch-set.spec.ts b/src/m365/spo/commands/listitem/listitem-batch-set.spec.ts index 0497d2795ec..1250edaceaa 100644 --- a/src/m365/spo/commands/listitem/listitem-batch-set.spec.ts +++ b/src/m365/spo/commands/listitem/listitem-batch-set.spec.ts @@ -101,9 +101,9 @@ describe(commands.LISTITEM_BATCH_SET, () => { assert.notStrictEqual(command.description, null); }); - it('updates single item in batch to a sharepoint list retrieved by listUrl including empty values', async () => { + it('updates single item in batch to a sharepoint list retrieved by listUrl including empty values and special characters', async () => { const csvContentHeadersEmptyValues = `Id,ContentType,Title,SingleChoiceField`; - const csvContentLineEmptyValues = `10,Item,Title A,`; + const csvContentLineEmptyValues = `10,Item,"Title A <>&""",`; const csvContentEmptyValues = `${csvContentHeadersEmptyValues}\n${csvContentLineEmptyValues}`; const listServerRelativeUrl: string = urlUtil.getServerRelativePath(webUrl, listUrl); const filterFields = ["InternalName eq 'ContentType'", "InternalName eq 'Title'", "InternalName eq 'SingleChoiceField'"]; @@ -136,6 +136,7 @@ describe(commands.LISTITEM_BATCH_SET, () => { await command.action(logger, { options: { webUrl: webUrl, filePath: filePath, listUrl: listUrl, idColumn: idColumn, systemUpdate: true, verbose: true } } as any); assert(postStub.called); + assert(postStub.args[0][0].data.includes('Title A <>&"')); }); it('system updates single item in batch to a sharepoint list retrieved by id without user fields', async () => { diff --git a/src/m365/spo/commands/listitem/listitem-batch-set.ts b/src/m365/spo/commands/listitem/listitem-batch-set.ts index 9741baacdff..8d3415c5a7f 100644 --- a/src/m365/spo/commands/listitem/listitem-batch-set.ts +++ b/src/m365/spo/commands/listitem/listitem-batch-set.ts @@ -219,7 +219,7 @@ class SpoListItemBatchSetCommand extends SpoCommand { actionString += `${field.InternalName}${lookupMultiString.join('')}`; break; default: - actionString += `${field.InternalName}${(row)[field.InternalName].toString()}`; + actionString += `${field.InternalName}${formatting.escapeXml((row)[field.InternalName].toString())}`; break; } } From 0a51b5f0981c8bc2cc9b994d8014d5ba35585f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20W=C3=B3jcik?= Date: Sun, 17 Nov 2024 22:59:01 +0100 Subject: [PATCH 40/42] Updates all sample scripts to align with v10. Closes #6369 --- .../entra/analyze-user-profile-photos/assets/sample.json | 2 +- .../entra/analyze-users-haveibeenpwnd/assets/sample.json | 2 +- .../entra/bulk-manage-group-users/assets/sample.json | 2 +- .../sample-scripts/entra/bulk-manage-group-users/index.mdx | 2 +- .../assets/sample.json | 2 +- .../entra/delete-m365-groups/assets/sample.json | 2 +- .../assets/sample.json | 2 +- .../entra/replace-membership-of-selected-groups/index.mdx | 2 +- .../replace-owner-with-a-different-one/assets/sample.json | 2 +- .../entra/replace-owner-with-a-different-one/index.mdx | 4 ++-- .../entra/scan-groups-with-user-names/assets/sample.json | 2 +- .../spo/add-ci-cd-pipeline/assets/sample.json | 2 +- docs/docs/sample-scripts/spo/add-ci-cd-pipeline/index.mdx | 6 +++--- .../assets/sample.json | 2 +- .../index.mdx | 2 +- .../spo/delete-non-group-connected-modern-sites/index.mdx | 4 ++-- .../spo/list-all-checked-out-files/assets/sample.json | 2 +- .../sample-scripts/spo/list-all-checked-out-files/index.mdx | 4 ++-- .../spo/list-all-files-specific-name/assets/sample.json | 2 +- .../spo/list-all-files-specific-name/index.mdx | 2 +- .../spo/list-all-list-folders-itemcount/assets/sample.json | 2 +- .../spo/list-all-list-folders-itemcount/index.mdx | 6 +++--- .../list-all-sites-with-app-installed/assets/sample.json | 2 +- .../spo/list-all-sites-with-app-installed/index.mdx | 2 +- .../spo/list-empty-folders-on-site/assets/sample.json | 2 +- .../sample-scripts/spo/list-empty-folders-on-site/index.mdx | 2 +- .../spo/list-large-files-within-a-site/assets/sample.json | 2 +- .../spo/list-large-files-within-a-site/index.mdx | 2 +- .../spo/list-site-collection-lists/assets/sample.json | 2 +- .../sample-scripts/spo/list-site-collection-lists/index.mdx | 2 +- .../spo/remove-orphaned-redirect-sites/index.mdx | 4 ++-- .../assets/sample.json | 2 +- .../sp-add-ins-and-azure-acs-retirement-report/index.mdx | 2 +- .../assets/sample.json | 2 +- .../teams/create-team-and-add-members-and-owners/index.mdx | 2 +- 35 files changed, 43 insertions(+), 43 deletions(-) diff --git a/docs/docs/sample-scripts/entra/analyze-user-profile-photos/assets/sample.json b/docs/docs/sample-scripts/entra/analyze-user-profile-photos/assets/sample.json index 15006a553f2..203e946e67e 100644 --- a/docs/docs/sample-scripts/entra/analyze-user-profile-photos/assets/sample.json +++ b/docs/docs/sample-scripts/entra/analyze-user-profile-photos/assets/sample.json @@ -11,7 +11,7 @@ "This script uses Azure Cognitive Service API and Microsoft 365 CLI to analyze user profile pictures and assess whether they meet the standards placed by the organization. It can be customized to ban content within an org channel or collaboration network where employees post pictures, memes, etc." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/analyze-users-haveibeenpwnd/assets/sample.json b/docs/docs/sample-scripts/entra/analyze-users-haveibeenpwnd/assets/sample.json index 1ad115aca39..fb567781696 100644 --- a/docs/docs/sample-scripts/entra/analyze-users-haveibeenpwnd/assets/sample.json +++ b/docs/docs/sample-scripts/entra/analyze-users-haveibeenpwnd/assets/sample.json @@ -11,7 +11,7 @@ "Validate all your users against known breaches with the have I been pwned api. That way you can quickly scan if your users are part of any known breaches." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/bulk-manage-group-users/assets/sample.json b/docs/docs/sample-scripts/entra/bulk-manage-group-users/assets/sample.json index bfb6597e2fa..88ccd6c83af 100644 --- a/docs/docs/sample-scripts/entra/bulk-manage-group-users/assets/sample.json +++ b/docs/docs/sample-scripts/entra/bulk-manage-group-users/assets/sample.json @@ -11,7 +11,7 @@ "Companies pursue to hasten profits growth or enter new marketplace through Mergers and Acquisitions (M&A). M&A typically fails during integration. This also applies to migrating users and data in Microsoft Teams and Groups. Partial acquisition can be pretty tricky. To help make the activity as charming as possible, I have created the following sample script to add/remove bulk users to/from Microsoft Teams team or Microsoft 365 group using CLI for Microsoft 365 commands." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/bulk-manage-group-users/index.mdx b/docs/docs/sample-scripts/entra/bulk-manage-group-users/index.mdx index 03469a7e8ef..1a175f19080 100644 --- a/docs/docs/sample-scripts/entra/bulk-manage-group-users/index.mdx +++ b/docs/docs/sample-scripts/entra/bulk-manage-group-users/index.mdx @@ -51,7 +51,7 @@ Note: Refactor the code as per your requirement. } } ElseIf ($action -eq "remove") { - m365 entra m365group user remove --groupId $group.id --userName $user.mail --force + m365 entra m365group user remove --groupId $group.id --userNames $user.mail --force Write-Host $user.mail " removed from " $group.mailNickname } Else { diff --git a/docs/docs/sample-scripts/entra/delete-m365-groups-and-sharepoint-sites/assets/sample.json b/docs/docs/sample-scripts/entra/delete-m365-groups-and-sharepoint-sites/assets/sample.json index 042ba0e0113..b2c41d84174 100644 --- a/docs/docs/sample-scripts/entra/delete-m365-groups-and-sharepoint-sites/assets/sample.json +++ b/docs/docs/sample-scripts/entra/delete-m365-groups-and-sharepoint-sites/assets/sample.json @@ -11,7 +11,7 @@ "Another example how you can delete all Microsoft 365 Groups and SharePoint Online sites in your development environment." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/delete-m365-groups/assets/sample.json b/docs/docs/sample-scripts/entra/delete-m365-groups/assets/sample.json index 3e0a3828d3a..dc51e727013 100644 --- a/docs/docs/sample-scripts/entra/delete-m365-groups/assets/sample.json +++ b/docs/docs/sample-scripts/entra/delete-m365-groups/assets/sample.json @@ -11,7 +11,7 @@ "There are so many different ways to create Microsoft 365 groups. Teams, Planner, SharePoint team sites, etc. — you can accumulate a lot of them very fast. Use this script below to delete the ones you no longer need." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/assets/sample.json b/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/assets/sample.json index 57008d4612f..36b1de39454 100644 --- a/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/assets/sample.json +++ b/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/assets/sample.json @@ -11,7 +11,7 @@ "This script can be used to replace the membership of a user for a selected list of Groups. It might be useful when a person changes role in an organization or is about to leave it." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/index.mdx b/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/index.mdx index 1c5663f655d..391c808ff50 100644 --- a/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/index.mdx +++ b/docs/docs/sample-scripts/entra/replace-membership-of-selected-groups/index.mdx @@ -111,7 +111,7 @@ This script can be used to replace the membership of a user for a selected list try { Write-Host "Removing $oldUser..." - m365 entra m365group user remove --groupId $groupId --userName $oldUser | Get-CLIValue + m365 entra m365group user remove --groupId $groupId --userNames $oldUser | Get-CLIValue } catch { Write-Host $_.Exception.Message -ForegroundColor Red diff --git a/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/assets/sample.json b/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/assets/sample.json index 825b7370ce9..7df9e2061ce 100644 --- a/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/assets/sample.json +++ b/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/assets/sample.json @@ -11,7 +11,7 @@ "Find all the Microsoft 365 Groups that a user is an Owner of and replace them with someone else useful for when an employee leaves and ownership needs to be updated." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/index.mdx b/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/index.mdx index 7f98a594c7d..f918fbc9811 100644 --- a/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/index.mdx +++ b/docs/docs/sample-scripts/entra/replace-owner-with-a-different-one/index.mdx @@ -67,7 +67,7 @@ Find all the Microsoft 365 Groups that a user is an Owner of and replace them wi $hasOwner = $null # verify if the old user is in the owners list - $hasOwner = m365 entra m365group user list --groupId $group.id --query "[?userType=='Owner' && userPrincipalName=='$oldUser'].[id]" | Get-CLIValue + $hasOwner = m365 entra m365group user list --groupId $group.id --role owner --query "[?userPrincipalName=='$oldUser'].[id]" | Get-CLIValue if ($hasOwner -ne $null) { Write-Host "Found $oldUser" -ForegroundColor Green try { @@ -80,7 +80,7 @@ Find all the Microsoft 365 Groups that a user is an Owner of and replace them wi try { Write-Host "Removing $oldUser permissions..." - m365 entra m365group user remove --groupId $group.id --userName $oldUser | Get-CLIValue + m365 entra m365group user remove --groupId $group.id --userNames $oldUser | Get-CLIValue } catch { Write-Host $_.Exception.Message -ForegroundColor Red diff --git a/docs/docs/sample-scripts/entra/scan-groups-with-user-names/assets/sample.json b/docs/docs/sample-scripts/entra/scan-groups-with-user-names/assets/sample.json index 4bce4d1b6dd..53b74130d0a 100644 --- a/docs/docs/sample-scripts/entra/scan-groups-with-user-names/assets/sample.json +++ b/docs/docs/sample-scripts/entra/scan-groups-with-user-names/assets/sample.json @@ -11,7 +11,7 @@ "We can use the group naming policy to enforce a consistent naming strategy for groups created by users in our organization. A naming policy can help us and our users identify the function of the group. We can use the policy to block specific words from being used in group names and aliases. But what if we need to find out the list of Microsoft 365 groups created with user’s givenName or surname as their mail? This sample script scans the Microsoft 365 groups that may contain user’s first or last name as the group mail." ], "products": [ - "AAD" + "Entra" ], "categories": [], "tags": [ diff --git a/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/assets/sample.json b/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/assets/sample.json index fafcb917963..ca3151d89a0 100644 --- a/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/assets/sample.json +++ b/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/assets/sample.json @@ -25,7 +25,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "7.7.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/index.mdx b/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/index.mdx index 0543946047c..a257861a511 100644 --- a/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/index.mdx +++ b/docs/docs/sample-scripts/spo/add-ci-cd-pipeline/index.mdx @@ -83,7 +83,7 @@ Depending on what authentication method you chose, you will need to add the foll For the application login method: -- `APP_ID` - client id of the registered AAD application +- `APP_ID` - client id of the registered Entra application - `CERTIFICATE_ENCODED` - application's encoded certificate - `CERTIFICATE_PASSWORD` - certificate password. This applies only if the certificate is encoded which is the recommended approach - `TENANT` - Tenant ID @@ -214,10 +214,10 @@ For the application login method you will need to fill in the following variable } if ($Scope -eq "tenant") { - m365 spfx project github workflow add --loginMethod $LoginMethod --name $Name --branchName $BranchName $ManuallyTriggerStr $SkipFeatureDeploymentStr --overwrite + m365 spfx project github workflow add --loginMethod $LoginMethod --name $Name --branchName $BranchName $ManuallyTriggerStr $SkipFeatureDeploymentStr } else { - m365 spfx project github workflow add --loginMethod $LoginMethod --name $Name --branchName $BranchName --scope "sitecollection" --siteUrl $SiteUrl $ManuallyTriggerStr $SkipFeatureDeploymentStr --overwrite + m365 spfx project github workflow add --loginMethod $LoginMethod --name $Name --branchName $BranchName --scope "sitecollection" --siteUrl $SiteUrl $ManuallyTriggerStr $SkipFeatureDeploymentStr } # show github guidance diff --git a/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/assets/sample.json b/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/assets/sample.json index c1d6d0f1989..856bcb1f9ca 100644 --- a/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/assets/sample.json +++ b/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/assets/sample.json @@ -23,7 +23,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.6.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/index.mdx b/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/index.mdx index e1672462cc3..2621fcb728f 100644 --- a/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/index.mdx +++ b/docs/docs/sample-scripts/spo/copy-library-folders-and-contents-to-another-library/index.mdx @@ -52,7 +52,7 @@ The following script shows how you can copy all library folders and contents to # Copy everything (folders and files) to this target level 1 folder Write-Host "Copying '$($Folder.ServerRelativeUrl)' to '$DestinationFolderUrl'" -ForegroundColor Cyan - m365 spo folder copy --webUrl $SiteURL --sourceUrl $Folder.ServerRelativeUrl --targetUrl $DestinationFolderUrl --allowSchemaMismatch + m365 spo folder copy --webUrl $SiteURL --sourceUrl $Folder.ServerRelativeUrl --targetUrl $DestinationFolderUrl } # Copy files from root diff --git a/docs/docs/sample-scripts/spo/delete-non-group-connected-modern-sites/index.mdx b/docs/docs/sample-scripts/spo/delete-non-group-connected-modern-sites/index.mdx index 6208720995e..b7b00de7054 100644 --- a/docs/docs/sample-scripts/spo/delete-non-group-connected-modern-sites/index.mdx +++ b/docs/docs/sample-scripts/spo/delete-non-group-connected-modern-sites/index.mdx @@ -30,7 +30,7 @@ When you delete Microsoft 365 groups, the modern group-connected team sites get $progress++ write-host $progress / $total":" $site.Title write-host $site.Url - m365 spo site remove --url $site.Url + m365 spo site remove --url $site.Url --force } ``` @@ -74,7 +74,7 @@ When you delete Microsoft 365 groups, the modern group-connected team sites get siteUrl=$(echo ${site} | jq -r '.Url') echo "Deleting site..." echo $siteUrl - m365 spo site remove --url $siteUrl + m365 spo site remove --url $siteUrl --force done ``` diff --git a/docs/docs/sample-scripts/spo/list-all-checked-out-files/assets/sample.json b/docs/docs/sample-scripts/spo/list-all-checked-out-files/assets/sample.json index 04c99a3da44..fdccfd3e927 100644 --- a/docs/docs/sample-scripts/spo/list-all-checked-out-files/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-all-checked-out-files/assets/sample.json @@ -21,7 +21,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.3.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-all-checked-out-files/index.mdx b/docs/docs/sample-scripts/spo/list-all-checked-out-files/index.mdx index 84c0de04a53..85b95924342 100644 --- a/docs/docs/sample-scripts/spo/list-all-checked-out-files/index.mdx +++ b/docs/docs/sample-scripts/spo/list-all-checked-out-files/index.mdx @@ -31,7 +31,7 @@ This script will retrieve all the checked out files in a particular site. $resultsForSite = @() foreach($library in $allLibs){ - $allDocs = m365 spo file list --webUrl $siteURL --folder $library.Url --recursive -o json | ConvertFrom-Json + $allDocs = m365 spo file list --webUrl $siteURL --folder $library.RootFolder.ServerRelativeUrl --recursive -o json | ConvertFrom-Json foreach($document in $allDocs){ if($document.CheckOutType -eq [int64]0){ @@ -122,7 +122,7 @@ Depending on the number of sites in your .csv file, the number of libraries as w foreach($lib in $allLibraries){ #Get all the documents - $allDocs = m365 spo file list --webUrl $row.siteURL --folder $lib.Url --recursive -o json | ConvertFrom-Json + $allDocs = m365 spo file list --webUrl $row.siteURL --folder $lib.RootFolder.ServerRelativeUrl --recursive -o json | ConvertFrom-Json foreach($docu in $allDocs){ if($docu.CheckOutType -eq [int64]0){ diff --git a/docs/docs/sample-scripts/spo/list-all-files-specific-name/assets/sample.json b/docs/docs/sample-scripts/spo/list-all-files-specific-name/assets/sample.json index 40b03bc4bd3..370cc24db1e 100644 --- a/docs/docs/sample-scripts/spo/list-all-files-specific-name/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-all-files-specific-name/assets/sample.json @@ -21,7 +21,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.3.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-all-files-specific-name/index.mdx b/docs/docs/sample-scripts/spo/list-all-files-specific-name/index.mdx index 3ffd6ac7b1a..a59ed791f78 100644 --- a/docs/docs/sample-scripts/spo/list-all-files-specific-name/index.mdx +++ b/docs/docs/sample-scripts/spo/list-all-files-specific-name/index.mdx @@ -42,7 +42,7 @@ This script will retrieve all the files in a site that have a specific word (i.e $i++ if ($ShowProgress) { Write-Host "Processing '$($lib.Title)' - ($i/$($allLibs.length))" } - $allFiles = m365 spo file list --webUrl $site --folder $lib.Url --recursive -o json | ConvertFrom-Json + $allFiles = m365 spo file list --webUrl $site --folder $lib.RootFolder.ServerRelativeUrl --recursive -o json | ConvertFrom-Json foreach ($file in $allFiles) { if ($ShowProgress) { Write-Host "Processing file '$($file.ServerRelativeUrl)'" } diff --git a/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/assets/sample.json b/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/assets/sample.json index 7d232727c8a..f02617597d5 100644 --- a/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/assets/sample.json @@ -22,7 +22,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.3.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/index.mdx b/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/index.mdx index c644af207f8..6ce491e44ad 100644 --- a/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/index.mdx +++ b/docs/docs/sample-scripts/spo/list-all-list-folders-itemcount/index.mdx @@ -47,10 +47,10 @@ List all Lists, the folders and sub folders in a given site, and output the item foreach ($list in $allLists) { if ($false -eq $list.Hidden -and $list.BaseTemplate -eq "101") { - Write-Output "Processing $($list.Url)" - [void]$results.Add([PSCustomObject]@{ Url = $list.Url; ItemCount = $list.ItemCount; Type = "List"; }) + Write-Output "Processing $($list.RootFolder.ServerRelativeUrl)" + [void]$results.Add([PSCustomObject]@{ Url = $list.RootFolder.ServerRelativeUrl; ItemCount = $list.ItemCount; Type = "List"; }) - Get-Folders $siteUrl $list.Url + Get-Folders $siteUrl $list.RootFolder.ServerRelativeUrl } } diff --git a/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/assets/sample.json b/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/assets/sample.json index 6c1143f8d56..d76413f243d 100644 --- a/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/assets/sample.json @@ -21,7 +21,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.4.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/index.mdx b/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/index.mdx index c64badbb680..8781236985c 100644 --- a/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/index.mdx +++ b/docs/docs/sample-scripts/spo/list-all-sites-with-app-installed/index.mdx @@ -53,7 +53,7 @@ This script helps you to list all sites in SharePoint Online, where a particular Write-Host "Processing site $($site.Url)..." $appInstance = m365 spo app instance list --siteUrl $site.Url --query $queryText | ConvertFrom-Json - if ($appInstance) { + if ($appInstance.count -gt 0) { $results += [pscustomobject]@{ SiteURL = $site.Url Title = $site.Title diff --git a/docs/docs/sample-scripts/spo/list-empty-folders-on-site/assets/sample.json b/docs/docs/sample-scripts/spo/list-empty-folders-on-site/assets/sample.json index d177b77a00c..1336e715029 100644 --- a/docs/docs/sample-scripts/spo/list-empty-folders-on-site/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-empty-folders-on-site/assets/sample.json @@ -22,7 +22,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.9.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-empty-folders-on-site/index.mdx b/docs/docs/sample-scripts/spo/list-empty-folders-on-site/index.mdx index 87733563a1a..72ccd02dc2f 100644 --- a/docs/docs/sample-scripts/spo/list-empty-folders-on-site/index.mdx +++ b/docs/docs/sample-scripts/spo/list-empty-folders-on-site/index.mdx @@ -39,7 +39,7 @@ The script loops through each document library in a site where the folder size s foreach($library in $allLibs) { Write-Host "Processing document library: $($library.Title)" - $allFolders = m365 spo folder list --webUrl $WebUrl --parentFolderUrl $library.Url --fields "Name,ServerRelativeUrl,TimeCreated,CreatedBy,TimeLastModified" --recursive --filter "ItemCount eq 0" -o json | ConvertFrom-Json + $allFolders = m365 spo folder list --webUrl $WebUrl --parentFolderUrl $library.RootFolder.ServerRelativeUrl --fields "Name,ServerRelativeUrl,TimeCreated,CreatedBy,TimeLastModified" --recursive --filter "ItemCount eq 0" -o json | ConvertFrom-Json foreach($folder in $allFolders) { $resultsEmptyFolders += [PSCustomObject]@{ diff --git a/docs/docs/sample-scripts/spo/list-large-files-within-a-site/assets/sample.json b/docs/docs/sample-scripts/spo/list-large-files-within-a-site/assets/sample.json index 428601a5d48..7e0f3ae42fd 100644 --- a/docs/docs/sample-scripts/spo/list-large-files-within-a-site/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-large-files-within-a-site/assets/sample.json @@ -21,7 +21,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.3.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-large-files-within-a-site/index.mdx b/docs/docs/sample-scripts/spo/list-large-files-within-a-site/index.mdx index 421fd707c5e..0ace118d213 100644 --- a/docs/docs/sample-scripts/spo/list-large-files-within-a-site/index.mdx +++ b/docs/docs/sample-scripts/spo/list-large-files-within-a-site/index.mdx @@ -43,7 +43,7 @@ The following script will help you find every files in a specific SharePoint Onl $i++ if ($ShowProgress) { Write-Host "Processing '$($lib.Title)' - ($i/$($allLibs.length))" } - $allFiles = m365 spo file list --webUrl $site --folder $lib.Url --recursive -o json | ConvertFrom-Json + $allFiles = m365 spo file list --webUrl $site --folder $lib.RootFolder.ServerRelativeUrl --recursive -o json | ConvertFrom-Json foreach ($file in $allFiles) { if ($ShowProgress) { Write-Host "Processing file '$($file.ServerRelativeUrl)'" } diff --git a/docs/docs/sample-scripts/spo/list-site-collection-lists/assets/sample.json b/docs/docs/sample-scripts/spo/list-site-collection-lists/assets/sample.json index cd4c4d5a1f6..e0e1f2e357a 100644 --- a/docs/docs/sample-scripts/spo/list-site-collection-lists/assets/sample.json +++ b/docs/docs/sample-scripts/spo/list-site-collection-lists/assets/sample.json @@ -22,7 +22,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "6.3.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/list-site-collection-lists/index.mdx b/docs/docs/sample-scripts/spo/list-site-collection-lists/index.mdx index a3f74f16859..a0f687094cf 100644 --- a/docs/docs/sample-scripts/spo/list-site-collection-lists/index.mdx +++ b/docs/docs/sample-scripts/spo/list-site-collection-lists/index.mdx @@ -56,7 +56,7 @@ This script helps you to list and export all site collection and their lists Sha $results += [pscustomobject][ordered]@{ Type = "list" Title = $list.Title - Url = $list.Url + Url = $list.RootFolder.ServerRelativeUrl Template = $list.BaseTemplate } } diff --git a/docs/docs/sample-scripts/spo/remove-orphaned-redirect-sites/index.mdx b/docs/docs/sample-scripts/spo/remove-orphaned-redirect-sites/index.mdx index 5f8c0cfc655..7c2cb1625c6 100644 --- a/docs/docs/sample-scripts/spo/remove-orphaned-redirect-sites/index.mdx +++ b/docs/docs/sample-scripts/spo/remove-orphaned-redirect-sites/index.mdx @@ -42,7 +42,7 @@ Changing the URL of a site results in a new site type: a Redirect Site. However } If($body.StatusCode -eq "404"){ Write-Host -f Red " Target location no longer exists, should be removed" - m365 spo site remove --url $siteUrl + m365 spo site remove --url $siteUrl --force } } } @@ -87,7 +87,7 @@ Changing the URL of a site results in a new site type: a Redirect Site. However siteUrl=$(echo ${site} | jq -r '.Url') echo "Deleting site..." echo $siteUrl - m365 spo site remove --url $siteUrl + m365 spo site remove --url $siteUrl --force done ``` diff --git a/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/assets/sample.json b/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/assets/sample.json index 30944b74fe7..5156c288260 100644 --- a/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/assets/sample.json +++ b/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/assets/sample.json @@ -21,7 +21,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "7.4.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/index.mdx b/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/index.mdx index 71e72096843..7b95cbed89f 100644 --- a/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/index.mdx +++ b/docs/docs/sample-scripts/spo/sp-add-ins-and-azure-acs-retirement-report/index.mdx @@ -186,7 +186,7 @@ function GetRetirementStatus { foreach ($List in $Lists) { $EventReveiversOnList = m365 spo eventreceiver list --webUrl $WebUrl --listId $List.Id --query "[?!contains(ReceiverAssembly,'Microsoft.')]" --output json | ConvertFrom-Json - $EventReceivers += GetEventReceiversDataToOutputList -EventReceivers $EventReveiversOnList -Location "List" -LocationUrl $List.Url + $EventReceivers += GetEventReceiversDataToOutputList -EventReceivers $EventReveiversOnList -Location "List" -LocationUrl $List.RootFolder.ServerRelativeUrl } return $EventReceivers } diff --git a/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/assets/sample.json b/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/assets/sample.json index 3d86bbc6045..3c14ead9ac0 100644 --- a/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/assets/sample.json +++ b/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/assets/sample.json @@ -22,7 +22,7 @@ "metadata": [ { "key": "CLI-FOR-MICROSOFT365", - "value": "7.0.0" + "value": "10.0.0" } ], "thumbnails": [ diff --git a/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/index.mdx b/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/index.mdx index 88b2da90032..e7fadd76b2a 100644 --- a/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/index.mdx +++ b/docs/docs/sample-scripts/teams/create-team-and-add-members-and-owners/index.mdx @@ -86,7 +86,7 @@ This sample script shows you how to create a Team and add members and owners usi # remove yourself from the owners group if ($removeYourSelfFromOwners -and $owners.IndexOf($whoAmI.connectedAs) -eq -1) { Write-Host "Removing $($whoAmI.connectedAs) from the owners list" - m365 entra m365group user remove --groupId $group.id --userName $whoAmI.connectedAs --force + m365 entra m365group user remove --groupId $group.id --userNames $whoAmI.connectedAs --force } Write-Host "Completed." From cd9d42a373e7d73d58a944dd5b3c553cb819f6dc Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:35:54 +0100 Subject: [PATCH 41/42] Updates release notes --- docs/docs/about/release-notes.mdx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/docs/about/release-notes.mdx b/docs/docs/about/release-notes.mdx index 9892e7b9dd9..93d5d3c836a 100644 --- a/docs/docs/about/release-notes.mdx +++ b/docs/docs/about/release-notes.mdx @@ -13,6 +13,10 @@ sidebar_position: 3 - [spe container list](../cmd/spe/container/container-list.mdx) - lists containers of a specific Container Type [#6082](https://github.com/pnp/cli-microsoft365/issues/6082) - [spe containertype get](../cmd/spe/containertype/containertype-get.mdx) - retrieves a specific Container Type [#5991](https://github.com/pnp/cli-microsoft365/issues/5991) +**SharePoint Premium:** + +- [spp model remove](../cmd/spp/model/model-remove.mdx) - deletes a document understanding model [#6118](https://github.com/pnp/cli-microsoft365/issues/6118) + **Viva:** - [viva engage community set](../cmd/viva/engage/engage-community-set.mdx) - updates an existing Viva Engage community [#6279](https://github.com/pnp/cli-microsoft365/issues/6279) @@ -20,15 +24,19 @@ sidebar_position: 3 ### Changes - fixed using env var for login command [#6469](https://github.com/pnp/cli-microsoft365/issues/6469) -- updated v10 upgrade guidance [#6363](https://github.com/pnp/cli-microsoft365/issues/6363) -- fixed missing 'sp' aliases in some 'enterpriseapp' commands [#6476](https://github.com/pnp/cli-microsoft365/issues/6476) +- fixed missing `sp` aliases in some `enterpriseapp` commands [#6476](https://github.com/pnp/cli-microsoft365/issues/6476) - updated `entra m365group` commands to accept displayName option [#6147](https://github.com/pnp/cli-microsoft365/issues/6147) - updated linting to make it faster in GitHub workflows [#6348](https://github.com/pnp/cli-microsoft365/issues/6348) -- enhanced `entra m365group get` with additional properties [#6148](https://github.com/pnp/cli-microsoft365/issues/6148) -- enhanced `spo contenttype` commands with Parent expand [#6329](https://github.com/pnp/cli-microsoft365/issues/6329) +- enhanced [entra m365group get](../cmd/entra/m365group/m365group-get.mdx) with additional properties [#6148](https://github.com/pnp/cli-microsoft365/issues/6148) +- enhanced `spo contenttype` commands with Parent property [#6329](https://github.com/pnp/cli-microsoft365/issues/6329) - updated docs on how to log in to CLI [#6343](https://github.com/pnp/cli-microsoft365/issues/6343) -- enhanced `graph subscription add` command with additional options [#6158](https://github.com/pnp/cli-microsoft365/issues/6158) -- fixed syntax highlighting in docs +- enhanced [graph subscription add](../cmd/graph/subscription/subscription-add.mdx) command with additional options [#6158](https://github.com/pnp/cli-microsoft365/issues/6158) +- fixed CSV-parsing issues for commands that accept a CSV-input [#5825](https://github.com/pnp/cli-microsoft365/issues/5825) +- added `copilot` alias to `pp chatbot` commands. Closes [#6261](https://github.com/pnp/cli-microsoft365/issues/6261) +- enhanced [spo listitem batch remove](../cmd/spo/listitem/listitem-batch-remove.mdx) command to allow case-insensitive column names [#6419](https://github.com/pnp/cli-microsoft365/issues/6419) +- enhanced `spo site admin` commands to retrieve the site ID as admin. Closes [#6443](https://github.com/pnp/cli-microsoft365/issues/6443) +- fixed [spo listitem batch set](../cmd/spo/listitem/listitem-batch-set.mdx) command to escape XML-special characters [#6430](https://github.com/pnp/cli-microsoft365/issues/6430) +- aligned all sample scripts with the latest v10 release [#6369](https://github.com/pnp/cli-microsoft365/issues/6369) ## [v10.0.0](https://github.com/pnp/cli-microsoft365/releases/tag/v10.0.0) From b39133d118cb93975638aa6c2b41738e551dc75d Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Mon, 25 Nov 2024 17:38:34 +0000 Subject: [PATCH 42/42] review comments --- src/m365/spe/commands/container/container-get.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m365/spe/commands/container/container-get.ts b/src/m365/spe/commands/container/container-get.ts index bafbd4e5d84..b01d7ee21c9 100644 --- a/src/m365/spe/commands/container/container-get.ts +++ b/src/m365/spe/commands/container/container-get.ts @@ -3,7 +3,7 @@ import { Logger } from '../../../../cli/Logger.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; import request, { CliRequestOptions } from '../../../../request.js'; -import { ContainerTypeProperties } from '../../ContainerTypeProperties.js'; +import { ContainerProperties } from '../../ContainerProperties.js'; interface CommandArgs { options: Options; @@ -53,7 +53,7 @@ class SpeContainerGetCommand extends GraphCommand { }; try { - const res = await request.get(requestOptions); + const res = await request.get(requestOptions); await logger.log(res); } catch (err: any) {