Skip to content

Commit 90f1959

Browse files
committed
addressed code review feedback
1 parent 62080a9 commit 90f1959

File tree

4 files changed

+84
-17
lines changed

4 files changed

+84
-17
lines changed

.devproxy/api-specs/sharepoint.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,47 @@ paths:
104104
responses:
105105
200:
106106
description: OK
107+
/_api/web/GetFileById({fileId})/versions/:
108+
get:
109+
parameters:
110+
- name: fileId
111+
in: path
112+
required: true
113+
schema:
114+
type: string
115+
example: "'19bbfec4-4425-4660-95cb-da1887baa7b9'"
116+
security:
117+
- delegated:
118+
- AllSites.Read
119+
- AllSites.Write
120+
- AllSites.Manage
121+
- AllSites.FullControl
122+
responses:
123+
200:
124+
description: OK
125+
/_api/web/GetFileById({fileId})/versions({versionId})/SetExpirationDate():
126+
post:
127+
parameters:
128+
- name: fileId
129+
in: path
130+
required: true
131+
schema:
132+
type: string
133+
example: "'19bbfec4-4425-4660-95cb-da1887baa7b9'"
134+
- name: versionId
135+
in: path
136+
required: true
137+
schema:
138+
type: integer
139+
example: 1030
140+
security:
141+
- delegated:
142+
- AllSites.Write
143+
- AllSites.Manage
144+
- AllSites.FullControl
145+
responses:
146+
200:
147+
description: OK
107148
/_api/web/GetFolderByServerRelativePath(DecodedUrl={folderPath}):
108149
get:
109150
parameters:

docs/docs/cmd/spo/file/file-version-keep.mdx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,34 @@ m365 spo file version keep [options]
2828

2929
<Global />
3030

31+
## Permissions
32+
33+
<Tabs>
34+
<TabItem value="Delegated" label="Delegated">
35+
36+
| Resource | Permissions |
37+
|------------|--------------------------------------------------|
38+
| SharePoint | AllSites.Write, AllSites.Manage, AllSites.FullControl |
39+
40+
</TabItem>
41+
<TabItem value="Application" label="Application">
42+
43+
| Resource | Permissions |
44+
|------------|-----------------------------------------------------|
45+
| SharePoint | Sites.ReadWrite.All, Sites.Manage.All, Sites.FullControl.All |
46+
47+
</TabItem>
48+
</Tabs>
49+
3150
## Examples
3251

33-
Mark a file version as never expiring by webUrl and fileUrl.
52+
Mark a file version as never expiring by file URL.
3453

3554
```sh
3655
m365 spo file version keep --webUrl "https://contoso.sharepoint.com/sites/marketing" --fileUrl "/sites/marketing/Documents/report.docx" --label "6.0"
3756
```
3857

39-
Mark a file version as never expiring by webUrl and fileId.
58+
Mark a file version as never expiring by file ID.
4059

4160
```sh
4261
m365 spo file version keep --webUrl "https://contoso.sharepoint.com/sites/marketing" --fileId "12345678-90ab-cdef-1234-567890abcdef" --label "6.0"

src/m365/spo/commands/file/file-version-keep.spec.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ describe(commands.FILE_VERSION_KEEP, () => {
8585
assert.strictEqual(actual.success, false);
8686
});
8787

88+
it('fails validation if label is not specified', async () => {
89+
const actual = commandOptionsSchema.safeParse({ webUrl: validWebUrl, fileUrl: validFileUrl });
90+
assert.strictEqual(actual.success, false);
91+
});
92+
8893
it('passes validation if fileUrl is specified', async () => {
8994
const actual = await command.validate({ options: { webUrl: validWebUrl, label: validLabel, fileUrl: validFileUrl } }, commandInfo);
9095
assert.strictEqual(actual, true);
@@ -97,7 +102,7 @@ describe(commands.FILE_VERSION_KEEP, () => {
97102

98103
it('ensures that a specific file version will never expire (fileUrl)', async () => {
99104
sinon.stub(request, 'get').callsFake(async (opts) => {
100-
if (opts.url === `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions/?$filter=VersionLabel eq '${validLabel}'`) {
105+
if (opts.url === `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions/?$filter=VersionLabel eq '${validLabel}'&$select=Id`) {
101106
return {
102107
value: [
103108
{
@@ -111,7 +116,7 @@ describe(commands.FILE_VERSION_KEEP, () => {
111116
throw 'Invalid request';
112117
});
113118

114-
sinon.stub(request, 'post').callsFake(async (opts) => {
119+
const postStub: sinon.SinonStub = sinon.stub(request, 'post').callsFake(async (opts) => {
115120
if (opts.url === `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions(1)/SetExpirationDate()`) {
116121
return;
117122
}
@@ -120,11 +125,12 @@ describe(commands.FILE_VERSION_KEEP, () => {
120125
});
121126

122127
await command.action(logger, { options: { webUrl: validWebUrl, fileUrl: validFileUrl, label: validLabel, verbose: true } });
128+
assert.strictEqual(postStub.lastCall.args[0].url, `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions(1)/SetExpirationDate()`);
123129
});
124130

125131
it('ensures that a specific file version will never expire (fileId)', async () => {
126132
sinon.stub(request, 'get').callsFake(async (opts) => {
127-
if (opts.url === `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions/?$filter=VersionLabel eq '${validLabel}'`) {
133+
if (opts.url === `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions/?$filter=VersionLabel eq '${validLabel}'&$select=Id`) {
128134
return {
129135
value: [
130136
{
@@ -138,7 +144,7 @@ describe(commands.FILE_VERSION_KEEP, () => {
138144
throw 'Invalid request';
139145
});
140146

141-
sinon.stub(request, 'post').callsFake(async (opts) => {
147+
const postStub: sinon.SinonStub = sinon.stub(request, 'post').callsFake(async (opts) => {
142148
if (opts.url === `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions(1)/SetExpirationDate()`) {
143149
return;
144150
}
@@ -147,11 +153,12 @@ describe(commands.FILE_VERSION_KEEP, () => {
147153
});
148154

149155
await command.action(logger, { options: { webUrl: validWebUrl, fileId: validFileId, label: validLabel, verbose: true } });
156+
assert.strictEqual(postStub.lastCall.args[0].url, `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions(1)/SetExpirationDate()`);
150157
});
151158

152159
it('correctly handles error when the specified version does not exist', async () => {
153160
sinon.stub(request, 'get').callsFake(async (opts) => {
154-
if (opts.url === `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions/?$filter=VersionLabel eq '${validLabel}'`) {
161+
if (opts.url === `${validWebUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${formatting.encodeQueryParameter(validFileUrl)}')/versions/?$filter=VersionLabel eq '${validLabel}'&$select=Id`) {
155162
return { value: [] };
156163
}
157164

