Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds 'spo folder sharinglink get' command. Closes #5962 #6015

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 116 additions & 0 deletions docs/docs/cmd/spo/folder/folder-sharinglink-get.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import Global from '/docs/cmd/_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# spo folder sharinglink get

Gets details about a specific sharing link on a folder

## Usage

```sh
m365 spo folder sharinglink get [options]
```

## Options

```md definition-list
`-u, --webUrl <webUrl>`
: The URL of the site where the folder is located.

`--folderUrl [folderUrl]`
: The server- or site-relative decoded URL of the folder. Specify either `folderUrl` or `folderId` but not both.

`--folderId [folderId]`
: The UniqueId (GUID) of the folder. Specify either `folderUrl` or `folderId` but not both.

`-i, --id <id>`
: The sharing link ID.
```

<Global />

## Examples

Gets a specific sharing link of a folder by id.

```sh
m365 spo folder sharinglink get --webUrl https://contoso.sharepoint.com/sites/demo --id 45fa6aed-362f-48b1-b04e-6da85a526506 --folderId daebb04b-a773-4baa-b1d1-3625418e3234
```

Gets a specific sharing link of a folder by url.

```sh
m365 spo folder sharinglink get --webUrl https://contoso.sharepoint.com/sites/demo --id 45fa6aed-362f-48b1-b04e-6da85a526506 --folderUrl "/sites/demo/shared documents/folder"
```

## Response

<Tabs>
<TabItem value="JSON">

```json
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#drives('b!FeaYl4alXkWEhoSRP0ksSSOaj9osSfFPqj5bQNdluvlwfL79GNVISZZCf6nfB3vY')/items('01A5WCPNVP7VPKWR54VZCKFHBM773D5TE6')/permissions/$entity",
"@deprecated.GrantedToIdentities": "GrantedToIdentities has been deprecated. Refer to GrantedToIdentitiesV2",
"id": "d6f6a428-9857-471f-9635-edd68d5aa6c1",
"roles": [
"write"
],
"shareId": "u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE",
"hasPassword": false,
"grantedToIdentitiesV2": [],
"grantedToIdentities": [],
"link": {
"scope": "anonymous",
"type": "edit",
"webUrl": "https://contoso.sharepoint.com/:f:/s/demo/Eq_9XqtHvK5Eopws__Y-zJ4BSrlQMS-jQ0E9blk8U-SSuQ",
"preventsDownload": false
}
}
```

</TabItem>
<TabItem value="Text">

```text
@deprecated.GrantedToIdentities: GrantedToIdentities has been deprecated. Refer to GrantedToIdentitiesV2
@odata.context : https://graph.microsoft.com/v1.0/$metadata#drives('b!FeaYl4alXkWEhoSRP0ksSSOaj9osSfFPqj5bQNdluvlwfL79GNVISZZCf6nfB3vY')/items('01A5WCPNVP7VPKWR54VZCKFHBM773D5TE6')/permissions/$entity
grantedToIdentities : []
grantedToIdentitiesV2 : []
hasPassword : false
id : d6f6a428-9857-471f-9635-edd68d5aa6c1
link : {"scope":"anonymous","type":"edit","webUrl":"https://contoso.sharepoint.com/:f:/s/demo/Eq_9XqtHvK5Eopws__Y-zJ4BSrlQMS-jQ0E9blk8U-SSuQ","preventsDownload":false}
roles : ["write"]
shareId : u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE
```

</TabItem>
<TabItem value="CSV">

```csv
@odata.context,@deprecated.GrantedToIdentities,id,shareId,hasPassword
https://graph.microsoft.com/v1.0/$metadata#drives('b!FeaYl4alXkWEhoSRP0ksSSOaj9osSfFPqj5bQNdluvlwfL79GNVISZZCf6nfB3vY')/items('01A5WCPNVP7VPKWR54VZCKFHBM773D5TE6')/permissions/$entity,GrantedToIdentities has been deprecated. Refer to GrantedToIdentitiesV2,d6f6a428-9857-471f-9635-edd68d5aa6c1,u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE,
```

