Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion docs/docs/cmd/spe/container/container-get.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ m365 spe container get [options]

`-n, --name [name]`
: Display name of the container. Specify either `id` or `name` but not both.

`--containerTypeId [containerTypeId]`
: The container type ID of the container instance. Specify either `containerTypeId` or `containerTypeName` when using `name` but not both.

`--containerTypeName [containerTypeName]`
: The container type name of the container instance. Specify either `containerTypeId` or `containerTypeName` when using `name` but not both.
```

<Global />
Expand Down Expand Up @@ -54,7 +60,13 @@ m365 spe container get --id "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDE
Gets a container of a specific type by display name.

```sh
m365 spe container get --name "My Application Storage Container"
m365 spe container get --name "My Application Storage Container" --containerTypeId "91710488-5756-407f-9046-fbe5f0b4de73"
```

Gets container by using its name and container type name.

```sh
m365 spe container get --name "Invoices" --containerTypeName "My container type name"
```

## Response
Expand Down
27 changes: 24 additions & 3 deletions docs/docs/cmd/spe/container/container-permission-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ m365 spe container permission list [options]
## Options

```md definition-list
`-i, --containerId <id>`
: The ID of the SharePoint Embedded Container.
`-i, --containerId [id]`
: The ID of the SharePoint Embedded Container. Specify either `containerId` or `containerName` but not both.

`-n, --containerName [containerName]`
: Display name of the SharePoint Embedded Container. Specify either `containerId` or `containerName` but not both.

`--containerTypeId [containerTypeId]`
: The container type ID of the container instance. Specify either `containerTypeId` or `containerTypeName` when using `containerName` but not both.

`--containerTypeName [containerTypeName]`
: The container type name of the container instance. Specify either `containerTypeId` or `containerTypeName` when using `containerName` but not both.
```

<Global />
Expand All @@ -42,12 +51,24 @@ m365 spe container permission list [options]

## Examples

Lists Container permissions.
Lists Container permissions by id.

```sh
m365 spe container permission list --containerId "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z"
```

Lists Container permissions by display name.

```sh
m365 spe container permission list --containerName "My Application Storage Container" --containerTypeId "91710488-5756-407f-9046-fbe5f0b4de73"
```

Lists Container permissions by display name and container type name.

```sh
m365 spe container permission list --containerName "My Application Storage Container" --containerTypeName "My container type name"
```

## Response

<Tabs>
Expand Down
213 changes: 193 additions & 20 deletions src/m365/spe/commands/container/container-permission-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@ import sinon from 'sinon';
import auth from '../../../../Auth.js';
import { Logger } from '../../../../cli/Logger.js';
import { CommandError } from '../../../../Command.js';
import request from '../../../../request.js';
import { telemetry } from '../../../../telemetry.js';
import { pid } from '../../../../utils/pid.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './container-permission-list.js';
import { formatting } from '../../../../utils/formatting.js';
import { z } from 'zod';
import { spe } from '../../../../utils/spe.js';
import { odata } from '../../../../utils/odata.js';
import { cli } from '../../../../cli/cli.js';

describe(commands.CONTAINER_PERMISSION_LIST, () => {
let log: string[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let loggerLogToStderrSpy: sinon.SinonSpy;
let schema: z.ZodTypeAny;

const containerId = "b!ISJs1WRro0y0EWgkUYcktDa0mE8zSlFEqFzqRn70Zwp1CEtDEBZgQICPkRbil_5Z";
const containerName = 'My Application Storage Container';
const containerTypeId = 'b2e2cef4-9ac1-4b3b-b4a5-2a2e3a2e2a2e';
const containerPermissionResponse = {
"value": [
{
Expand Down Expand Up @@ -66,6 +72,7 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {
sinon.stub(telemetry, 'trackEvent').resolves();
sinon.stub(pid, 'getProcessName').returns('');
auth.connection.active = true;
schema = command.getSchemaToParse()!;
});

beforeEach(() => {
Expand All @@ -82,12 +89,18 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {
}
};
loggerLogSpy = sinon.spy(logger, 'log');
loggerLogToStderrSpy = sinon.spy(logger, 'logToStderr');
});

afterEach(() => {
sinonUtil.restore([
request.get
spe.getContainerIdByName,
spe.getContainerTypeIdByName,
odata.getAllItems,
cli.handleMultipleResultsFound
]);
loggerLogSpy.restore();
loggerLogToStderrSpy.restore();
});

after(() => {
Expand All @@ -108,13 +121,7 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {
});

it('correctly lists permissions of a SharePoint Embedded Container', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/storage/fileStorage/containers/${formatting.encodeQueryParameter(containerId)}/permissions`) {
return containerPermissionResponse;
}

throw 'Invalid request';
});
sinon.stub(odata, 'getAllItems').resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
Expand All @@ -127,13 +134,7 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {
});

