Skip to content

Commit 0c5c478

Browse files
committed
f
1 parent 7f954ed commit 0c5c478

File tree

4 files changed

+314
-88
lines changed

4 files changed

+314
-88
lines changed

src/m365/spe/commands/container/container-add.spec.ts

+4-65
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import command from './container-add.js';
1414
import { spo } from '../../../../utils/spo.js';
1515
import { z } from 'zod';
1616
import { CommandError } from '../../../../Command.js';
17+
import { spe } from '../../../../utils/spe.js';
1718

1819
describe(commands.CONTAINER_ADD, () => {
1920
const spoAdminUrl = 'https://contoso-admin.sharepoint.com';
@@ -48,6 +49,8 @@ describe(commands.CONTAINER_ADD, () => {
4849
sinon.stub(pid, 'getProcessName').returns('');
4950
sinon.stub(session, 'getId').returns('');
5051

52+
sinon.stub(spe, 'getContainerTypeIdByName').resolves(containerTypeId);
53+
5154
auth.connection.active = true;
5255
auth.connection.spoUrl = spoAdminUrl.replace('-admin.sharepoint.com', '.sharepoint.com');
5356
commandInfo = cli.getCommandInfo(command);
@@ -72,9 +75,7 @@ describe(commands.CONTAINER_ADD, () => {
7275

7376
afterEach(() => {
7477
sinonUtil.restore([
75-
request.post,
76-
spo.getAllContainerTypes,
77-
cli.handleMultipleResultsFound
78+
request.post
7879
]);
7980
});
8081

@@ -190,68 +191,6 @@ describe(commands.CONTAINER_ADD, () => {
190191
});
191192
});
192193

193-
it('correctly creates a new container with containerTypeName when there are multiple with the same name', async () => {
194-
const postStub = sinon.stub(request, 'post').callsFake(async (opts) => {
195-
if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers') {
196-
return requestResponse;
197-
}
198-
199-
throw 'Invalid POST request: ' + opts.url;
200-
});
201-
202-
const containerTypes = [
203-
{
204-
AzureSubscriptionId: '/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)/',
205-
ContainerTypeId: `/Guid(${containerTypeId})/`,
206-
CreationDate: '3/11/2024 2:38:56 PM',
207-
DisplayName: containerTypeName,
208-
ExpiryDate: '3/11/2028 2:38:56 PM',
209-
IsBillingProfileRequired: true,
210-
OwningAppId: '/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)/',
211-
OwningTenantId: '/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)/',
212-
Region: 'West Europe',
213-
ResourceGroup: 'Standard group',
214-
SPContainerTypeBillingClassification: 'Standard'
215-
},
216-
{
217-
AzureSubscriptionId: '/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)/',
218-
ContainerTypeId: '/Guid(9fb77a21-face-4c4a-9d17-c6ba82ab66d9)/',
219-
CreationDate: '3/11/2024 2:38:56 PM',
220-
DisplayName: containerTypeName,
221-
ExpiryDate: '3/11/2028 2:38:56 PM',
222-
IsBillingProfileRequired: true,
223-
OwningAppId: '/Guid(000b8660-9a44-4a7c-9c02-657f3ff5d5ac)/',
224-
OwningTenantId: '/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)/',
225-
Region: 'West Europe',
226-
ResourceGroup: 'Standard group',
227-
SPContainerTypeBillingClassification: 'Standard'
228-
}
229-
];
230-
sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypes);
231-
232-
const stubMultiResults = sinon.stub(cli, 'handleMultipleResultsFound').resolves(containerTypes.find(c => c.ContainerTypeId === `/Guid(${containerTypeId})/`)!);
233-
234-
await command.action(logger, { options: { name: containerName, containerTypeName: containerTypeName } });
235-
assert(stubMultiResults.calledOnce);
236-
assert.deepStrictEqual(postStub.lastCall.args[0].data, {
237-
displayName: containerName,
238-
description: undefined,
239-
containerTypeId: containerTypeId,
240-
settings: {
241-
isOcrEnabled: undefined,
242-
itemMajorVersionLimit: undefined,
243-
isItemVersioningEnabled: undefined
244-
}
245-
});
246-
});
247-
248-
it('throws error when containerTypeName was not found', async () => {
249-
sinon.stub(spo, 'getAllContainerTypes').resolves([]);
250-
251-
await assert.rejects(command.action(logger, { options: { name: containerName, containerTypeName: containerTypeName } }),
252-
new CommandError(`The specified container type '${containerTypeName}' does not exist.`));
253-
});
254-
255194
it('correctly handles error', async () => {
256195
sinon.stub(request, 'post').rejects({
257196
error: {

src/m365/spe/commands/container/container-add.ts

+4-23
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import { zod } from '../../../../utils/zod.js';
44
import { Logger } from '../../../../cli/Logger.js';
55
import commands from '../../commands.js';
66
import { validation } from '../../../../utils/validation.js';
7-
import { formatting } from '../../../../utils/formatting.js';
8-
import { cli } from '../../../../cli/cli.js';
9-
import { ContainerTypeProperties, spo } from '../../../../utils/spo.js';
7+
import { spe } from '../../../../utils/spe.js';
8+
import { spo } from '../../../../utils/spo.js';
109
import GraphCommand from '../../../base/GraphCommand.js';
1110
import request, { CliRequestOptions } from '../../../../request.js';
1211

@@ -97,26 +96,8 @@ class SpeContainerAddCommand extends GraphCommand {
9796
await logger.logToStderr(`Getting container type with name '${options.containerTypeName}'...`);
9897
}
9998

100-
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.verbose);
101-
const allContainerTypes: ContainerTypeProperties[] = await spo.getAllContainerTypes(spoAdminUrl, logger, this.verbose);
102-
const containerTypes = allContainerTypes.filter(ct => ct.DisplayName.toLowerCase() === options.containerTypeName!.toLowerCase());
103-
104-
if (containerTypes.length === 0) {
105-
throw new Error(`The specified container type '${options.containerTypeName}' does not exist.`);
106-
}
107-
108-
// Convert CSOM GUIDs to real GUIDs
109-
containerTypes.forEach(ct => {
110-
ct.ContainerTypeId = formatting.extractCsomGuid(ct.ContainerTypeId);
111-
});
112-
113-
if (containerTypes.length > 1) {
114-
const containerTypeKeyValuePair = formatting.convertArrayToHashTable('ContainerTypeId', containerTypes);
115-
const containerType = await cli.handleMultipleResultsFound<ContainerTypeProperties>(`Multiple container types with name '${options.containerTypeName}' found.`, containerTypeKeyValuePair);
116-
return containerType.ContainerTypeId;
117-
}
118-
119-
return containerTypes[0].ContainerTypeId;
99+
const adminUrl = await spo.getSpoAdminUrl(logger, this.verbose);
100+
return spe.getContainerTypeIdByName(adminUrl, options.containerTypeName!);
120101
}
121102
}
122103

src/utils/spe.spec.ts

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
2+
import assert from 'assert';
3+
import sinon from 'sinon';
4+
import { spe } from './spe.js';
5+
import { sinonUtil } from './sinonUtil.js';
6+
import request from '../request.js';
7+
import auth from '../Auth.js';
8+
import config from '../config.js';
9+
import { cli } from '../cli/cli.js';
10+
11+
describe('utils/spe', () => {
12+
const siteUrl = 'https://contoso.sharepoint.com';
13+
const adminUrl = siteUrl.replace('.sharepoint.com', '-admin.sharepoint.com');
14+
15+
const containerTypeResponse = [
16+
{
17+
_ObjectType_: 'Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties',
18+
ApplicationRedirectUrl: null,
19+
AzureSubscriptionId: '/Guid(00000000-0000-0000-0000-000000000000)/',
20+
ContainerTypeId: '/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/',
21+
CreationDate: null,
22+
DisplayName: 'test1',
23+
ExpiryDate: null,
24+
IsBillingProfileRequired: true,
25+
OwningAppId: '/Guid(df4085cc-9a38-4255-badc-5c5225610475)/',
26+
OwningTenantId: '/Guid(00000000-0000-0000-0000-000000000000)/',
27+
Region: null,
28+
ResourceGroup: null,
29+
SPContainerTypeBillingClassification: 0
30+
},
31+
{
32+
_ObjectType_: 'Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties',
33+
ApplicationRedirectUrl: null,
34+
AzureSubscriptionId: '/Guid(00000000-0000-0000-0000-000000000000)/',
35+
ContainerTypeId: '/Guid(880ab3bd-5b68-01d4-3744-01a7656cf2ba)/',
36+
CreationDate: null,
37+
DisplayName: 'test2',
38+
ExpiryDate: null,
39+
IsBillingProfileRequired: true,
40+
OwningAppId: '/Guid(50785fde-3082-47ac-a36d-06282ac5c7da)/',
41+
OwningTenantId: '/Guid(00000000-0000-0000-0000-000000000000)/',
42+
Region: null,
43+
ResourceGroup: null,
44+
SPContainerTypeBillingClassification: 0
45+
}
46+
];
47+
48+
before(() => {
49+
auth.connection.active = true;
50+
auth.connection.spoUrl = siteUrl;
51+
});
52+
53+
afterEach(() => {
54+
sinonUtil.restore([
55+
request.post
56+
]);
57+
});
58+
59+
after(() => {
60+
auth.connection.active = false;
61+
auth.connection.spoUrl = undefined;
62+
sinon.restore();
63+
});
64+
65+
it('correctly retrieves a list of container types', async () => {
66+
const postStub = sinon.stub(request, 'post').callsFake(async (opts) => {
67+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
68+
return [
69+
{
70+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73"
71+
}, 46, {
72+
"IsNull": false
73+
}, 47, containerTypeResponse
74+
];
75+
}
76+
77+
throw 'Invalid request';
78+
});
79+
80+
await spe.getAllContainerTypes(adminUrl);
81+
assert.deepStrictEqual(postStub.lastCall.args[0].data, `<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="${config.applicationName}" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="46" ObjectPathId="45" /><Method Name="GetSPOContainerTypes" Id="47" ObjectPathId="45"><Parameters><Parameter Type="Enum">1</Parameter></Parameters></Method></Actions><ObjectPaths><Constructor Id="45" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /></ObjectPaths></Request>`);
82+
});
83+
84+
it('correctly outputs a list of container types', async () => {
85+
sinon.stub(request, 'post').callsFake(async (opts) => {
86+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
87+
return [
88+
{
89+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73"
90+
}, 46, {
91+
"IsNull": false
92+
}, 47, containerTypeResponse
93+
];
94+
}
95+
96+
throw 'Invalid request';
97+
});
98+
99+
const actual = await spe.getAllContainerTypes(adminUrl);
100+
assert.deepStrictEqual(actual, [
101+
{
102+
ApplicationRedirectUrl: null,
103+
AzureSubscriptionId: '00000000-0000-0000-0000-000000000000',
104+
ContainerTypeId: '073269af-f1d2-042d-2ef5-5bdd6ac83115',
105+
CreationDate: null,
106+
DisplayName: 'test1',
107+
ExpiryDate: null,
108+
IsBillingProfileRequired: true,
109+
OwningAppId: 'df4085cc-9a38-4255-badc-5c5225610475',
110+
OwningTenantId: '00000000-0000-0000-0000-000000000000',
111+
Region: null,
112+
ResourceGroup: null,
113+
SPContainerTypeBillingClassification: 0
114+
},
115+
{
116+
ApplicationRedirectUrl: null,
117+
AzureSubscriptionId: '00000000-0000-0000-0000-000000000000',
118+
ContainerTypeId: '880ab3bd-5b68-01d4-3744-01a7656cf2ba',
119+
CreationDate: null,
120+
DisplayName: 'test2',
121+
ExpiryDate: null,
122+
IsBillingProfileRequired: true,
123+
OwningAppId: '50785fde-3082-47ac-a36d-06282ac5c7da',
124+
OwningTenantId: '00000000-0000-0000-0000-000000000000',
125+
Region: null,
126+
ResourceGroup: null,
127+
SPContainerTypeBillingClassification: 0
128+
}
129+
]);
130+
});
131+
132+
it('correctly throws error when retrieving container types', async () => {
133+
sinon.stub(request, 'post').callsFake(async (opts) => {
134+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
135+
return [
136+
{
137+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.7324.1200", "ErrorInfo": {
138+
"ErrorMessage": "An error has occurred", "ErrorValue": null, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d", "ErrorCode": -1, "ErrorTypeName": "SPException"
139+
}, "TraceCorrelationId": "e13c489e-2026-5000-8242-7ec96d02ba1d"
140+
}
141+
];
142+
}
143+
144+
throw 'Invalid request';
145+
});
146+
147+
await assert.rejects(spe.getAllContainerTypes(adminUrl), new Error('An error has occurred'));
148+
});
149+
150+
it('correctly retrieves the container type ID by name when using getContainerTypeIdByName', async () => {
151+
sinon.stub(request, 'post').callsFake(async (opts) => {
152+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
153+
return [
154+
{
155+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73"
156+
}, 46, {
157+
"IsNull": false
158+
}, 47, containerTypeResponse
159+
];
160+
}
161+
162+
throw 'Invalid request';
163+
});
164+
165+
const actual = await spe.getContainerTypeIdByName(adminUrl, 'test2');
166+
assert.strictEqual(actual, '880ab3bd-5b68-01d4-3744-01a7656cf2ba');
167+
});
168+
169+
it('correctly throws error when name not found when using getContainerTypeIdByName', async () => {
170+
sinon.stub(request, 'post').callsFake(async (opts) => {
171+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
172+
return [
173+
{
174+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73"
175+
}, 46, {
176+
"IsNull": false
177+
}, 47, containerTypeResponse
178+
];
179+
}
180+
181+
throw 'Invalid request';
182+
});
183+
184+
await assert.rejects(spe.getContainerTypeIdByName(adminUrl, 'nonexistent'),
185+
new Error(`The specified container type 'nonexistent' does not exist.`));
186+
});
187+
188+
it('correctly handles multiple results when using getContainerTypeIdByName', async () => {
189+
const containerTypes = [
190+
...containerTypeResponse,
191+
{
192+
_ObjectType_: 'Microsoft.Online.SharePoint.TenantAdministration.SPContainerTypeProperties',
193+
ApplicationRedirectUrl: null,
194+
AzureSubscriptionId: '/Guid(00000000-0000-0000-0000-000000000000)/',
195+
ContainerTypeId: '/Guid(4c8bc473-2d5a-474d-b2f3-fc60b7d39726)/',
196+
CreationDate: null,
197+
DisplayName: 'test1',
198+
ExpiryDate: null,
199+
IsBillingProfileRequired: true,
200+
OwningAppId: '/Guid(48cc3066-7f0d-4cb9-80fb-f7891069c0f9)/',
201+
OwningTenantId: '/Guid(00000000-0000-0000-0000-000000000000)/',
202+
Region: null,
203+
ResourceGroup: null,
204+
SPContainerTypeBillingClassification: 0
205+
}
206+
];
207+
208+
sinon.stub(request, 'post').callsFake(async (opts) => {
209+
if (opts.url === `${adminUrl}/_vti_bin/client.svc/ProcessQuery`) {
210+
return [
211+
{
212+
"SchemaVersion": "15.0.0.0", "LibraryVersion": "16.0.24817.12005", "ErrorInfo": null, "TraceCorrelationId": "2d63d39f-3016-0000-a532-30514e76ae73"
213+
}, 46, {
214+
"IsNull": false
215+
}, 47, containerTypes
216+
];
217+
}
218+
219+
throw 'Invalid request';
220+
});
221+
222+
const stubMultiResults = sinon.stub(cli, 'handleMultipleResultsFound').resolves(containerTypes.find(c => c.ContainerTypeId === '/Guid(4c8bc473-2d5a-474d-b2f3-fc60b7d39726)/')!);
223+
const actual = await spe.getContainerTypeIdByName(adminUrl, 'test1');
224+
assert(stubMultiResults.calledOnce);
225+
assert.strictEqual(actual, '4c8bc473-2d5a-474d-b2f3-fc60b7d39726');
226+
});
227+
});

0 commit comments

Comments
 (0)