Skip to content

Commit

Permalink
Adds tenant site membership list command Closes #5980
Browse files Browse the repository at this point in the history
  • Loading branch information
mkm17 committed Jul 10, 2024
1 parent 10b9b79 commit bc2e96e
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 0 deletions.
89 changes: 89 additions & 0 deletions docs/docs/cmd/spo/tenant/tenant-site-membership-list.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import Global from '/docs/cmd/_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# spo tenant site membership list

Retrieve information about default site groups' membership

## Usage

```sh
m365 spo tenant site membership list [options]
```

## Options

```md definition-list
`-u, --siteUrl <siteUrl>`
: The URL of the site.

`-r, --role [role]`
: Filter the results to include only users with the specified roles: 'Owner', 'Member', or 'Visitor'..

```

<Global />

## Remarks

To execute this command successfully, ensure you are logged in to the CLI for M365 with an account that has SharePoint admin permissions. For other scenarios, refer to the `spo web get --withGroups` and `spo group member list` commands.

## Examples

Retrieves information about default site groups' owners, members, and visitors of the site.

```sh
m365 spo tenant site membership list --siteUrl https://contoso.sharepoint.com
```

Retrieves information about site owners.

```sh
m365 spo tenant site membership list --siteUrl https://contoso.sharepoint.com --role Owner
```

## Response

<Tabs>
<TabItem value="JSON">

```json
{
"AssociatedOwnerGroup": [
{
"email": "[email protected]",
"loginName": "i:0#.f|membership|[email protected]",
"name": "UserName",
"userPrincipalName": "[email protected]"
}
],
"AssociatedMemberGroup": [
{
"email": "[email protected]",
"loginName": "i:0#.f|membership|[email protected]",
"name": "internal",
"userPrincipalName": "[email protected]"
}
],
"AssociatedVisitorGroup": [
{
"email": "",
"loginName": "c:0-.f|rolemanager|spo-grid-all-users/dc109ffd-4298-487e-9cbc-6b9b1a2cd3e2",
"name": "Everyone except external users",
"userPrincipalName": null
}
]
}
```
</TabItem>
<TabItem value="Text">

```text
AssociatedMemberGroup : [{"email":"[email protected]","loginName":"i:0#.f|membership|[email protected]","name":"internal","userPrincipalName":"[email protected]"}]
AssociatedOwnerGroup : [{"email":"[email protected]","loginName":"i:0#.f|membership|[email protected]","name":"UserName","userPrincipalName":"[email protected]"}]
AssociatedVisitorGroup: [{"email":"","loginName":"c:0-.f|rolemanager|spo-grid-all-users/dc109ffd-4298-487e-9cbc-6b9b1a2cd3e2","name":"Everyone except external users","userPrincipalName":null}]
```

</TabItem>
</Tabs>
5 changes: 5 additions & 0 deletions docs/src/config/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3646,6 +3646,11 @@ const sidebars: SidebarsConfig = {
type: 'doc',
label: 'tenant site unarchive',
id: 'cmd/spo/tenant/tenant-site-unarchive'
},
{
type: 'doc',
label: 'tenant site membership list',
id: 'cmd/spo/tenant/tenant-site-membership-list'
}
]
},
Expand Down
1 change: 1 addition & 0 deletions src/m365/spo/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ export default {
TENANT_SETTINGS_SET: `${prefix} tenant settings set`,
TENANT_SITE_ARCHIVE: `${prefix} tenant site archive`,
TENANT_SITE_UNARCHIVE: `${prefix} tenant site unarchive`,
TENANT_SITE_MEMBERSHIP_LIST: `${prefix} tenant site membership list`,
TERM_ADD: `${prefix} term add`,
TERM_GET: `${prefix} term get`,
TERM_LIST: `${prefix} term list`,
Expand Down
196 changes: 196 additions & 0 deletions src/m365/spo/commands/tenant/tenant-site-membership-list.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
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 { spo } from '../../../../utils/spo.js';
import commands from '../../commands.js';
import command from './tenant-site-membership-list.js';
import { CommandInfo } from '../../../../cli/CommandInfo.js';
import { cli } from '../../../../cli/cli.js';

describe(commands.TENANT_SITE_MEMBERSHIP_LIST, () => {
let log: any[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
const membershipList = [
{
email: '[email protected]',
loginName: 'i:0#.f|membership|[email protected]',
name: 'user1DisplayName',
userPrincipalName: 'user1loginName'
},
{
email: '[email protected]',
loginName: 'i:0#.f|membership|[email protected]',
name: 'user2DisplayName',
userPrincipalName: 'user2loginName'
}
];
const adminUrl = 'https://contoso-admin.sharepoint.com';
const siteUrl = 'https://contoso.sharepoint.com/sites/site';
const siteId = '00000000-0000-0000-0000-000000000010';

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
sinon.stub(telemetry, 'trackEvent').returns();
sinon.stub(pid, 'getProcessName').returns('');
sinon.stub(session, 'getId').returns('');
sinon.stub(spo, 'getRequestDigest').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
]);
});

