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

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 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,110 @@
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 Unique ID (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
{
"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
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
id,shareId,hasPassword
d6f6a428-9857-471f-9635-edd68d5aa6c1,u!aHR0cHM6Ly9uYWNoYW4zNjUuc2hhcmVwb2ludC5jb20vOmY6L3MvU1BEZW1vL0VxXzlYcXRIdks1RW9wd3NfX1kteko0QlNybFFNUy1qUTBFOWJsazhVLVNTdVE,
```

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

```md
# spo folder sharinglink get --webUrl "https://contoso.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
---------|-------
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 @@ -2567,6 +2567,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 @@ -101,6 +101,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
166 changes: 166 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,166 @@
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 { telemetry } from '../../../../telemetry.js';
import { Logger } from '../../../../cli/Logger.js';
import request from '../../../../request.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';
import { spo } from '../../../../utils/spo.js';
import { drive } from '../../../../utils/drive.js';
import { CommandError } from '../../../../Command.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 =
{
"id": driveId,
"webUrl": `${webUrl}/Shared%20Documents`
};

const defaultGetStub = (): sinon.SinonStub => {
sinon.stub(spo, 'getFolderServerRelativeUrl').resolves(folderUrl);
sinon.stub(drive, 'getDrive').resolves(getDriveResponse);
sinon.stub(drive, 'getDriveItemId').resolves(itemId);

return sinon.stub(request, 'get').callsFake(async opts => {
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('');
sinon.stub(spo, 'getSiteId').resolves(siteId);
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,
spo.getFolderServerRelativeUrl,
drive.getDrive,
drive.getDriveItemId
]);
});

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 (debug)', async () => {
defaultGetStub();

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

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

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

it('correctly handles API OData error', async () => {
const errorMessage = 'Something went wrong';
sinon.stub(request, 'get').rejects({ error: { error: { message: errorMessage } } });

await assert.rejects(command.action(logger, {
options: {
debug: true,
webUrl: webUrl,
folderUrl: folderUrl,
id: id
}
}), new CommandError(errorMessage));
});
});
Loading