Skip to content

Commit

Permalink
Enhances 'spo folder move' with new endpoint. Closes #6154
Browse files Browse the repository at this point in the history
  • Loading branch information
milanholemans authored and MathijsVerbeeck committed Oct 16, 2024
1 parent 6dddc64 commit d48e589
Show file tree
Hide file tree
Showing 12 changed files with 737 additions and 272 deletions.
4 changes: 1 addition & 3 deletions docs/docs/cmd/spo/file/file-move.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,13 @@ m365 spo file move [options]
: This indicates whether a file with a share lock can still be moved. Use this option to move a file that is locked.

`--skipWait`
: Don't wait for the copy operation to complete.
: Don't wait for the move operation to complete.
```

<Global />

## Remarks

All file versions are retained while moving a file.

When you specify a value for `nameConflictBehavior`, consider the following:
- `fail` will throw an error when the destination file already exists.
- `replace` will replace the destination file if it already exists.
Expand Down
97 changes: 84 additions & 13 deletions docs/docs/cmd/spo/folder/folder-move.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Global from '/docs/cmd/_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# spo folder move

Expand Down Expand Up @@ -31,19 +33,14 @@ m365 spo folder move [options]
`--nameConflictBehavior [nameConflictBehavior]`
: Behavior when a file or folder with the same name is already present at the destination. Allowed values: `fail`, `rename`. Defaults to `fail`.

`--retainEditorAndModified`
: Use this option to retain the editor and modified date. When not specified, these values are reset.

`--bypassSharedLock`
: This indicates whether a folder with a share lock can still be moved. Use this option to move a folder that is locked.
`--skipWait`
: Don't wait for the move operation to complete.
```

<Global />

## Remarks

All folder versions are retained while moving a folder.

When you specify a value for `nameConflictBehavior`, consider the following:

- `fail` will throw an error when the destination folder already exists.
Expand All @@ -69,16 +66,90 @@ Move a folder to another location and use a new name on conflict.
m365 spo folder move --webUrl https://contoso.sharepoint.com/sites/project-x --sourceUrl "/sites/project-x/Shared Documents/Reports" --targetUrl "/sites/project-y/Shared Documents/Project files" --nameConflictBehavior rename
```

Move a folder referenced by its ID to another document library and retain editor and modified date.
Move a folder referenced by its ID to another document library and don't wait for the move job to finish.

```sh
m365 spo folder move --webUrl https://contoso.sharepoint.com/sites/project-x --sourceId b8cc341b-9c11-4f2d-aa2b-0ce9c18bcba2 --targetUrl "/sites/project-x/Project files" --retainEditorAndModified
m365 spo folder move --webUrl https://contoso.sharepoint.com/sites/project-x --sourceId b8cc341b-9c11-4f2d-aa2b-0ce9c18bcba2 --targetUrl "/sites/project-x/Project files" --skipWait
```

## Response

The command won't return a response on success.
### Standard Response

<Tabs>
<TabItem value="JSON">

```json
{
"Exists": true,
"ExistsAllowThrowForPolicyFailures": true,
"ExistsWithException": true,
"IsWOPIEnabled": false,
"ItemCount": 6,
"Name": "Company",
"ProgID": null,
"ServerRelativeUrl": "/sites/Sales/Icons/Company",
"TimeCreated": "2024-09-26T22:08:53Z",
"TimeLastModified": "2024-09-26T22:09:31Z",
"UniqueId": "d3a37396-ca16-467b-b968-48f5fc41f2b6",
"WelcomePage": ""
}
```

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

```text
Exists : true
ExistsAllowThrowForPolicyFailures: true
ExistsWithException : true
IsWOPIEnabled : false
ItemCount : 6
Name : Company
ProgID : null
ServerRelativeUrl : /sites/Sales/Icons/Company
TimeCreated : 2024-09-26T22:08:53Z
TimeLastModified : 2024-09-26T22:09:31Z
UniqueId : d3a37396-ca16-467b-b968-48f5fc41f2b6
WelcomePage :
```

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

```csv
Exists,ExistsAllowThrowForPolicyFailures,ExistsWithException,IsWOPIEnabled,ItemCount,Name,ProgID,ServerRelativeUrl,TimeCreated,TimeLastModified,UniqueId,WelcomePage
1,1,1,0,6,Company,,/sites/Sales/Icons/Company,2024-09-26T22:08:53Z,2024-09-26T22:09:31Z,d3a37396-ca16-467b-b968-48f5fc41f2b6,
```

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

```md
# spo folder move --webUrl "https://contoso.sharepoint.com/sites/Marketing" --sourceUrl "/Logos/Contoso" --targetUrl "/sites/Sales/Logos"

Date: 27/09/2024

## Company (d3a37396-ca16-467b-b968-48f5fc41f2b6)