@@ -163,7 +170,7 @@ describe(commands.FILE_VERSION_KEEP, () => {
163170

164171
it('correctly handles API OData error', async () => {
165172
sinon.stub(request, 'get').callsFake(async (opts) => {
166-
if (opts.url === `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions/?$filter=VersionLabel eq '${validLabel}'`) {
173+
if (opts.url === `${validWebUrl}/_api/web/GetFileById('${validFileId}')/versions/?$filter=VersionLabel eq '${validLabel}'&$select=Id`) {
167174
throw {
168175
error: {
169176
'odata.error': {

src/m365/spo/commands/file/file-version-keep.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export const options = globalOptionsZod
1717
}))
1818
),
1919
fileUrl: z.string().optional(),
20-
fileId: zod.alias('i', z.string().optional()
20+
fileId: zod.alias('i', z.string()
2121
.refine(id => id === undefined || validation.isValidGuid(id), id => ({
2222
message: `'${id}' is not a valid GUID.`
23-
}))
23+
})).optional()
2424
),
2525
label: z.string()
2626
})
@@ -47,7 +47,7 @@ class SpoFileVersionKeepCommand extends SpoCommand {
4747

4848
public getRefinedSchema(schema: z.ZodTypeAny): z.ZodEffects<any> | undefined {
4949
return schema
50-
.refine(options => (options.fileUrl !== undefined) !== (options.fileId !== undefined), {
50+
.refine(options => [options.fileUrl, options.fileId].filter(o => o !== undefined).length === 1, {
5151
message: `Specify 'fileUrl' or 'fileId', but not both.`
5252
});
5353
}
@@ -58,12 +58,12 @@ class SpoFileVersionKeepCommand extends SpoCommand {
5858
}
5959

6060
try {
61-
const fileUrl = this.getFileUrl(args.options.webUrl, args.options.fileUrl, args.options.fileId);
61+
const baseApiUrl = this.getBaseApiUrl(args.options.webUrl, args.options.fileUrl, args.options.fileId);
6262

6363
const requestVersionOptions: CliRequestOptions = {
64-
url: `${fileUrl}/versions/?$filter=VersionLabel eq '${args.options.label}'`,
64+
url: `${baseApiUrl}/versions/?$filter=VersionLabel eq '${args.options.label}'&$select=Id`,
6565
headers: {
66-
'accept': 'application/json;odata=nometadata'
66+
accept: 'application/json;odata=nometadata'
6767
},
6868
responseType: 'json'
6969
};
@@ -76,9 +76,9 @@ class SpoFileVersionKeepCommand extends SpoCommand {
7676
}
7777

7878
const requestExpirationOptions: CliRequestOptions = {
79-
url: `${fileUrl}/versions(${version.ID})/SetExpirationDate()`,
79+
url: `${baseApiUrl}/versions(${version.ID})/SetExpirationDate()`,
8080
headers: {
81-
'accept': 'application/json;odata=nometadata',
81+
accept: 'application/json;odata=nometadata',
8282
'content-type': 'application/json'
8383
},
8484
responseType: 'json'
@@ -91,7 +91,7 @@ class SpoFileVersionKeepCommand extends SpoCommand {
9191
}
9292
}
9393

94-
private getFileUrl(webUrl: string, fileUrl?: string, fileId?: string): string {
94+
private getBaseApiUrl(webUrl: string, fileUrl?: string, fileId?: string): string {
9595
let requestUrl: string;
9696

9797
if (fileUrl) {

0 commit comments

Comments
 (0)