forked from pnp/cli-microsoft365
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New command: m365 spo folder sharinglink set. Closes pnp#5964
Empty commit
- Loading branch information
1 parent
3152fc7
commit 5efbe7b
Showing
6 changed files
with
574 additions
and
1 deletion.
There are no files selected for viewing
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,119 @@ | ||
import Global from '/docs/cmd/_global.mdx'; | ||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
# spo folder sharinglink set | ||
|
||
Updates a sharing link of a folder | ||
|
||
## Usage | ||
|
||
```sh | ||
m365 spo folder sharinglink set [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. | ||
|
||
`--id <id>` | ||
: The sharing link ID. | ||
|
||
`--role [role]` | ||
: Role to set. Possible options are: `read` and `write`. This option only work for permissions that don't have a sharing link. | ||
|
||
`--expirationDateTime <expirationDateTime>` | ||
: The date and time to set the expiration. This should be defined as a valid ISO 8601 string. This option only works for anonymous links. | ||
``` | ||
|
||
<Global /> | ||
|
||
## Examples | ||
|
||
Updates the expiration datetime of an anonymous sharing link with a specific id. | ||
|
||
```sh | ||
m365 spo folder sharinglink set --webUrl https://contoso.sharepoint.com/sites/demo --folderId daebb04b-a773-4baa-b1d1-3625418e3234 --id 7c9f97c9-1bda-433c-9364-bb83e81771ee --expirationDateTime '2022-11-30T00:00:00Z' | ||
``` | ||
|
||
Updates the roles of a sharing permission. | ||
|
||
```sh | ||
m365 spo folder sharinglink set --webUrl https://contoso.sharepoint.com/sites/demo --folderUrl /sites/demo/shared%20documents/Folder --id Q29udG9zbyBEcm9uZSBMYW5kaW5nIE93bmVycw --role read | ||
``` | ||
|
||
## Response | ||
|
||
<Tabs> | ||
<TabItem value="JSON"> | ||
|
||
```json | ||
{ | ||
"id": "bd1481e9-958b-4c1a-a33c-fb021f4ed444", | ||
"roles": [ | ||
"write" | ||
], | ||
"shareId": "u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc", | ||
"expirationDateTime": "2024-05-05T16:57:00Z", | ||
"hasPassword": false, | ||
"grantedToIdentitiesV2": [], | ||
"grantedToIdentities": [], | ||
"link": { | ||
"scope": "anonymous", | ||
"type": "edit", | ||
"webUrl": "https://contoso.sharepoint.com/:f:/g/Ep-ZFPqvbElBsgYraF2A4mcBfV3P7qM4xfUUrQdwgIye4g", | ||
"preventsDownload": false | ||
} | ||
} | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="Text"> | ||
|
||
```text | ||
expirationDateTime : 2024-05-05T16:57:00Z | ||
grantedToIdentities : [] | ||
grantedToIdentitiesV2: [] | ||
hasPassword : false | ||
id : bd1481e9-958b-4c1a-a33c-fb021f4ed444 | ||
link : {"scope":"anonymous","type":"edit","webUrl":"https://contoso.sharepoint.com/:f:/g/Ep-ZFPqvbElBsgYraF2A4mcBfV3P7qM4xfUUrQdwgIye4g","preventsDownload":false} | ||
roles : ["write"] | ||
shareId : u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="CSV"> | ||
|
||
```csv | ||
id,shareId,expirationDateTime,hasPassword | ||
bd1481e9-958b-4c1a-a33c-fb021f4ed444,u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc,2024-05-05T16:57:00Z, | ||
``` | ||
|
||
</TabItem> | ||
<TabItem value="Markdown"> | ||
|
||
```md | ||
# spo folder sharinglink set --webUrl "https://contoso.sharepoint.com" --folderUrl "/shared documents/f1" --id "bd1481e9-958b-4c1a-a33c-fb021f4ed444" --expirationDateTime "2024-05-05T16:57:00.000Z" | ||
|
||
Date: 03/05/2024 | ||
|
||
## bd1481e9-958b-4c1a-a33c-fb021f4ed444 | ||
|
||
Property | Value | ||
---------|------- | ||
id | bd1481e9-958b-4c1a-a33c-fb021f4ed444 | ||
shareId | u!aHR0cHM6Ly83NTY2YXZhLnNoYXJlcG9pbnQuY29tLzpmOi9nL0VwLVpGUHF2YkVsQnNnWXJhRjJBNG1jQmZWM1A3cU00eGZVVXJRZHdnSXllNGc | ||
expirationDateTime | 2024-05-05T16:57:00Z | ||
hasPassword | false | ||
``` | ||
|
||
</TabItem> | ||
</Tabs> |
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
210 changes: 210 additions & 0 deletions
210
src/m365/spo/commands/folder/folder-sharinglink-set.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,210 @@ | ||
import assert from 'assert'; | ||
import sinon from 'sinon'; | ||
import auth from '../../../../Auth.js'; | ||
import { cli } from '../../../../cli/cli.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 { CommandInfo } from '../../../../cli/CommandInfo.js'; | ||
import commands from '../../commands.js'; | ||
import command from './folder-sharinglink-set.js'; | ||
|
||
describe(commands.FOLDER_SHARINGLINK_SET, () => { | ||
let log: any[]; | ||
let logger: Logger; | ||
let loggerLogSpy: sinon.SinonSpy; | ||
let commandInfo: CommandInfo; | ||
|
||
const webUrl = 'https://contoso.sharepoint.com/sites/project-x'; | ||
const folderId = 'f09c4efe-b8c0-4e89-a166-03418661b89b'; | ||
const folderUrl = '/sites/project-x/shared documents/folder1'; | ||
const siteId = '0f9b8f4f-0e8e-4630-bb0a-501442db9b64'; | ||
const driveId = '013TMHP6UOOSLON57HT5GLKEU7R5UGWZVK'; | ||
const itemId = 'b!T4-bD44OMEa7ClAUQtubZID9tc40pGJKpguycvELod_Gx-lo4ZQiRJ7vylonTufG'; | ||
const id = 'ef1cddaa-b74a-4aae-8a7a-5c16b4da67f2'; | ||
|
||
const defaultGetStub = (): sinon.SinonStub => { | ||
return sinon.stub(request, 'get').callsFake(async opts => { | ||
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')}')?$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: 'Folder Not Found.' } } } }; | ||
} | ||
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 getDriveResponse; | ||
} | ||
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 }; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
}; | ||
|
||
const getDriveResponse: any = { | ||
value: [ | ||
{ | ||
"id": driveId, | ||
"webUrl": `${webUrl}/Shared%20Documents` | ||
} | ||
] | ||
}; | ||
|
||
const graphResponse = { | ||
"id": "2a021f54-90a2-4016-b3b3-5f34d2e7d932", | ||
"roles": [ | ||
"read" | ||
], | ||
"hasPassword": false, | ||
"grantedToIdentitiesV2": [], | ||
"grantedToIdentities": [], | ||
"link": { | ||
"scope": "anonymous", | ||
"type": "view", | ||
"webUrl": "https://contoso.sharepoint.com/:b:/s/pnpcoresdktestgroup/EY50lub3559MtRKfj2hrZqoBWnHOpGIcgi4gzw9XiWYJ-A", | ||
"preventsDownload": false | ||
} | ||
}; | ||
|
||
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, | ||
request.patch | ||
]); | ||
}); | ||
|
||
after(() => { | ||
sinon.restore(); | ||
auth.connection.active = false; | ||
}); | ||
|
||
it('has correct name', () => { | ||
assert.strictEqual(command.name, commands.FOLDER_SHARINGLINK_SET); | ||
}); | ||
|
||
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 expirationDateTime option is not a valid date', async () => { | ||
const actual = await command.validate({ options: { webUrl: 'https://contoso.sharepoint.com', folderId: folderId, expirationDateTime: 'invalid date', id: id } }, commandInfo); | ||
assert.notStrictEqual(actual, true); | ||
}); | ||
|
||
it('fails validation if invalid role specified', async () => { | ||
const actual = await command.validate({ options: { webUrl: 'https://contoso.sharepoint.com', folderId: folderId, role: 'invalid role', id: id } }, commandInfo); | ||
assert.notStrictEqual(actual, true); | ||
}); | ||
|
||
it('passes validation if options are valid', async () => { | ||
const actual = await command.validate({ options: { webUrl: 'https://contoso.sharepoint.com', folderId: folderId, role: 'read', id: id } }, commandInfo); | ||
assert.strictEqual(actual, true); | ||
}); | ||
|
||
it('updates a sharing link to a folder specified by the id', async () => { | ||
defaultGetStub(); | ||
|
||
sinon.stub(request, 'patch').callsFake(async (opts) => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/permissions/${id}`) { | ||
return graphResponse; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { options: { webUrl: webUrl, folderId: folderId, role: 'read', id: id, verbose: true } } as any); | ||
assert(loggerLogSpy.calledWith(graphResponse)); | ||
}); | ||
|
||
it('updates a sharing link to a folder specified by the URL', async () => { | ||
defaultGetStub(); | ||
|
||
sinon.stub(request, 'patch').callsFake(async (opts) => { | ||
if (opts.url === `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/permissions/${id}`) { | ||
return graphResponse; | ||
} | ||
|
||
throw 'Invalid request'; | ||
}); | ||
|
||
await command.action(logger, { options: { webUrl: webUrl, folderUrl: '/sites/project-x/shared documents/', expirationDateTime: '2024-05-05T16:57:00.000Z', id: id, verbose: true } } 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', role: "read" } } as any), | ||
new CommandError(`Folder 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, type: 'read' } } as any), | ||
new CommandError(`Drive 'https://contoso.sharepoint.com/sites/project-x/shared%20documents/folder1' not found`)); | ||
}); | ||
}); |
Oops, something went wrong.