From 4b464ed9d0c03ff5343df266cacee8cc81b2a436 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Tue, 31 Oct 2023 07:49:50 +0100 Subject: [PATCH 1/4] Enhancement: Run "pa app remove" as admin --- docs/docs/cmd/pa/app/app-remove.mdx | 14 +++++ src/m365/pa/commands/app/app-remove.spec.ts | 60 +++++++++++++++++++++ src/m365/pa/commands/app/app-remove.ts | 22 +++++++- 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/docs/docs/cmd/pa/app/app-remove.mdx b/docs/docs/cmd/pa/app/app-remove.mdx index 3d0d43372cc..17297f95dca 100644 --- a/docs/docs/cmd/pa/app/app-remove.mdx +++ b/docs/docs/cmd/pa/app/app-remove.mdx @@ -18,6 +18,12 @@ m365 pa app remove [options] `-f, --force` : Don't prompt for confirmation + +`-e, --environmentName [environmentName]` +: The name of the environment for which to remove the specified app. + +`--asAdmin` +: Set, to remove the Power App as admin. Otherwise will remove only your own app. ``` @@ -26,6 +32,8 @@ m365 pa app remove [options] By default, the command will try to remove a Power App. As maker, you are able to delete the Power Apps you own. As administrator, you are also able to delete Power Apps from other users. +To remove the app from other user, use the `asAdmin` option and make sure to specify the `environment` option. You cannot specify only one of the options, when specifying the `environment` option the `asAdmin` option has to be present as well. + To remove a model-driven Power App you need administrator permissions. If the Power App with the name you specified doesn't exist, you will get the `Error: App 'abc' does not exist` error. @@ -44,6 +52,12 @@ Removes the specified Power App without confirmation m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --force ``` +Removes the specified Power App from a given environment + +```sh +m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --asAdmin +``` + ## Response The command won't return a response on success. diff --git a/src/m365/pa/commands/app/app-remove.spec.ts b/src/m365/pa/commands/app/app-remove.spec.ts index 990f4c3a1d9..60fa90e5c98 100644 --- a/src/m365/pa/commands/app/app-remove.spec.ts +++ b/src/m365/pa/commands/app/app-remove.spec.ts @@ -158,6 +158,29 @@ describe(commands.APP_REMOVE, () => { assert(loggerLogToStderrSpy.called); }); + it('removes the specified Microsoft Power App from other user as admin when prompt confirmed (debug)', async () => { + sinon.stub(request, 'delete').callsFake(async (opts) => { + if (opts.url === `https://api.powerapps.com/providers/Microsoft.PowerApps/admin/environments/4ce50206-9576-4237-8b17-38d8aadfaa35/apps/e0c89645-7f00-4877-a290-cbaf6e060da1?api-version=2017-08-01`) { + return { statusCode: 200 }; + } + + throw 'Invalid request'; + }); + + sinonUtil.restore(Cli.prompt); + sinon.stub(Cli, 'prompt').resolves({ continue: true }); + + await command.action(logger, { + options: { + debug: true, + name: 'e0c89645-7f00-4877-a290-cbaf6e060da1', + environmentName: '4ce50206-9576-4237-8b17-38d8aadfaa35', + asAdmin: true + } + }); + assert(loggerLogToStderrSpy.called); + }); + it('removes the specified Microsoft Power App without prompting when confirm specified (debug)', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { if (opts.url === `https://api.powerapps.com/providers/Microsoft.PowerApps/apps/e0c89645-7f00-4877-a290-cbaf6e060da1?api-version=2017-08-01`) { @@ -270,6 +293,28 @@ describe(commands.APP_REMOVE, () => { assert(containsOption); }); + it('supports specifying environment', () => { + const options = command.options; + let containsOption = false; + options.forEach(o => { + if (o.option.indexOf('--environment') > -1) { + containsOption = true; + } + }); + assert(containsOption); + }); + + it('supports specifying asAdmin', () => { + const options = command.options; + let containsOption = false; + options.forEach(o => { + if (o.option.indexOf('--asAdmin') > -1) { + containsOption = true; + } + }); + assert(containsOption); + }); + it('correctly handles random api error', async () => { sinon.stub(request, 'delete').rejects(new Error("Something went wrong")); @@ -283,4 +328,19 @@ describe(commands.APP_REMOVE, () => { } } as any), new CommandError("Something went wrong")); }); + + it('fails validation if asAdmin specified without environment', async () => { + const actual = await command.validate({ options: { name: "5369f386-e380-46cb-82a4-4e18f9e4f3a7", asAdmin: true } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('fails validation if environment specified without admin', async () => { + const actual = await command.validate({ options: { name: "5369f386-e380-46cb-82a4-4e18f9e4f3a7", environmentName: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6' } }, commandInfo); + assert.notStrictEqual(actual, true); + }); + + it('passes validation if asAdmin specified with environment', async () => { + const actual = await command.validate({ options: { name: "5369f386-e380-46cb-82a4-4e18f9e4f3a7", asAdmin: true, environmentName: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6' } }, commandInfo); + assert.strictEqual(actual, true); + }); }); diff --git a/src/m365/pa/commands/app/app-remove.ts b/src/m365/pa/commands/app/app-remove.ts index e29db0c8a42..c2fdebe054a 100644 --- a/src/m365/pa/commands/app/app-remove.ts +++ b/src/m365/pa/commands/app/app-remove.ts @@ -15,6 +15,8 @@ interface CommandArgs { interface Options extends GlobalOptions { name: string; force?: boolean; + environmentName?: string; + asAdmin: boolean; } class PaAppRemoveCommand extends PowerAppsCommand { @@ -37,7 +39,9 @@ class PaAppRemoveCommand extends PowerAppsCommand { #initTelemetry(): void { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { - force: typeof args.options.force !== 'undefined' + force: typeof args.options.force !== 'undefined', + asAdmin: args.options.asAdmin === true, + environmentName: typeof args.options.environmentName !== 'undefined' }); }); } @@ -49,6 +53,12 @@ class PaAppRemoveCommand extends PowerAppsCommand { }, { option: '-f, --force' + }, + { + option: '-e, --environmentName [environmentName]' + }, + { + option: '--asAdmin' } ); } @@ -60,6 +70,14 @@ class PaAppRemoveCommand extends PowerAppsCommand { return `${args.options.name} is not a valid GUID`; } + if (args.options.asAdmin && !args.options.environmentName) { + return 'When specifying the asAdmin option the environment option is required as well'; + } + + if (args.options.environmentName && !args.options.asAdmin) { + return 'When specifying the environment option the asAdmin option is required as well'; + } + return true; } ); @@ -72,7 +90,7 @@ class PaAppRemoveCommand extends PowerAppsCommand { const removePaApp = async (): Promise => { const requestOptions: CliRequestOptions = { - url: `${this.resource}/providers/Microsoft.PowerApps/apps/${formatting.encodeQueryParameter(args.options.name)}?api-version=2017-08-01`, + url: `${this.resource}/providers/Microsoft.PowerApps${args.options.asAdmin ? '/scopes/admin' : ''}${args.options.environmentName ? '/environments/' + formatting.encodeQueryParameter(args.options.environmentName) : ''}/apps/${formatting.encodeQueryParameter(args.options.name)}?api-version=2017-08-01`, fullResponse: true, headers: { accept: 'application/json' From 6a361ed36745d1f012735286af16c3c01df151aa Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Tue, 31 Oct 2023 09:26:21 +0100 Subject: [PATCH 2/4] Enhancement: Run "pa app remove" as admin --- src/m365/pa/commands/app/app-remove.spec.ts | 23 +-------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/m365/pa/commands/app/app-remove.spec.ts b/src/m365/pa/commands/app/app-remove.spec.ts index 60fa90e5c98..75d68f12190 100644 --- a/src/m365/pa/commands/app/app-remove.spec.ts +++ b/src/m365/pa/commands/app/app-remove.spec.ts @@ -137,30 +137,9 @@ describe(commands.APP_REMOVE, () => { assert(loggerLogToStderrSpy.called); }); - it('removes the specified Microsoft Power App from other user when prompt confirmed (debug)', async () => { - sinon.stub(request, 'delete').callsFake(async (opts) => { - if (opts.url === `https://api.powerapps.com/providers/Microsoft.PowerApps/apps/e0c89645-7f00-4877-a290-cbaf6e060da1?api-version=2017-08-01`) { - return { statusCode: 200 }; - } - - throw 'Invalid request'; - }); - - sinonUtil.restore(Cli.prompt); - sinon.stub(Cli, 'prompt').resolves({ continue: true }); - - await command.action(logger, { - options: { - debug: true, - name: 'e0c89645-7f00-4877-a290-cbaf6e060da1' - } - }); - assert(loggerLogToStderrSpy.called); - }); - it('removes the specified Microsoft Power App from other user as admin when prompt confirmed (debug)', async () => { sinon.stub(request, 'delete').callsFake(async (opts) => { - if (opts.url === `https://api.powerapps.com/providers/Microsoft.PowerApps/admin/environments/4ce50206-9576-4237-8b17-38d8aadfaa35/apps/e0c89645-7f00-4877-a290-cbaf6e060da1?api-version=2017-08-01`) { + if (opts.url === `https://api.powerapps.com/providers/Microsoft.PowerApps/scopes/admin/environments/4ce50206-9576-4237-8b17-38d8aadfaa35/apps/e0c89645-7f00-4877-a290-cbaf6e060da1?api-version=2017-08-01`) { return { statusCode: 200 }; } From 2e1521c061e1c780c25e62c58c9f857ab27861f8 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Sat, 16 Dec 2023 14:34:53 +0100 Subject: [PATCH 3/4] Enhancement: Run "pa app remove" as admin --- docs/docs/cmd/pa/app/app-remove.mdx | 12 +++--- src/m365/pa/commands/app/app-remove.spec.ts | 44 --------------------- src/m365/pa/commands/app/app-remove.ts | 22 +++++++---- 3 files changed, 21 insertions(+), 57 deletions(-) diff --git a/docs/docs/cmd/pa/app/app-remove.mdx b/docs/docs/cmd/pa/app/app-remove.mdx index 17297f95dca..5e7e98881c9 100644 --- a/docs/docs/cmd/pa/app/app-remove.mdx +++ b/docs/docs/cmd/pa/app/app-remove.mdx @@ -19,11 +19,11 @@ m365 pa app remove [options] `-f, --force` : Don't prompt for confirmation -`-e, --environmentName [environmentName]` -: The name of the environment for which to remove the specified app. - `--asAdmin` -: Set, to remove the Power App as admin. Otherwise will remove only your own app. +: Run the command as admin for apps you don't have access to. + +`-e, --environmentName [environmentName]` +: The name of the environment. Required when using `asAdmin`. ``` @@ -32,7 +32,7 @@ m365 pa app remove [options] By default, the command will try to remove a Power App. As maker, you are able to delete the Power Apps you own. As administrator, you are also able to delete Power Apps from other users. -To remove the app from other user, use the `asAdmin` option and make sure to specify the `environment` option. You cannot specify only one of the options, when specifying the `environment` option the `asAdmin` option has to be present as well. +To remove the app from another user, use the `asAdmin` option and make sure to specify the `environmentName` option. To remove a model-driven Power App you need administrator permissions. @@ -52,7 +52,7 @@ Removes the specified Power App without confirmation m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --force ``` -Removes the specified Power App from a given environment +Removes the specified Power App you don't have access to ```sh m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --asAdmin diff --git a/src/m365/pa/commands/app/app-remove.spec.ts b/src/m365/pa/commands/app/app-remove.spec.ts index 942acdbe19d..fd8b42b6273 100644 --- a/src/m365/pa/commands/app/app-remove.spec.ts +++ b/src/m365/pa/commands/app/app-remove.spec.ts @@ -246,50 +246,6 @@ describe(commands.APP_REMOVE, () => { } as any); }); - it('supports specifying name', () => { - const options = command.options; - let containsOption = false; - options.forEach(o => { - if (o.option.indexOf('--name') > -1) { - containsOption = true; - } - }); - assert(containsOption); - }); - - it('supports specifying confirm', () => { - const options = command.options; - let containsOption = false; - options.forEach(o => { - if (o.option.indexOf('--force') > -1) { - containsOption = true; - } - }); - assert(containsOption); - }); - - it('supports specifying environment', () => { - const options = command.options; - let containsOption = false; - options.forEach(o => { - if (o.option.indexOf('--environment') > -1) { - containsOption = true; - } - }); - assert(containsOption); - }); - - it('supports specifying asAdmin', () => { - const options = command.options; - let containsOption = false; - options.forEach(o => { - if (o.option.indexOf('--asAdmin') > -1) { - containsOption = true; - } - }); - assert(containsOption); - }); - it('correctly handles random api error', async () => { sinon.stub(request, 'delete').rejects(new Error("Something went wrong")); diff --git a/src/m365/pa/commands/app/app-remove.ts b/src/m365/pa/commands/app/app-remove.ts index 3333958878b..46587c58322 100644 --- a/src/m365/pa/commands/app/app-remove.ts +++ b/src/m365/pa/commands/app/app-remove.ts @@ -16,7 +16,7 @@ interface Options extends GlobalOptions { name: string; force?: boolean; environmentName?: string; - asAdmin: boolean; + asAdmin?: boolean; } class PaAppRemoveCommand extends PowerAppsCommand { @@ -40,7 +40,7 @@ class PaAppRemoveCommand extends PowerAppsCommand { this.telemetry.push((args: CommandArgs) => { Object.assign(this.telemetryProperties, { force: typeof args.options.force !== 'undefined', - asAdmin: args.options.asAdmin === true, + asAdmin: !!args.options.asAdmin, environmentName: typeof args.options.environmentName !== 'undefined' }); }); @@ -55,10 +55,10 @@ class PaAppRemoveCommand extends PowerAppsCommand { option: '-f, --force' }, { - option: '-e, --environmentName [environmentName]' + option: '--asAdmin' }, { - option: '--asAdmin' + option: '-e, --environmentName [environmentName]' } ); } @@ -71,11 +71,11 @@ class PaAppRemoveCommand extends PowerAppsCommand { } if (args.options.asAdmin && !args.options.environmentName) { - return 'When specifying the asAdmin option the environment option is required as well'; + return 'When specifying the asAdmin option, the environment option is required as well.'; } if (args.options.environmentName && !args.options.asAdmin) { - return 'When specifying the environment option the asAdmin option is required as well'; + return 'When specifying the environment option, the asAdmin option is required as well.'; } return true; @@ -89,8 +89,16 @@ class PaAppRemoveCommand extends PowerAppsCommand { } const removePaApp = async (): Promise => { + let endpoint; + if (args.options.asAdmin) { + endpoint = `${this.resource}/providers/Microsoft.PowerApps/scopes/admin/environments/${formatting.encodeQueryParameter(args.options.environmentName!)}/apps/${formatting.encodeQueryParameter(args.options.name)}?api-version=2017-08-01`; + } + else { + endpoint = `${this.resource}/providers/Microsoft.PowerApps/apps/${formatting.encodeQueryParameter(args.options.name)}?api-version=2017-08-01`; + } + const requestOptions: CliRequestOptions = { - url: `${this.resource}/providers/Microsoft.PowerApps${args.options.asAdmin ? '/scopes/admin' : ''}${args.options.environmentName ? '/environments/' + formatting.encodeQueryParameter(args.options.environmentName) : ''}/apps/${formatting.encodeQueryParameter(args.options.name)}?api-version=2017-08-01`, + url: endpoint, fullResponse: true, headers: { accept: 'application/json' From af556095037feca0afe42d046bd9be5c656e1500 Mon Sep 17 00:00:00 2001 From: Martin Machacek Date: Tue, 26 Dec 2023 18:48:18 +0100 Subject: [PATCH 4/4] Enhancement: Run "pa app remove" as admin --- docs/docs/cmd/pa/app/app-remove.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/cmd/pa/app/app-remove.mdx b/docs/docs/cmd/pa/app/app-remove.mdx index 5e7e98881c9..1a14dad452a 100644 --- a/docs/docs/cmd/pa/app/app-remove.mdx +++ b/docs/docs/cmd/pa/app/app-remove.mdx @@ -32,7 +32,7 @@ m365 pa app remove [options] By default, the command will try to remove a Power App. As maker, you are able to delete the Power Apps you own. As administrator, you are also able to delete Power Apps from other users. -To remove the app from another user, use the `asAdmin` option and make sure to specify the `environmentName` option. +To remove the app you do not own, use the `asAdmin` option and make sure to specify the `environmentName` option. To remove a model-driven Power App you need administrator permissions. @@ -52,7 +52,7 @@ Removes the specified Power App without confirmation m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --force ``` -Removes the specified Power App you don't have access to +Removes the specified Power App you do not own ```sh m365 pa app remove --name 3989cb59-ce1a-4a5c-bb78-257c5c39381d --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --asAdmin