Skip to content

Commit 4fbe863

Browse files
committed
Adds command 'spe container recyclebinitem list'. Closes #6156
1 parent 908b842 commit 4fbe863

17 files changed

+844
-295
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import Global from '/docs/cmd/_global.mdx';
2+
import Tabs from '@theme/Tabs';
3+
import TabItem from '@theme/TabItem';
4+
5+
# spe container recyclebinitem list
6+
7+
Lists deleted containers of a specific container type
8+
9+
## Usage
10+
11+
```sh
12+
m365 spe container recyclebinitem list [options]
13+
```
14+
15+
## Options
16+
17+
```md definition-list
18+
`--containerTypeId [containerTypeId]`
19+
: The container type ID of the container instance. Use either `containerTypeId` or `containerTypeName` but not both.
20+
21+
`--containerTypeName [containerTypeName]`
22+
: The container type name of the container instance. Use either `containerTypeId` or `containerTypeName` but not both.
23+
```
24+
25+
<Global />
26+
27+
## Examples
28+
29+
List deleted containers of a specific container type specified by id.
30+
31+
```sh
32+
m365 spe container recyclebinitem list --containerTypeId "91710488-5756-407f-9046-fbe5f0b4de73"
33+
```
34+
35+
List deleted containers of a specific container type specified by name.
36+
37+
```sh
38+
m365 spe container recyclebinitem list --containerTypeName "My container type name"
39+
```
40+
41+
## Response
42+
43+
<Tabs>
44+
<TabItem value="JSON">
45+
46+
```json
47+
[
48+
{
49+
"id": "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z",
50+
"displayName": "My Application Storage Container",
51+
"containerTypeId": "1a55ba46-a673-45a4-b0d9-bd9913d06957",
52+
"createdDateTime": "2025-04-15T21:51:48Z",
53+
"settings": {
54+
"isOcrEnabled": false
55+
}
56+
}
57+
]
58+
```
59+
60+
</TabItem>
61+
<TabItem value="Text">
62+
63+
```text
64+
id displayName
65+
------------------------------------------------------------------ --------------------------------
66+
b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z My Application Storage Container
67+
```
68+
69+
</TabItem>
70+
<TabItem value="CSV">
71+
72+
```csv
73+
id,displayName,containerTypeId,createdDateTime
74+
b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z,My Application Storage Container,1a55ba46-a673-45a4-b0d9-bd9913d06957,2025-04-15T21:51:48Z
75+
```
76+
77+
</TabItem>
78+
<TabItem value="Markdown">
79+
80+
```md
81+
# spe container recyclebinitem list --containerTypeId "1a55ba46-a673-45a4-b0d9-bd9913d06957"
82+
83+
Date: 18/04/2025
84+
85+
## My Application Storage Container (b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z)
86+
87+
Property | Value
88+
---------|-------
89+
id | b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z
90+
displayName | My Application Storage Container
91+
containerTypeId | 1a55ba46-a673-45a4-b0d9-bd9913d06957
92+
createdDateTime | 2025-04-15T21:51:48Z
93+
```
94+
95+
</TabItem>
96+
</Tabs>

docs/src/config/sidebars.ts

+5
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,11 @@ const sidebars: SidebarsConfig = {
21322132
type: 'doc',
21332133
label: 'container list',
21342134
id: 'cmd/spe/container/container-list'
2135+
},
2136+
{
2137+
type: 'doc',
2138+
label: 'container recyclebinitem list',
2139+
id: 'cmd/spe/container/container-recyclebinitem-list'
21352140
}
21362141
]
21372142
},