Property | Value
---------|-------
Exists | true
ExistsAllowThrowForPolicyFailures | true
ExistsWithException | true
IsWOPIEnabled | false
ItemCount | 6
Name | Company
ServerRelativeUrl | /sites/Sales/Icons/Company
TimeCreated | 2024-09-26T22:08:53Z
TimeLastModified | 2024-09-26T22:09:31Z
UniqueId | d3a37396-ca16-467b-b968-48f5fc41f2b6
WelcomePage |
```

</TabItem>
</Tabs>

### `skipWait` response

## More information

- Move items from a SharePoint document library: [https://support.office.com/en-us/article/move-or-copy-items-from-a-sharepoint-document-library-00e2f483-4df3-46be-a861-1f5f0c1a87bc](https://support.office.com/en-us/article/move-or-copy-items-from-a-sharepoint-document-library-00e2f483-4df3-46be-a861-1f5f0c1a87bc)
The command won't return a response on success.
17 changes: 17 additions & 0 deletions docs/docs/v10-upgrade-guidance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ In the past versions of CLI for Microsoft 365, the command had no output. When u

When using the [spo file move](./cmd/spo/file/file-move.mdx) command, please use the new command input. This means that you'll have to remove option `--retainEditorAndModified` from your scripts and automation tools.

### Updated command `spo folder move`

Because of some limitations of the current [spo folder move](./cmd/spo/folder/folder-move.mdx) command, we have decided to move it to a new endpoint. This change is necessary to ensure the command's functionality and reliability. Because of the new endpoint, the command input and output have changed.

**Command options:**

Unfortunately, we had to drop the `--retainEditorAndModified` and `--bypassSharedLock` options as it's no longer supported by the new endpoint. In return, we were able to add a new option:
- `--skipWait`: Don't wait for the move operation to complete.

**Command output:**

In the past versions of CLI for Microsoft 365, the command had no output. When using option `--nameConflictBehavior rename`, it's hard for the user to know what the actual name of the moved folder is. With the new endpoint, the command now returns the folder information about the destination folder, providing you with all the info you need to execute other commands on this folder.

#### What action do I need to take?

When using the [spo folder move](./cmd/spo/folder/folder-move.mdx) command, please use the new command input. This means that you'll have to remove options `--retainEditorAndModified` and `--bypassSharedLock` from your scripts and automation tools.

### Removed `spo folder rename` alias

The `spo folder rename` command was removed and replaced by the [spo folder set](./cmd/spo/folder/folder-set.mdx) command.
Expand Down
12 changes: 6 additions & 6 deletions src/m365/spo/commands/file/file-copy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './file-copy.js';
import { settingsNames } from '../../../../settingsNames.js';
import { CreateCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { CreateFileCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';

describe(commands.FILE_COPY, () => {
const sourceWebUrl = 'https://contoso.sharepoint.com/sites/Sales';
Expand Down Expand Up @@ -94,7 +94,7 @@ describe(commands.FILE_COPY, () => {
commandInfo = cli.getCommandInfo(command);

sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => settingName === settingsNames.prompt ? false : defaultValue);
spoUtilCreateCopyJobStub = sinon.stub(spo, 'createCopyJob').resolves(copyJobInfo);
spoUtilCreateCopyJobStub = sinon.stub(spo, 'createFileCopyJob').resolves(copyJobInfo);
});

beforeEach(() => {
Expand Down Expand Up @@ -300,7 +300,7 @@ describe(commands.FILE_COPY, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Fail,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Fail,
bypassSharedLock: false,
ignoreVersionHistory: false,
operation: 'copy',
Expand Down Expand Up @@ -332,7 +332,7 @@ describe(commands.FILE_COPY, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Fail,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Fail,
bypassSharedLock: false,
ignoreVersionHistory: false,
operation: 'copy',
Expand Down Expand Up @@ -367,7 +367,7 @@ describe(commands.FILE_COPY, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Rename,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Rename,
bypassSharedLock: true,
ignoreVersionHistory: true,
operation: 'copy',
Expand Down Expand Up @@ -400,7 +400,7 @@ describe(commands.FILE_COPY, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Replace,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Replace,
bypassSharedLock: false,
ignoreVersionHistory: false,
operation: 'copy',
Expand Down
14 changes: 7 additions & 7 deletions src/m365/spo/commands/file/file-copy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Logger } from '../../../../cli/Logger.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { CreateCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { CreateFileCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { urlUtil } from '../../../../utils/urlUtil.js';
import { validation } from '../../../../utils/validation.js';
import SpoCommand from '../../../base/SpoCommand.js';
Expand Down Expand Up @@ -140,7 +140,7 @@ class SpoFileCopyCommand extends SpoCommand {
newName += sourceServerRelativePath.substring(sourceServerRelativePath.lastIndexOf('.'));
}

const copyJobResponse = await spo.createCopyJob(
const copyJobResponse = await spo.createFileCopyJob(
args.options.webUrl,
sourcePath,
destinationPath,
Expand Down Expand Up @@ -208,16 +208,16 @@ class SpoFileCopyCommand extends SpoCommand {
return file.DecodedUrl;
}

private getNameConflictBehaviorValue(nameConflictBehavior?: string): CreateCopyJobsNameConflictBehavior {
private getNameConflictBehaviorValue(nameConflictBehavior?: string): CreateFileCopyJobsNameConflictBehavior {
switch (nameConflictBehavior?.toLowerCase()) {
case 'fail':
return CreateCopyJobsNameConflictBehavior.Fail;
return CreateFileCopyJobsNameConflictBehavior.Fail;
case 'replace':
return CreateCopyJobsNameConflictBehavior.Replace;
return CreateFileCopyJobsNameConflictBehavior.Replace;
case 'rename':
return CreateCopyJobsNameConflictBehavior.Rename;
return CreateFileCopyJobsNameConflictBehavior.Rename;
default:
return CreateCopyJobsNameConflictBehavior.Fail;
return CreateFileCopyJobsNameConflictBehavior.Fail;
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/m365/spo/commands/file/file-move.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './file-move.js';
import { CreateCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { CreateFileCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { settingsNames } from '../../../../settingsNames.js';

describe(commands.FILE_MOVE, () => {
Expand Down Expand Up @@ -95,7 +95,7 @@ describe(commands.FILE_MOVE, () => {
auth.connection.active = true;
commandInfo = cli.getCommandInfo(command);
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => settingName === settingsNames.prompt ? false : defaultValue);
spoUtilCreateCopyJobStub = sinon.stub(spo, 'createCopyJob').resolves(copyJobInfo);
spoUtilCreateCopyJobStub = sinon.stub(spo, 'createFileCopyJob').resolves(copyJobInfo);
});

beforeEach(() => {
Expand Down Expand Up @@ -296,7 +296,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Fail,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Fail,
bypassSharedLock: false,
includeItemPermissions: false,
operation: 'move',
Expand Down Expand Up @@ -328,7 +328,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Fail,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Fail,
bypassSharedLock: false,
includeItemPermissions: false,
operation: 'move',
Expand Down Expand Up @@ -360,7 +360,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Fail,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Fail,
bypassSharedLock: false,
includeItemPermissions: false,
operation: 'move',
Expand Down Expand Up @@ -392,7 +392,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Replace,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Replace,
bypassSharedLock: false,
includeItemPermissions: false,
operation: 'move',
Expand Down Expand Up @@ -427,7 +427,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Rename,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Rename,
bypassSharedLock: true,
includeItemPermissions: true,
operation: 'move',
Expand Down Expand Up @@ -460,7 +460,7 @@ describe(commands.FILE_MOVE, () => {
sourceAbsoluteUrl,
destAbsoluteTargetUrl,
{
nameConflictBehavior: CreateCopyJobsNameConflictBehavior.Replace,
nameConflictBehavior: CreateFileCopyJobsNameConflictBehavior.Replace,
bypassSharedLock: false,
includeItemPermissions: false,
operation: 'move',
Expand Down
14 changes: 7 additions & 7 deletions src/m365/spo/commands/file/file-move.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Logger } from '../../../../cli/Logger.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { CreateCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { CreateFileCopyJobsNameConflictBehavior, spo } from '../../../../utils/spo.js';
import { urlUtil } from '../../../../utils/urlUtil.js';
import { validation } from '../../../../utils/validation.js';
import SpoCommand from '../../../base/SpoCommand.js';
Expand Down Expand Up @@ -141,7 +141,7 @@ class SpoFileMoveCommand extends SpoCommand {
newName += sourceServerRelativePath.substring(sourceServerRelativePath.lastIndexOf('.'));
}

const copyJobResponse = await spo.createCopyJob(
const copyJobResponse = await spo.createFileCopyJob(
args.options.webUrl,
sourcePath,
destinationPath,
Expand Down Expand Up @@ -209,16 +209,16 @@ class SpoFileMoveCommand extends SpoCommand {
return file.DecodedUrl;
}

private getNameConflictBehaviorValue(nameConflictBehavior?: string): CreateCopyJobsNameConflictBehavior {
private getNameConflictBehaviorValue(nameConflictBehavior?: string): CreateFileCopyJobsNameConflictBehavior {
switch (nameConflictBehavior?.toLowerCase()) {
case 'fail':
return CreateCopyJobsNameConflictBehavior.Fail;
return CreateFileCopyJobsNameConflictBehavior.Fail;
case 'replace':
return CreateCopyJobsNameConflictBehavior.Replace;
return CreateFileCopyJobsNameConflictBehavior.Replace;
case 'rename':
return CreateCopyJobsNameConflictBehavior.Rename;
return CreateFileCopyJobsNameConflictBehavior.Rename;
default:
return CreateCopyJobsNameConflictBehavior.Fail;
return CreateFileCopyJobsNameConflictBehavior.Fail;
}
}

Expand Down
Loading

0 comments on commit d48e589

Please sign in to comment.