after(() => {
sinon.restore();
auth.connection.active = false;
auth.connection.spoUrl = undefined;
});

it('has correct name', () => {
assert.strictEqual(command.name, commands.TENANT_SITE_MEMBERSHIP_LIST);
});

it('has a description', () => {
assert.notStrictEqual(command.description, null);
});

it('fails validation if the siteUrl option is not a valid SharePoint URL', () => {
const actual = command.validate({ options: { siteUrl: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('fails validation if the role option is not a valid role', () => {
const actual = command.validate({ options: { siteUrl: 'https://contoso.sharepoint.com', role: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('lists all site membership groups', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites/GetSiteUserGroups?siteId=%27${siteId}%27&userGroupIds=[0,1,2]`) {
return { value: [{ userGroup: membershipList }, { userGroup: membershipList }, { userGroup: membershipList }] };
};

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';
});

await command.action(logger, { options: { siteUrl: siteUrl } });
assert(loggerLogSpy.calledWith({
AssociatedOwnerGroup: membershipList,
AssociatedMemberGroup: membershipList,
AssociatedVisitorGroup: membershipList
}));
});

it('lists all site membership groups - just Owners group', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites/GetSiteUserGroups?siteId=%27${siteId}%27&userGroupIds=[0]`) {
return { value: [{ userGroup: membershipList }] };
};

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';
});

await command.action(logger, { options: { siteUrl: siteUrl, role: "Owner" } });
assert(loggerLogSpy.calledWith({
AssociatedOwnerGroup: membershipList
}));
});

it('lists all site membership groups - just Members group', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites/GetSiteUserGroups?siteId=%27${siteId}%27&userGroupIds=[1]`) {
return { value: [{ userGroup: membershipList }] };
};

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';
});

await command.action(logger, { options: { siteUrl: siteUrl, role: "Member" } });
assert(loggerLogSpy.calledWith({
AssociatedMemberGroup: membershipList
}));
});

it('lists all site membership groups - just Visitors group', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites/GetSiteUserGroups?siteId=%27${siteId}%27&userGroupIds=[2]`) {
return { value: [{ userGroup: membershipList }] };
};

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';
});

await command.action(logger, { options: { siteUrl: siteUrl, role: "Visitor" } });
assert(loggerLogSpy.calledWith({
AssociatedVisitorGroup: membershipList
}));
});

it('correctly handles error when site is not found for specified site URL', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${adminUrl}/_api/SPO.Tenant/sites/GetSiteUserGroups?siteId=%27${siteId}%27&userGroupIds=[2]`) {
return { value: [{ userGroup: membershipList }] };
};

if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/site?$select=id`) {
return { id: 'Incorrect ID' };
}

throw 'Invalid request';
});

await assert.rejects(command.action(logger, { options: { siteUrl: siteUrl } }), new CommandError(`Site with URL ${siteUrl} not found`));
});
});
Loading

0 comments on commit bc2e96e

Please sign in to comment.