it('correctly lists permissions of a SharePoint Embedded Container (TEXT)', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/storage/fileStorage/containers/${formatting.encodeQueryParameter(containerId)}/permissions`) {
return containerPermissionResponse;
}

throw 'Invalid request';
});
sinon.stub(odata, 'getAllItems').resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
Expand All @@ -146,8 +147,114 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {
assert(loggerLogSpy.calledWith(textOutput));
});

it('correctly lists permissions of a SharePoint Embedded Container by name', async () => {
sinon.stub(odata, 'getAllItems').onFirstCall().resolves([
{
id: containerId,
displayName: containerName
}
]).onSecondCall().resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
containerName,
containerTypeId,
debug: true
}
});

assert(loggerLogSpy.calledWith(containerPermissionResponse.value));
});

it('logs progress when resolving container id by name in verbose mode', async () => {
sinon.stub(odata, 'getAllItems').onFirstCall().resolves([
{
id: containerId,
displayName: containerName
}
]).onSecondCall().resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
containerName,
containerTypeId,
verbose: true
}
});

assert(loggerLogToStderrSpy.calledWith(`Resolving container id from name '${containerName}'...`));
});

it('fails when container with specified name does not exist', async () => {
sinon.stub(odata, 'getAllItems').resolves([]);

await assert.rejects(
command.action(logger, {
options: {
containerName,
containerTypeId
}
}),
new CommandError(`The specified container '${containerName}' does not exist.`)
);
});

it('handles multiple containers with same name when resolving id', async () => {
sinon.stub(odata, 'getAllItems').onFirstCall().resolves([
{
id: '1',
displayName: containerName
},
{
id: containerId,
displayName: containerName
}
]).onSecondCall().resolves(containerPermissionResponse.value);
sinon.stub(cli, 'handleMultipleResultsFound').resolves({
id: containerId
});

await command.action(logger, {
options: {
containerName,
containerTypeId
}
});

assert(loggerLogSpy.calledWith(containerPermissionResponse.value));
});

it('rethrows unexpected errors when resolving container id by name', async () => {
sinon.stub(odata, 'getAllItems').rejects({
error: {
'odata.error': {
message: {
value: 'unexpected error'
}
}
}
});

await assert.rejects(command.action(logger, {
options: {
containerName,
containerTypeId
}
}), new CommandError('unexpected error'));
});

it('rethrows CommandError thrown during command execution', async () => {
sinon.stub(odata, 'getAllItems').rejects(new CommandError('command error'));

await assert.rejects(command.action(logger, {
options: {
containerId
}
}), new CommandError('command error'));
});

it('correctly handles error when SharePoint Embedded Container is not found', async () => {
sinon.stub(request, 'get').rejects({
sinon.stub(odata, 'getAllItems').rejects({
error: { 'odata.error': { message: { value: 'Item Not Found.' } } }
});

Expand All @@ -157,12 +264,78 @@ describe(commands.CONTAINER_PERMISSION_LIST, () => {

it('correctly handles error when retrieving permissions of a SharePoint Embedded Container', async () => {
const error = 'An error has occurred';
sinon.stub(request, 'get').rejects(new Error(error));
sinon.stub(odata, 'getAllItems').rejects(new Error(error));

await assert.rejects(command.action(logger, {
options: {
containerId: containerId
}
}), new CommandError(error));
});
});

it('fails validation when neither containerId nor containerName is specified', () => {
const result = schema.safeParse({});
assert.strictEqual(result.success, false);
assert(result.error?.issues.some(issue => issue.message.includes('Specify either id or name')));
});

it('fails validation when both containerId and containerName are specified', () => {
const result = schema.safeParse({
containerId,
containerName
});
assert.strictEqual(result.success, false);
assert(result.error?.issues.some(issue => issue.message.includes('Specify either id or name')));
});

it('passes validation when only containerId is specified', () => {
const result = schema.safeParse({ containerId });
assert.strictEqual(result.success, true);
});

it('passes validation when containerName and containerTypeId are specified', () => {
const result = schema.safeParse({ containerName, containerTypeId });
assert.strictEqual(result.success, true);
});

it('correctly lists permissions of a SharePoint Embedded Container by containerTypeName', async () => {
const containerTypeName = 'My Container Type';
sinon.stub(spe, 'getContainerTypeIdByName').resolves(containerTypeId);
sinon.stub(odata, 'getAllItems').onFirstCall().resolves([
{
id: containerId,
displayName: containerName
}
]).onSecondCall().resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
containerName,
containerTypeName
}
});

assert(loggerLogSpy.calledWith(containerPermissionResponse.value));
});

it('logs progress when getting container type by name in verbose mode', async () => {
const containerTypeName = 'My Container Type';
sinon.stub(spe, 'getContainerTypeIdByName').resolves(containerTypeId);
sinon.stub(odata, 'getAllItems').onFirstCall().resolves([
{
id: containerId,
displayName: containerName
}
]).onSecondCall().resolves(containerPermissionResponse.value);

await command.action(logger, {
options: {
containerName,
containerTypeName,
verbose: true
}
});

assert(loggerLogToStderrSpy.calledWith(`Getting container type with name '${containerTypeName}'...`));
});
});
Loading
Loading