From 7756d111ca80a196dfb2182b571e6838a6ee9123 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Wed, 6 Sep 2023 09:43:43 +0000 Subject: [PATCH 01/10] Add name option --- docs/docs/cmd/teams/user/user-app-remove.mdx | 15 ++- .../commands/user/user-app-remove.spec.ts | 121 +++++++++++++++++- .../teams/commands/user/user-app-remove.ts | 54 +++++++- 3 files changed, 176 insertions(+), 14 deletions(-) diff --git a/docs/docs/cmd/teams/user/user-app-remove.mdx b/docs/docs/cmd/teams/user/user-app-remove.mdx index cda4387a971..91640291b71 100644 --- a/docs/docs/cmd/teams/user/user-app-remove.mdx +++ b/docs/docs/cmd/teams/user/user-app-remove.mdx @@ -13,8 +13,11 @@ m365 teams user app remove [options] ## Options ```md definition-list -`--id ` -: The unique id of the app instance installed for the user. +`--id [id]` +: The unique id of the app instance installed for the user. Specify either `id` or `name`. + +`--name [name]` +: Name of the app instance installed for the user. Specify either `id` or `name`. `--userId ` : The ID of the user to uninstall the app for. @@ -32,12 +35,18 @@ Do not use the ID from the manifest of the zip app package or the id from the Mi ## Examples -Uninstall an app for the specified user. +Uninstall an app by id for the specified user. ```sh m365 teams user app remove --id YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY= --userId 2609af39-7775-4f94-a3dc-0dd67657e900 ``` +Uninstall an app by name for the specified user. + +```sh +m365 teams user app remove --name HelloWorld --userId 2609af39-7775-4f94-a3dc-0dd67657e900 +``` + ## Response The command won't return a response on success. diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index d2e479b23d1..9e8aa6b61d6 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -50,6 +50,7 @@ describe(commands.USER_APP_REMOVE, () => { afterEach(() => { sinonUtil.restore([ + request.get, request.delete, Cli.prompt ]); @@ -68,6 +69,24 @@ describe(commands.USER_APP_REMOVE, () => { assert.notStrictEqual(command.description, null); }); + it('fails validation if both id and name options are passed', async () => { + const actual = await command.validate({ + options: { + id: 'e3e29acb-8c79-412b-b746-e6c39ff4cd22', + name: 'TeamsApp' + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if both id and name options are not passed', async () => { + const actual = await command.validate({ + options: { + } + }, commandInfo); + assert.notStrictEqual(actual, true); + }); + it('fails validation if the userId is not a valid guid.', async () => { const actual = await command.validate({ options: { @@ -95,10 +114,12 @@ describe(commands.USER_APP_REMOVE, () => { id: 'YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=' } } as any); + let promptIssued = false; if (promptOptions && promptOptions.type === 'confirm') { promptIssued = true; } + assert(promptIssued); }); @@ -114,11 +135,12 @@ describe(commands.USER_APP_REMOVE, () => { assert(requestDeleteSpy.notCalled); }); - it('removes the app for the specified user when confirmation is specified (debug)', async () => { + it('removes the app by id for the specified user when confirmation is specified (debug)', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) > -1) { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { return; } + throw 'Invalid request'; }); @@ -132,11 +154,12 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); - it('removes the app for the specified user when prompt is confirmed (debug)', async () => { + it('removes the app by id for the specified user when prompt is confirmed (debug)', async () => { sinon.stub(request, 'delete').callsFake((opts) => { - if ((opts.url as string).indexOf(`/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) > -1) { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { return Promise.resolve(); } + throw 'Invalid request'; }); @@ -152,6 +175,91 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); + it('removes the app by name for the specified user when prompt is confirmed (debug)', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { + "value": [ + { + "id": "YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=", + "displayName": "TeamsApp" + } + ] + }; + } + + throw 'Invalid request'; + }); + + sinon.stub(request, 'delete').callsFake((opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { + return Promise.resolve(); + } + + throw 'Invalid request'; + }); + + sinonUtil.restore(Cli.prompt); + sinon.stub(Cli, 'prompt').resolves({ continue: true }); + + await command.action(logger, { + options: { + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + debug: true + } + } as any); + }); + + it('fails to get Teams app when app does not exists', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { value: [] }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { + options: { + debug: true, + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + force: true + } + } as any), new CommandError('The specified Teams app does not exist')); + }); + + it('handles error when multiple Teams apps with the specified name found', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { + "value": [ + { + "id": "ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=", + "displayName": "TeamsApp" + }, + { + "id": "NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk", + "displayName": "TeamsApp" + } + ] + }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { + options: { + debug: true, + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + force: true + } + } as any), new CommandError('Multiple Teams apps with name TeamsApp found. Please choose one of these ids: ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=, NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk')); + }); + it('correctly handles error while removing teams app', async () => { const error = { "error": { @@ -166,9 +274,10 @@ describe(commands.USER_APP_REMOVE, () => { }; sinon.stub(request, 'delete').callsFake(async (opts) => { - if ((opts.url as string).indexOf(`/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) > -1) { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { throw error; } + throw 'Invalid request'; }); @@ -180,4 +289,4 @@ describe(commands.USER_APP_REMOVE, () => { } } as any), new CommandError(error.error.message)); }); -}); +}); \ No newline at end of file diff --git a/src/m365/teams/commands/user/user-app-remove.ts b/src/m365/teams/commands/user/user-app-remove.ts index eb98a07bf51..4cea79b1bf0 100644 --- a/src/m365/teams/commands/user/user-app-remove.ts +++ b/src/m365/teams/commands/user/user-app-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 { validation } from '../../../../utils/validation.js'; import GraphCommand from '../../../base/GraphCommand.js'; import commands from '../../commands.js'; @@ -11,7 +12,8 @@ interface CommandArgs { } interface Options extends GlobalOptions { - id: string; + id?: string; + name?: string; userId: string; force?: boolean; } @@ -31,12 +33,15 @@ class TeamsUserAppRemoveCommand extends GraphCommand { this.#initTelemetry(); this.#initOptions(); this.#initValidators(); + this.#initOptionSets(); } #initTelemetry(): void { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { - force: (!!args.options.force).toString() + force: (!!args.options.force).toString(), + id: typeof args.options.id !== 'undefined', + name: typeof args.options.name !== 'undefined' }); }); } @@ -44,7 +49,10 @@ class TeamsUserAppRemoveCommand extends GraphCommand { #initOptions(): void { this.options.unshift( { - option: '--id ' + option: '--id [id]' + }, + { + option: '--name [name]' }, { option: '--userId ' @@ -67,12 +75,21 @@ class TeamsUserAppRemoveCommand extends GraphCommand { ); } + #initOptionSets(): void { + this.optionSets.push({ options: ['id', 'name'] }); + } + public async commandAction(logger: Logger, args: CommandArgs): Promise { const removeApp = async (): Promise => { + const appId: string = await this.getAppId(args); const endpoint: string = `${this.resource}/v1.0`; + if (this.verbose) { + await logger.logToStderr(`Removing app with ID ${args.options.id} for user ${args.options.userId}`); + } + const requestOptions: CliRequestOptions = { - url: `${endpoint}/users/${args.options.userId}/teamwork/installedApps/${args.options.id}`, + url: `${endpoint}/users/${args.options.userId}/teamwork/installedApps/${appId}`, headers: { 'accept': 'application/json;odata.metadata=none' }, @@ -95,7 +112,7 @@ class TeamsUserAppRemoveCommand extends GraphCommand { type: 'confirm', name: 'continue', default: false, - message: `Are you sure you want to remove the app with id ${args.options.id} for user ${args.options.userId}?` + message: `Are you sure you want to remove the app ${args.options.id || args.options.name} for user ${args.options.userId}?` }); if (result.continue) { @@ -103,6 +120,33 @@ class TeamsUserAppRemoveCommand extends GraphCommand { } } } + + private async getAppId(args: CommandArgs): Promise { + if (args.options.id) { + return args.options.id; + } + + const requestOptions: CliRequestOptions = { + url: `${this.resource}/v1.0/users/${args.options.userId}/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq '${formatting.encodeQueryParameter(args.options.name as string)}'`, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + + const response = await request.get<{ value: { id: string; }[] }>(requestOptions); + const app: { id: string; } | undefined = response.value[0]; + + if (!app) { + throw `The specified Teams app does not exist`; + } + + if (response.value.length > 1) { + throw `Multiple Teams apps with name ${args.options.name} found. Please choose one of these ids: ${response.value.map(x => x.id).join(', ')}`; + } + + return app.id; + } } export default new TeamsUserAppRemoveCommand(); \ No newline at end of file From 7f3e7234a98fe282d745257682f97d0eb3ebdfbd Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Sun, 8 Oct 2023 20:37:20 +0530 Subject: [PATCH 02/10] Update user-app-remove.spec.ts --- .../commands/user/user-app-remove.spec.ts | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index 9e8aa6b61d6..1b484dd9338 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -69,24 +69,6 @@ describe(commands.USER_APP_REMOVE, () => { assert.notStrictEqual(command.description, null); }); - it('fails validation if both id and name options are passed', async () => { - const actual = await command.validate({ - options: { - id: 'e3e29acb-8c79-412b-b746-e6c39ff4cd22', - name: 'TeamsApp' - } - }, commandInfo); - assert.notStrictEqual(actual, true); - }); - - it('fails validation if both id and name options are not passed', async () => { - const actual = await command.validate({ - options: { - } - }, commandInfo); - assert.notStrictEqual(actual, true); - }); - it('fails validation if the userId is not a valid guid.', async () => { const actual = await command.validate({ options: { @@ -289,4 +271,4 @@ describe(commands.USER_APP_REMOVE, () => { } } as any), new CommandError(error.error.message)); }); -}); \ No newline at end of file +}); From b4f49b0adb9c071b06bc7567281efdfee35e5397 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 10:26:01 +0530 Subject: [PATCH 03/10] Update user-app-remove.ts --- src/m365/teams/commands/user/user-app-remove.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.ts b/src/m365/teams/commands/user/user-app-remove.ts index bf64fec54a9..af0ac497d00 100644 --- a/src/m365/teams/commands/user/user-app-remove.ts +++ b/src/m365/teams/commands/user/user-app-remove.ts @@ -41,7 +41,7 @@ class TeamsUserAppRemoveCommand extends GraphCommand { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { id: typeof args.options.id !== 'undefined', - name: typeof args.options.name !== 'undefined' + name: typeof args.options.name !== 'undefined', userId: typeof args.options.userId !== 'undefined', userName: typeof args.options.userName !== 'undefined', force: (!!args.options.force).toString() @@ -163,4 +163,4 @@ class TeamsUserAppRemoveCommand extends GraphCommand { } } -export default new TeamsUserAppRemoveCommand(); \ No newline at end of file +export default new TeamsUserAppRemoveCommand(); From 21c666149686034a782e3c32f5b2323accbf7f12 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 10:44:02 +0530 Subject: [PATCH 04/10] Update user-app-remove.ts --- src/m365/teams/commands/user/user-app-remove.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.ts b/src/m365/teams/commands/user/user-app-remove.ts index af0ac497d00..48b558251da 100644 --- a/src/m365/teams/commands/user/user-app-remove.ts +++ b/src/m365/teams/commands/user/user-app-remove.ts @@ -102,7 +102,7 @@ class TeamsUserAppRemoveCommand extends GraphCommand { } const requestOptions: CliRequestOptions = { - url: `${endpoint}/users/${args.options.userId}/teamwork/installedApps/${appId}`, + url: `${endpoint}/users/${formatting.encodeQueryParameter(userId)}/teamwork/installedApps/${appId}`, headers: { 'accept': 'application/json;odata.metadata=none' }, @@ -121,13 +121,6 @@ class TeamsUserAppRemoveCommand extends GraphCommand { await removeApp(); } else { - const result = await Cli.prompt<{ continue: boolean }>({ - type: 'confirm', - name: 'continue', - default: false, - message: `Are you sure you want to remove the app ${args.options.id || args.options.name} for user ${args.options.userId}?` - }); - const result = await Cli.promptForConfirmation({ message: `Are you sure you want to remove the app with id ${args.options.id} for user ${args.options.userId ?? args.options.userName}?` }); if (result) { await removeApp(); From cfc847031247ea4d8fb9e5d67bcbaa2d3a6040f1 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 10:44:29 +0530 Subject: [PATCH 05/10] Update user-app-remove.spec.ts --- .../commands/user/user-app-remove.spec.ts | 55 ++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index ec60e7b2a34..f0bbf0c11f2 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -143,6 +143,41 @@ describe(commands.USER_APP_REMOVE, () => { assert(requestDeleteSpy.notCalled); }); + it('removes the app for the specified user when confirmation is specified (debug)', async () => { + sinon.stub(request, 'delete').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { + return; + } + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + userId: userId, + id: appId, + debug: true, + force: true + } + } as any); + }); + + it('removes the app for the specified user using username when confirmation is specified.', async () => { + sinon.stub(request, 'delete').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/users/${formatting.encodeQueryParameter(userName)}/teamwork/installedApps/${appId}`) > -1) { + return Promise.resolve(); + } + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + userName: userName, + id: appId, + force: true + } + } as any); + }); + it('removes the app by id for the specified user when confirmation is specified (debug)', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { @@ -162,7 +197,23 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); -it('removes the app by id for the specified user when prompt is confirmed (debug)', async () => { + it('removes the app for the specified user using username', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { + return Promise.resolve(); + } + throw 'Invalid request'; + }); + + await command.action(logger, { + options: { + userName: userName, + id: appId + } + } as any); + }); + + it('removes the app by id for the specified user when prompt is confirmed (debug)', async () => { sinon.stub(request, 'delete').callsFake((opts) => { if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { return Promise.resolve(); @@ -297,4 +348,4 @@ it('removes the app by id for the specified user when prompt is confirmed (debug } } as any), new CommandError(error.error.message)); }); -}); \ No newline at end of file +}); From b40457adde0900ac19e708ef00aa2e577dec25b8 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 11:45:55 +0530 Subject: [PATCH 06/10] Update user-app-remove.spec.ts --- .../commands/user/user-app-remove.spec.ts | 133 ++---------------- 1 file changed, 11 insertions(+), 122 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index f0bbf0c11f2..aefcd8a5664 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -56,7 +56,6 @@ describe(commands.USER_APP_REMOVE, () => { afterEach(() => { sinonUtil.restore([ - request.get, request.delete, Cli.promptForConfirmation ]); @@ -123,11 +122,6 @@ describe(commands.USER_APP_REMOVE, () => { } } as any); - let promptIssued = false; - if (promptOptions && promptOptions.type === 'confirm') { - promptIssued = true; - } - assert(promptIssued); }); @@ -178,21 +172,22 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); - it('removes the app by id for the specified user when confirmation is specified (debug)', async () => { - sinon.stub(request, 'delete').callsFake(async (opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { - return; + it('removes the app for the specified user when prompt is confirmed (debug)', async () => { + sinon.stub(request, 'delete').callsFake((opts) => { + if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { + return Promise.resolve(); } - throw 'Invalid request'; }); + sinonUtil.restore(Cli.promptForConfirmation); + sinon.stub(Cli, 'promptForConfirmation').resolves(true); + await command.action(logger, { options: { userId: userId, id: appId, - debug: true, - force: true + debug: true } } as any); }); @@ -213,111 +208,6 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); - it('removes the app by id for the specified user when prompt is confirmed (debug)', async () => { - sinon.stub(request, 'delete').callsFake((opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { - return Promise.resolve(); - } - - throw 'Invalid request'; - }); - - sinonUtil.restore(Cli.prompt); - sinon.stub(Cli, 'prompt').resolves({ continue: true }); - - await command.action(logger, { - options: { - userId: 'c527a470-a882-481c-981c-ee6efaba85c7', - id: 'YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=', - debug: true - } - } as any); - }); - - it('removes the app by name for the specified user when prompt is confirmed (debug)', async () => { - sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { - return { - "value": [ - { - "id": "YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=", - "displayName": "TeamsApp" - } - ] - }; - } - - throw 'Invalid request'; - }); - - sinon.stub(request, 'delete').callsFake((opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { - return Promise.resolve(); - } - - throw 'Invalid request'; - }); - - sinonUtil.restore(Cli.prompt); - sinon.stub(Cli, 'prompt').resolves({ continue: true }); - - await command.action(logger, { - options: { - userId: 'c527a470-a882-481c-981c-ee6efaba85c7', - name: 'TeamsApp', - debug: true - } - } as any); - }); - - it('fails to get Teams app when app does not exists', async () => { - sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { - return { value: [] }; - } - - throw 'Invalid request'; - }); - - await assert.rejects(command.action(logger, { - options: { - debug: true, - userId: 'c527a470-a882-481c-981c-ee6efaba85c7', - name: 'TeamsApp', - force: true - } - } as any), new CommandError('The specified Teams app does not exist')); - }); - - it('handles error when multiple Teams apps with the specified name found', async () => { - sinon.stub(request, 'get').callsFake(async (opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { - return { - "value": [ - { - "id": "ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=", - "displayName": "TeamsApp" - }, - { - "id": "NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk", - "displayName": "TeamsApp" - } - ] - }; - } - - throw 'Invalid request'; - }); - - await assert.rejects(command.action(logger, { - options: { - debug: true, - userId: 'c527a470-a882-481c-981c-ee6efaba85c7', - name: 'TeamsApp', - force: true - } - } as any), new CommandError('Multiple Teams apps with name TeamsApp found. Please choose one of these ids: ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=, NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk')); - }); it('correctly handles error while removing teams app', async () => { const error = { @@ -333,17 +223,16 @@ describe(commands.USER_APP_REMOVE, () => { }; sinon.stub(request, 'delete').callsFake(async (opts) => { - if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { + if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { throw error; } - throw 'Invalid request'; }); await assert.rejects(command.action(logger, { options: { - userId: 'c527a470-a882-481c-981c-ee6efaba85c7', - id: 'YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=', + userId: userId, + id: appId, force: true } } as any), new CommandError(error.error.message)); From 3dd526c1e833d6261d400e607c89ed2d7ea0b64d Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 11:54:18 +0530 Subject: [PATCH 07/10] Update user-app-remove.spec.ts --- .../commands/user/user-app-remove.spec.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index aefcd8a5664..293a2f795ab 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -208,6 +208,60 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); + it('removes the app by name for the specified user when prompt is confirmed (debug)', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { + "value": [ + { + "id": "YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=", + "displayName": "TeamsApp" + } + ] + }; + } + + throw 'Invalid request'; + }); + + sinon.stub(request, 'delete').callsFake((opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps/YzUyN2E0NzAtYTg4Mi00ODFjLTk4MWMtZWU2ZWZhYmE4NWM3IyM0ZDFlYTA0Ny1mMTk2LTQ1MGQtYjJlOS0wZDI4NTViYTA1YTY=`) { + return Promise.resolve(); + } + + throw 'Invalid request'; + }); + + sinonUtil.restore(Cli.prompt); + sinon.stub(Cli, 'prompt').resolves({ continue: true }); + + await command.action(logger, { + options: { + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + debug: true + } + } as any); + }); + + it('fails to get teams app when app by name does not exists', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { value: [] }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { + options: { + debug: true, + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + force: true + } + } as any), new CommandError('The specified Teams app does not exist')); + }); it('correctly handles error while removing teams app', async () => { const error = { From bf7727d37ebe89d5a031b1d332e82290687cbe6e Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 11:58:43 +0530 Subject: [PATCH 08/10] Update user-app-remove.spec.ts --- src/m365/teams/commands/user/user-app-remove.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index 293a2f795ab..ef9388a9093 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -125,7 +125,7 @@ describe(commands.USER_APP_REMOVE, () => { assert(promptIssued); }); - it('aborts removing the app when confirmation prompt is not continued', async () => { + it('aborts removing the app by id when confirmation prompt is not continued', async () => { const requestDeleteSpy = sinon.stub(request, 'delete'); await command.action(logger, { @@ -137,7 +137,7 @@ describe(commands.USER_APP_REMOVE, () => { assert(requestDeleteSpy.notCalled); }); - it('removes the app for the specified user when confirmation is specified (debug)', async () => { + it('removes the app by id for the specified user when confirmation is specified (debug)', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { return; @@ -155,7 +155,7 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); - it('removes the app for the specified user using username when confirmation is specified.', async () => { + it('removes the app by id for the specified user using username when confirmation is specified.', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { if ((opts.url as string).indexOf(`/users/${formatting.encodeQueryParameter(userName)}/teamwork/installedApps/${appId}`) > -1) { return Promise.resolve(); @@ -172,7 +172,7 @@ describe(commands.USER_APP_REMOVE, () => { } as any); }); - it('removes the app for the specified user when prompt is confirmed (debug)', async () => { + it('removes the app by id for the specified user when prompt is confirmed (debug)', async () => { sinon.stub(request, 'delete').callsFake((opts) => { if ((opts.url as string).indexOf(`/users/${userId}/teamwork/installedApps/${appId}`) > -1) { return Promise.resolve(); @@ -232,8 +232,8 @@ describe(commands.USER_APP_REMOVE, () => { throw 'Invalid request'; }); - sinonUtil.restore(Cli.prompt); - sinon.stub(Cli, 'prompt').resolves({ continue: true }); + sinonUtil.restore(Cli.promptForConfirmation); + sinon.stub(Cli, 'promptForConfirmation').resolves(true); await command.action(logger, { options: { From 286ff5de27d3be0029150c99c31078191bc0c170 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 12:03:44 +0530 Subject: [PATCH 09/10] Update user-app-remove.spec.ts --- src/m365/teams/commands/user/user-app-remove.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index ef9388a9093..45bd2f0e6d3 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -56,6 +56,7 @@ describe(commands.USER_APP_REMOVE, () => { afterEach(() => { sinonUtil.restore([ + request.get, request.delete, Cli.promptForConfirmation ]); From 369375a04c4739efd330fc6b21ad45c44611ce09 Mon Sep 17 00:00:00 2001 From: Nanddeep Nachan Date: Fri, 10 Nov 2023 12:10:24 +0530 Subject: [PATCH 10/10] Update user-app-remove.spec.ts --- .../commands/user/user-app-remove.spec.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/m365/teams/commands/user/user-app-remove.spec.ts b/src/m365/teams/commands/user/user-app-remove.spec.ts index 45bd2f0e6d3..36d0907a011 100644 --- a/src/m365/teams/commands/user/user-app-remove.spec.ts +++ b/src/m365/teams/commands/user/user-app-remove.spec.ts @@ -264,6 +264,36 @@ describe(commands.USER_APP_REMOVE, () => { } as any), new CommandError('The specified Teams app does not exist')); }); + it('handles error when multiple teams apps with the specified name found', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/c527a470-a882-481c-981c-ee6efaba85c7/teamwork/installedApps?$expand=teamsAppDefinition&$filter=teamsAppDefinition/displayName eq 'TeamsApp'`) { + return { + "value": [ + { + "id": "ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=", + "displayName": "TeamsApp" + }, + { + "id": "NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk", + "displayName": "TeamsApp" + } + ] + }; + } + + throw 'Invalid request'; + }); + + await assert.rejects(command.action(logger, { + options: { + debug: true, + userId: 'c527a470-a882-481c-981c-ee6efaba85c7', + name: 'TeamsApp', + force: true + } + } as any), new CommandError('Multiple Teams apps with name TeamsApp found. Please choose one of these ids: ZDczZWVjZmQtYzFkNS00MzY2LWJkMjEtZDUyOTM1ZThkYjkxIyMxLjYuMC4wIyNQdWJsaXNoZWQ=, NmY0ODM2N2EtMjVmMC00NjNmLTlmMGQtMmFiZTBiYmYzNzRjIyMxLjAuMCMjUHVibGlzaGVk')); + }); + it('correctly handles error while removing teams app', async () => { const error = { "error": {