-
Notifications
You must be signed in to change notification settings - Fork 331
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d126736
commit 69ef4cc
Showing
8 changed files
with
692 additions
and
0 deletions.
There are no files selected for viewing
55 changes: 55 additions & 0 deletions
55
docs/docs/cmd/viva/engage/engage-community-user-remove.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import Global from '/docs/cmd/_global.mdx'; | ||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
# viva engage community user remove | ||
|
||
Removes a specified user from a Microsoft 365 Viva Engage community | ||
|
||
## Usage | ||
|
||
```sh | ||
m365 viva engage community user remove [options] | ||
``` | ||
|
||
## Options | ||
|
||
```md definition-list | ||
`-i, --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`. | ||
|
||
`--id [id]` | ||
: Microsoft Entra ID of the user. Specify either `id` or `userName` but not both. | ||
|
||
`--userName [userName]` | ||
: The user principal name of the user. Specify either `id` or `userName` but not both. | ||
|
||
`-f, --force` | ||
: Don't prompt for confirming removing the user from the specified Viva Engage community. | ||
``` | ||
|
||
<Global /> | ||
|
||
## Examples | ||
|
||
Remove a user specified by ID as a member from a community specified by display name. | ||
|
||
```sh | ||
m365 viva engage community user remove --communityDisplayName "All company" --id 098b9f52-f48c-4401-819f-29c33794c3f5 | ||
``` | ||
|
||
Remove a user specified by UPN from a community specified by its group ID without confirmation. | ||
|
||
```sh | ||
m365 viva engage community user remove --entraGroupId a03c0c35-ef9a-419b-8cab-f89e0a8d2d2a --userName [email protected] --force | ||
``` | ||
|
||
## Response | ||
|
||
The command won't return a response on success. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ export interface Community { | |
displayName: string; | ||
description?: string; | ||
privacy: string; | ||
groupId: string; | ||
} |
239 changes: 239 additions & 0 deletions
239
src/m365/viva/commands/engage/engage-community-user-remove.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
|
||
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-remove.js'; | ||
import { CommandInfo } from '../../../../cli/CommandInfo.js'; | ||
import { z } from 'zod'; | ||
import { cli } from '../../../../cli/cli.js'; | ||
import { vivaEngage } from '../../../../utils/vivaEngage.js'; | ||
import { entraUser } from '../../../../utils/entraUser.js'; | ||
|
||
describe(commands.ENGAGE_COMMUNITY_USER_REMOVE, () => { | ||
const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiIzNjAyMDAxMTAwOSJ9'; | ||
const communityDisplayName = 'All company'; | ||
const entraGroupId = 'b6c35b51-ebca-445c-885a-63a67d24cb53'; | ||
const userName = '[email protected]'; | ||
const userId = '3f2504e0-4f89-11d3-9a0c-0305e82c3301'; | ||
|
||
let log: string[]; | ||
let logger: Logger; | ||
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()!; | ||
sinon.stub(entraUser, 'getUserIdByUpn').resolves(userId); | ||
sinon.stub(vivaEngage, 'getEntraGroupIdByCommunityDisplayName').resolves(entraGroupId); | ||
sinon.stub(vivaEngage, 'getEntraGroupIdByCommunityId').resolves(entraGroupId); | ||
}); | ||
|
||
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.ENGAGE_COMMUNITY_USER_REMOVE); | ||
}); | ||
|
||
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', | ||
userName: userName | ||
}); | ||
assert.notStrictEqual(actual.success, true); | ||
}); | ||
|
||
it('fails validation if id is not a valid GUID', () => { | ||
const actual = commandOptionsSchema.safeParse({ | ||
entraGroupId: entraGroupId, | ||
id: 'invalid' | ||
}); | ||
assert.notStrictEqual(actual.success, true); | ||
}); | ||
|
||
it('fails validation if userName is invalid user principal name', () => { | ||
const actual = commandOptionsSchema.safeParse({ | ||
entraGroupId: entraGroupId, | ||
userName: '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, | ||
id: userId | ||
}); | ||
assert.notStrictEqual(actual.success, true); | ||
}); | ||
|
||
it('passes validation if communityId is specified', () => { | ||
const actual = commandOptionsSchema.safeParse({ | ||
communityId: communityId, | ||
userName: userName | ||
}); | ||
assert.strictEqual(actual.success, true); | ||
}); | ||
|
||
it('passes validation if entraGroupId is specified with a proper GUID', () => { | ||
const actual = commandOptionsSchema.safeParse({ | ||
entraGroupId: entraGroupId, | ||
userName: userName | ||
}); | ||
assert.strictEqual(actual.success, true); | ||
}); | ||
|
||
it('passes validation if communityDisplayName is specified', () => { | ||
const actual = commandOptionsSchema.safeParse({ | ||
communityDisplayName: communityDisplayName, | ||
userName: userName | ||
}); | ||
assert.strictEqual(actual.success, true); | ||
}); | ||
|
||
it('correctly removes user specified by id', async () => { | ||
const deleteStub = sinon.stub(request, 'delete').callsFake(async (opts) => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners/${userId}/$ref`) { | ||
return; | ||
} | ||
|
||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members/${userId}/$ref`) { | ||
return; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { options: { communityDisplayName: communityDisplayName, id: userId, force: true, verbose: true } }); | ||
assert(deleteStub.calledTwice); | ||
}); | ||
|
||
it('correctly removes user by userName', async () => { | ||
const deleteStub = sinon.stub(request, 'delete').callsFake(async (opts) => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners/${userId}/$ref`) { | ||
return; | ||
} | ||
|
||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members/${userId}/$ref`) { | ||
return; | ||
} | ||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { options: { communityId: communityId, verbose: true, userName: userName, force: true } }); | ||
assert(deleteStub.calledTwice); | ||
}); | ||
|
||
it('correctly removes user as member by userName', async () => { | ||
const deleteStub = sinon.stub(request, 'delete').callsFake(async (opts) => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners/${userId}/$ref`) { | ||
throw { | ||
response: { | ||
status: 404, | ||
data: { | ||
message: 'Object does not exist...' | ||
} | ||
} | ||
}; | ||
} | ||
|
||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/members/${userId}/$ref`) { | ||
return; | ||
} | ||
throw 'Invalid request'; | ||
}); | ||
|
||
sinonUtil.restore(cli.promptForConfirmation); | ||
sinon.stub(cli, 'promptForConfirmation').resolves(true); | ||
|
||
await command.action(logger, { options: { communityId: communityId, verbose: true, userName: userName } }); | ||
assert(deleteStub.calledTwice); | ||
}); | ||
|
||
it('handles API error when removing user', async () => { | ||
const errorMessage = 'Invalid object identifier'; | ||
sinon.stub(request, 'delete').callsFake(async opts => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/groups/${entraGroupId}/owners/${userId}/$ref`) { | ||
throw { | ||
response: { | ||
status: 400, | ||
data: { error: { 'odata.error': { message: { value: errorMessage } } } } | ||
} | ||
}; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
sinonUtil.restore(cli.promptForConfirmation); | ||
sinon.stub(cli, 'promptForConfirmation').resolves(true); | ||
|
||
await assert.rejects(command.action(logger, { options: { entraGroupId: entraGroupId, id: userId } }), | ||
new CommandError(errorMessage)); | ||
}); | ||
|
||
it('prompts before removal when confirmation argument not passed', async () => { | ||
const promptStub: sinon.SinonStub = sinon.stub(cli, 'promptForConfirmation').resolves(false); | ||
|
||
await command.action(logger, { options: { entraGroupId: entraGroupId, id: userId } }); | ||
|
||
assert(promptStub.called); | ||
}); | ||
|
||
it('aborts execution when prompt not confirmed', async () => { | ||
const deleteStub = sinon.stub(request, 'delete'); | ||
sinon.stub(cli, 'promptForConfirmation').resolves(false); | ||
|
||
await command.action(logger, { options: { entraGroupId: entraGroupId, id: userId } }); | ||
assert(deleteStub.notCalled); | ||
}); | ||
}); |
Oops, something went wrong.