Skip to content

Commit

Permalink
New command: viva engage community remove
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinM85 committed Oct 25, 2024
1 parent 4e5cad4 commit dee9610
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 43 deletions.
12 changes: 4 additions & 8 deletions src/m365/viva/commands/engage/Community.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
export interface Community {
id: string;
displayName: string;
id?: string;
displayName?: string;
description?: string;
privacy: string;
}export interface Community {
id: string;
displayName: string;
description: string;
privacy: string;
privacy?: string;
groupId?: string;
}
35 changes: 33 additions & 2 deletions src/m365/viva/commands/engage/engage-community-remove.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,25 @@ import { sinonUtil } from '../../../../utils/sinonUtil.js';
import { cli } from '../../../../cli/cli.js';
import command from './engage-community-remove.js';
import { vivaEngage } from '../../../../utils/vivaEngage.js';
import { CommandInfo } from '../../../../cli/CommandInfo.js';

describe(commands.ENGAGE_COMMUNITY_REMOVE, () => {
const communityId = 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9';
const displayName = 'Software Engineers';
const entraGroupId = '0bed8b86-5026-4a93-ac7d-56750cc099f1';

let log: string[];
let logger: Logger;
let promptIssued: boolean;
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(() => {
Expand All @@ -53,7 +57,6 @@ describe(commands.ENGAGE_COMMUNITY_REMOVE, () => {
afterEach(() => {
sinonUtil.restore([
request.delete,
vivaEngage.getCommunityIdByDisplayName,
cli.promptForConfirmation
]);
});
Expand All @@ -71,6 +74,16 @@ describe(commands.ENGAGE_COMMUNITY_REMOVE, () => {
assert.notStrictEqual(command.description, null);
});

it('passes validation when entraGroupId is specified', async () => {
const actual = await command.validate({ options: { entraGroupId: '0bed8b86-5026-4a93-ac7d-56750cc099f1' } }, commandInfo);
assert.strictEqual(actual, true);
});

it('fails validation when entraGroupId is not a valid GUID', async () => {
const actual = await command.validate({ options: { entraGroupId: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('prompts before removing the community when confirm option not passed', async () => {
await command.action(logger, { options: { id: communityId } });

Expand Down Expand Up @@ -98,7 +111,7 @@ describe(commands.ENGAGE_COMMUNITY_REMOVE, () => {
});

it('removes the community specified by displayName while prompting for confirmation', async () => {
sinon.stub(vivaEngage, 'getCommunityIdByDisplayName').resolves(communityId);
sinon.stub(vivaEngage, 'getCommunityByDisplayName').resolves({ id: communityId });

const deleteRequestStub = sinon.stub(request, 'delete').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) {
Expand All @@ -115,6 +128,24 @@ describe(commands.ENGAGE_COMMUNITY_REMOVE, () => {
assert(deleteRequestStub.called);
});

it('removes the community specified by Entra group id while prompting for confirmation', async () => {
sinon.stub(vivaEngage, 'getCommunityByEntraGroupId').resolves({ id: communityId });

const deleteRequestStub = sinon.stub(request, 'delete').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities/${communityId}`) {
return;
}

throw 'Invalid request';
});

sinonUtil.restore(cli.promptForConfirmation);
sinon.stub(cli, 'promptForConfirmation').resolves(true);

await command.action(logger, { options: { entraGroupId: entraGroupId } });
assert(deleteRequestStub.called);
});

it('throws an error when the community specified by id cannot be found', async () => {
const error = {
error: {
Expand Down
30 changes: 26 additions & 4 deletions src/m365/viva/commands/engage/engage-community-remove.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import GlobalOptions from '../../../../GlobalOptions.js';
import { Logger } from '../../../../cli/Logger.js';
import { cli } from '../../../../cli/cli.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';
Expand All @@ -13,6 +14,7 @@ interface CommandArgs {
interface Options extends GlobalOptions {
id?: string;
displayName?: string;
entraGroupId?: string;
force?: boolean
}

Expand All @@ -29,6 +31,7 @@ class VivaEngageCommunityRemoveCommand extends GraphCommand {

this.#initTelemetry();
this.#initOptions();
this.#initValidators();
this.#initOptionSets();
this.#initTypes();
}
Expand All @@ -38,6 +41,7 @@ class VivaEngageCommunityRemoveCommand extends GraphCommand {
Object.assign(this.telemetryProperties, {
id: args.options.id !== 'undefined',
displayName: args.options.displayName !== 'undefined',
entraGroupId: args.options.entraGroupId !== 'undefined',
force: !!args.options.force
});
});
Expand All @@ -51,22 +55,37 @@ class VivaEngageCommunityRemoveCommand extends GraphCommand {
{
option: '-n, --displayName [displayName]'
},
{
option: '--entraGroupId [entraGroupId]'
},
{
option: '-f, --force'
}
);
}

#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'.`;
}

return true;
}
);
}

#initOptionSets(): void {
this.optionSets.push(
{
options: ['id', 'displayName']
options: ['id', 'displayName', 'entraGroupId']
}
);
}

#initTypes(): void {
this.types.string.push('id', 'displayName');
this.types.string.push('id', 'displayName', 'entraGroupId');
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
Expand All @@ -76,7 +95,10 @@ class VivaEngageCommunityRemoveCommand extends GraphCommand {
let communityId = args.options.id;

if (args.options.displayName) {
communityId = await vivaEngage.getCommunityIdByDisplayName(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 (args.options.verbose) {
Expand All @@ -101,7 +123,7 @@ class VivaEngageCommunityRemoveCommand extends GraphCommand {
await removeCommunity();
}
else {
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove Viva Engage community '${args.options.id || args.options.displayName}'?` });
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove Viva Engage community '${args.options.id || args.options.displayName || args.options.entraGroupId }'?` });

if (result) {
await removeCommunity();
Expand Down
128 changes: 106 additions & 22 deletions src/utils/vivaEngage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@ 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"
"privacy": "Public",
"groupId": "0bed8b86-5026-4a93-ac7d-56750cc099f1"
};
const anotherCommunityResponse = {
"id": "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw",
"description": "Test only",
"displayName": "All Company",
"privacy": "Private"
"privacy": "Private",
"groupId": "0bed8b86-5026-4a93-ac7d-56750cc099f1"
};

afterEach(() => {
Expand All @@ -31,56 +35,58 @@ describe('utils/vivaEngage', () => {
]);
});

it('correctly get single community id by name using getCommunityIdByDisplayName', async () => {
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)}'`) {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) {
return {
value: [
communityResponse
{
id: communityId
}
]
};
}

return 'Invalid Request';
});

const actual = await vivaEngage.getCommunityIdByDisplayName(displayName);
assert.deepStrictEqual(actual, 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9');
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 getCommunityIdByDisplayName and cli is set to prompt', async () => {
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)}'`) {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) {
return {
value: [
communityResponse,
anotherCommunityResponse
{ id: communityId },
{ id: "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw" }
]
};
}

return 'Invalid Request';
});

sinon.stub(cli, 'handleMultipleResultsFound').resolves(communityResponse);
sinon.stub(cli, 'handleMultipleResultsFound').resolves({ id: communityId });

const actual = await vivaEngage.getCommunityIdByDisplayName(displayName);
assert.deepStrictEqual(actual, 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9');
const actual = await vivaEngage.getCommunityByDisplayName(displayName, ['id']);
assert.deepStrictEqual(actual, { id: 'eyJfdHlwZSI6Ikdyb3VwIiwiaWQiOiI0NzY5MTM1ODIwOSJ9' });
});

it('throws error message when no community was found using getCommunityIdByDisplayName', async () => {
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)}'`) {
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.getCommunityIdByDisplayName(invalidDisplayName)), Error(`The specified Viva Engage community '${invalidDisplayName}' does not exist.`);
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 getCommunityIdByDisplayName', async () => {
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;
Expand All @@ -90,19 +96,97 @@ describe('utils/vivaEngage', () => {
});

sinon.stub(request, 'get').callsFake(async opts => {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'`) {
if (opts.url === `https://graph.microsoft.com/v1.0/employeeExperience/communities?$filter=displayName eq '${formatting.encodeQueryParameter(displayName)}'&$select=id`) {
return {
value: [
communityResponse,
anotherCommunityResponse
{ id: communityId },
{ id: "eyJfdHlwZ0NzY5SIwiIiSJ9IwO6IaWQiOIMTM1ODikdyb3Vw" }
]
};
}

return 'Invalid Request';
});

await assert.rejects(vivaEngage.getCommunityIdByDisplayName(displayName),
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.`);
});
});
Loading

0 comments on commit dee9610

Please sign in to comment.