</TabItem>
<TabItem value="Markdown">

```md
# spo folder sharinglink get --webUrl "https://nachan365.sharepoint.com/sites/demo" --id "d6f6a428-9857-471f-9635-edd68d5aa6c1" --folderUrl "/sites/demo/shared documents/Test"

Date: 5/2/2024

## d6f6a428-9857-471f-9635-edd68d5aa6c1

Property | Value
---------|-------
@odata.context | https://graph.microsoft.com/v1.0/$metadata#drives('b!FeaYl4alXkWEhoSRP0ksSSOaj9osSfFPqj5bQNdluvlwfL79GNVISZZCf6nfB3vY')/items('01A5WCPNVP7VPKWR54VZCKFHBM773D5TE6')/permissions/$entity
@deprecated.GrantedToIdentities | GrantedToIdentities has been deprecated. Refer to GrantedToIdentitiesV2
id | d6f6a428-9857-471f-9635-edd68d5aa6c1
shareId | u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE
hasPassword | false
```

</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 @@ -2506,6 +2506,11 @@ const sidebars: SidebarsConfig = {
type: 'doc',
label: 'folder roleinheritance reset',
id: 'cmd/spo/folder/folder-roleinheritance-reset'
},
{
type: 'doc',
label: 'folder sharinglink get',
id: 'cmd/spo/folder/folder-sharinglink-get'
}
]
},
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 @@ -100,6 +100,7 @@ export default {
FOLDER_ROLEASSIGNMENT_ADD: `${prefix} folder roleassignment add`,
FOLDER_ROLEINHERITANCE_BREAK: `${prefix} folder roleinheritance break`,
FOLDER_ROLEINHERITANCE_RESET: `${prefix} folder roleinheritance reset`,
FOLDER_SHARINGLINK_GET: `${prefix} folder sharinglink get`,
GET: `${prefix} get`,
GROUP_ADD: `${prefix} group add`,
GROUP_GET: `${prefix} group get`,
Expand Down
193 changes: 193 additions & 0 deletions src/m365/spo/commands/folder/folder-sharinglink-get.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import assert from 'assert';
import sinon from 'sinon';
import auth from '../../../../Auth.js';
import { cli } from '../../../../cli/cli.js';
import { CommandInfo } from '../../../../cli/CommandInfo.js';
import { CommandError } from '../../../../Command.js';
import { telemetry } from '../../../../telemetry.js';
import { Logger } from '../../../../cli/Logger.js';
import request from '../../../../request.js';
import { formatting } from '../../../../utils/formatting.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 './folder-sharinglink-get.js';

describe(commands.FOLDER_SHARINGLINK_GET, () => {
let log: any[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;

const webUrl = 'https://contoso.sharepoint.com/sites/project-x';
const folderUrl = '/sites/project-x/shared documents/folder1';
const folderId = 'f09c4efe-b8c0-4e89-a166-03418661b89b';
const id = 'd6f6a428-9857-471f-9635-edd68d5aa6c1';
const siteId = '0f9b8f4f-0e8e-4630-bb0a-501442db9b64';
const driveId = '013TMHP6UOOSLON57HT5GLKEU7R5UGWZVK';
const itemId = 'b!T4-bD44OMEa7ClAUQtubZID9tc40pGJKpguycvELod_Gx-lo4ZQiRJ7vylonTufG';

const graphResponse = {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#drives('b!T4-bD44OMEa7ClAUQtubZID9tc40pGJKpguycvELod_Gx-lo4ZQiRJ7vylonTufG')/items('013TMHP6UOOSLON57HT5GLKEU7R5UGWZVK')/permissions/$entity",
"@deprecated.GrantedToIdentities": "GrantedToIdentities has been deprecated. Refer to GrantedToIdentitiesV2",
"id": "d6f6a428-9857-471f-9635-edd68d5aa6c1",
"roles": [
"write"
],
"shareId": "u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE",
"hasPassword": false,
"grantedToIdentitiesV2": [],
"grantedToIdentities": [],
"link": {
"scope": "anonymous",
"type": "edit",
"webUrl": "https://contoso.sharepoint.com/:f:/s/project-x/Eq_9XqtHvK5Eopws__Y-zJ4BSrlQMS-jQ0E9blk8U-SSuQ",
"preventsDownload": false
}
};

const getDriveResponse: any = {
value: [
{
"id": driveId,
"webUrl": `${webUrl}/Shared%20Documents`
}
]
};

const defaultGetStub = (): sinon.SinonStub => {
return sinon.stub(request, 'get').callsFake(async opts => {
if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/project-x?$select=id`) {
return { id: siteId };
}
else if (opts.url === `https://graph.microsoft.com/v1.0/sites/${siteId}/drives?$select=webUrl,id`) {
return getDriveResponse;
}
else if (opts.url === `${webUrl}/_api/web/GetFolderById('${folderId}')?$select=ServerRelativeUrl`) {
return { ServerRelativeUrl: folderUrl };
}
else if (opts.url === `${webUrl}/_api/web/GetFolderByServerRelativePath(decodedUrl='${formatting.encodeQueryParameter('/sites/project-x/shared documents/folder1')}')?$select=ServerRelativeUrl`) {
return { ServerRelativeUrl: '/sites/project-x/shared documents' };
}
else if (opts.url === `${webUrl}/_api/web/GetFolderById('invalid')?$select=ServerRelativeUrl`) {
throw { error: { 'odata.error': { message: { value: 'File Not Found.' } } } };
}
else if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/root:/folder1?$select=id` ||
opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/root?$select=id`) {
return { id: itemId };
}
else if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/permissions/${id}`) {
return graphResponse;
}

throw 'Invalid request';
});
};

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(() => {
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
]);
});

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

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

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

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

it('fails validation if the folderId option is not a valid GUID', async () => {
const actual = await command.validate({ options: { webUrl: webUrl, folderId: 'invalid', id: id } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('fails validation if the id option is not a valid GUID', async () => {
const actual = await command.validate({ options: { webUrl: webUrl, folderId: folderId, id: 'invalid' } }, commandInfo);
assert.notStrictEqual(actual, true);
});

it('passes validation if options are valid', async () => {
const actual = await command.validate({ options: { webUrl: webUrl, folderId: folderId, id: id } }, commandInfo);
assert.strictEqual(actual, true);
});

it('retrieves sharing link of folder specified by id', async () => {
defaultGetStub();

await command.action(logger, { options: { webUrl: webUrl, folderId: folderId, id: id } } as any);
assert(loggerLogSpy.calledWith(graphResponse));
});

it('retrieves sharing link of folder specified by url', async () => {
defaultGetStub();

await command.action(logger, { options: { webUrl: webUrl, folderUrl: folderUrl, id: id } } as any);
assert(loggerLogSpy.calledWith(graphResponse));
});

it('throws error when folder not found by id', async () => {
defaultGetStub();

await assert.rejects(command.action(logger, { options: { webUrl: webUrl, folderId: 'invalid', id: id, verbose: true } } as any),
new CommandError(`File Not Found.`));
});

it('throws error when drive not found by url', async () => {
sinon.stub(request, 'get').callsFake(async opts => {
if (opts.url === `${webUrl}/_api/web/GetFolderByServerRelativePath(decodedUrl='${formatting.encodeQueryParameter(folderUrl)}')?$select=ServerRelativeUrl`) {
return { ServerRelativeUrl: folderUrl };
}
else if (opts.url === `https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/project-x?$select=id`) {
return { id: siteId };
}
else if (opts.url === `https://graph.microsoft.com/v1.0/sites/${siteId}/drives?$select=webUrl,id`) {
return {
value: []
};
}

throw 'Invalid request';
});

await assert.rejects(command.action(logger, { options: { webUrl: webUrl, folderUrl: folderUrl, id: id, verbose: true } } as any),
new CommandError(`Drive 'https://contoso.sharepoint.com/sites/project-x/shared%20documents/folder1' not found`));
});
});