src/m365/spe/commands.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default {
44
CONTAINER_ACTIVATE: `${prefix} container activate`,
55
CONTAINER_GET: `${prefix} container get`,
66
CONTAINER_LIST: `${prefix} container list`,
7+
CONTAINER_RECYCLEBINITEM_LIST: `${prefix} container recyclebinitem list`,
78
CONTAINERTYPE_ADD: `${prefix} containertype add`,
89
CONTAINERTYPE_GET: `${prefix} containertype get`,
910
CONTAINERTYPE_LIST: `${prefix} containertype list`

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

+11-53
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import { session } from '../../../../utils/session.js';
99
import { sinonUtil } from '../../../../utils/sinonUtil.js';
1010
import commands from '../../commands.js';
1111
import command from './container-list.js';
12-
import { spo } from '../../../../utils/spo.js';
1312
import { CommandError } from '../../../../Command.js';
1413
import { CommandInfo } from '../../../../cli/CommandInfo.js';
1514
import { cli } from '../../../../cli/cli.js';
15+
import { spe } from '../../../../utils/spe.js';
1616

1717
describe(commands.CONTAINER_LIST, () => {
1818
let log: string[];
@@ -34,40 +34,12 @@ describe(commands.CONTAINER_LIST, () => {
3434
"createdDateTime": "2021-11-24T15:41:52.347Z"
3535
}];
3636

37-
const containerTypedata = [{
38-
"AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)",
39-
"ContainerTypeId": "/Guid(e2756c4d-fa33-4452-9c36-2325686e1082)",
40-
"CreationDate": "3/11/2024 2:38:56 PM",
41-
"DisplayName": "standard container",
42-
"ExpiryDate": "3/11/2028 2:38:56 PM",
43-
"IsBillingProfileRequired": true,
44-
"OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)",
45-
"OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)",
46-
"Region": "West Europe",
47-
"ResourceGroup": "Standard group",
48-
"SPContainerTypeBillingClassification": "Standard"
49-
},
50-
{
51-
"AzureSubscriptionId": "/Guid(f08575e2-36c4-407f-a891-eabae23f66bc)",
52-
"ContainerTypeId": "/Guid(e2756c4d-fa33-4452-9c36-2325686e1082)",
53-
"CreationDate": "3/11/2024 2:38:56 PM",
54-
"DisplayName": "trial container",
55-
"ExpiryDate": "3/11/2028 2:38:56 PM",
56-
"IsBillingProfileRequired": true,
57-
"OwningAppId": "/Guid(1b3b8660-9a44-4a7c-9c02-657f3ff5d5ac)",
58-
"OwningTenantId": "/Guid(e1dd4023-a656-480a-8a0e-c1b1eec51e1d)",
59-
"Region": "West Europe",
60-
"ResourceGroup": "Standard group",
61-
"SPContainerTypeBillingClassification": "Standard"
62-
}];
63-
6437
before(() => {
6538
sinon.stub(auth, 'restoreAuth').resolves();
6639
sinon.stub(telemetry, 'trackEvent').resolves();
6740
sinon.stub(pid, 'getProcessName').returns('');
6841
sinon.stub(session, 'getId').returns('');
69-
sinon.stub(spo, 'getSpoAdminUrl').resolves(adminUrl);
70-
sinon.stub(spo, 'ensureFormDigest').resolves({ FormDigestValue: 'abc', FormDigestTimeoutSeconds: 1800, FormDigestExpiresAt: new Date(), WebFullUrl: 'https://contoso.sharepoint.com' });
42+
7143
auth.connection.active = true;
7244
auth.connection.spoUrl = 'https://contoso.sharepoint.com';
7345
commandInfo = cli.getCommandInfo(command);
@@ -87,14 +59,15 @@ describe(commands.CONTAINER_LIST, () => {
8759
}
8860
};
8961
loggerLogSpy = sinon.spy(logger, 'log');
62+
63+
sinon.stub(spe, 'getContainerTypeIdByName').withArgs(adminUrl, 'standard container').resolves('e2756c4d-fa33-4452-9c36-2325686e1082');
9064
});
9165

9266
afterEach(() => {
9367
sinonUtil.restore([
9468
request.get,
9569
request.post,
96-
spo.getSpoAdminUrl,
97-
spo.getAllContainerTypes
70+
spe.getContainerTypeIdByName
9871
]);
9972
});
10073

@@ -135,13 +108,11 @@ describe(commands.CONTAINER_LIST, () => {
135108
throw 'Invalid request';
136109
});
137110

138-
await command.action(logger, { options: { containerTypeId: "e2756c4d-fa33-4452-9c36-2325686e1082", debug: true } });
111+
await command.action(logger, { options: { containerTypeId: "e2756c4d-fa33-4452-9c36-2325686e1082", verbose: true } });
139112
assert(loggerLogSpy.calledWith(containersList));
140113
});
141114

142115
it('retrieves list of container type by name', async () => {
143-
sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypedata);
144-
145116
sinon.stub(request, 'get').callsFake(async (opts) => {
146117
if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq e2756c4d-fa33-4452-9c36-2325686e1082') {
147118
return { "value": containersList };
@@ -150,32 +121,19 @@ describe(commands.CONTAINER_LIST, () => {
150121
throw 'Invalid request';
151122
});
152123

153-
await command.action(logger, { options: { containerTypeName: "standard container", debug: true } });
124+
await command.action(logger, { options: { containerTypeName: "standard container", verbose: true } });
154125
assert(loggerLogSpy.calledWith(containersList));
155126
});
156127

157-
it('throws an error when service principal is not found', async () => {
158-
sinon.stub(request, 'get').callsFake(async (opts) => {
159-
if (opts.url === 'https://graph.microsoft.com/v1.0/storage/fileStorage/containers?$filter=containerTypeId eq e2756c4d-fa33-4452-9c36-2325686e1086') {
160-
return [];
161-
}
162-
163-
throw 'Invalid request';
164-
});
165-
166-
sinon.stub(spo, 'getAllContainerTypes').resolves(containerTypedata);
167-
168-
await assert.rejects(command.action(logger, { options: { containerTypeName: "nonexisting container", debug: true } }),
169-
new CommandError(`Container type with name nonexisting container not found`));
170-
});
171-
172128
it('correctly handles error when retrieving containers', async () => {
173129
const error = 'An error has occurred';
174-
sinon.stub(spo, 'getAllContainerTypes').rejects(new Error(error));
130+
sinonUtil.restore(spe.getContainerTypeIdByName);
131+
sinon.stub(spe, 'getContainerTypeIdByName').rejects(new Error(error));
175132

176133
await assert.rejects(command.action(logger, {
177134
options: {
178-
debug: true
135+
containerTypeName: "nonexisting container",
136+
verbose: true
179137
}
180138
}), new CommandError('An error has occurred'));
181139
});

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

+3-12
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { validation } from '../../../../utils/validation.js';
66
import GraphCommand from '../../../base/GraphCommand.js';
77
import commands from '../../commands.js';
88
import { ContainerProperties } from '../../ContainerProperties.js';
9-
import { ContainerTypeProperties, spo } from '../../../../utils/spo.js';
9+
import { spe } from '../../../../utils/spe.js';
10+
import { spo } from '../../../../utils/spo.js';
1011

1112
interface CommandArgs {
1213
options: Options;
@@ -101,17 +102,7 @@ class SpeContainerListCommand extends GraphCommand {
101102
}
102103

103104
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug);
104-
const containerTypes: ContainerTypeProperties[] = await spo.getAllContainerTypes(spoAdminUrl, logger, this.debug);
105-
106-
// Get id of the container type by name
107-
const containerType: ContainerTypeProperties | undefined = containerTypes.find(c => c.DisplayName === options.containerTypeName);
108-
if (!containerType) {
109-
throw new Error(`Container type with name ${options.containerTypeName} not found`);
110-
}
111-
112-
// The value is returned as "/Guid(073269af-f1d2-042d-2ef5-5bdd6ac83115)/". We need to extract the GUID from it.
113-
const containerTypeValue = containerType.ContainerTypeId.toString();
114-
return containerTypeValue.substring(containerTypeValue.indexOf('(') + 1, containerTypeValue.lastIndexOf(')'));
105+
return spe.getContainerTypeIdByName(spoAdminUrl, options.containerTypeName!);
115106
}
116107
}
117108

0 commit comments

Comments
 (0)