Skip to content
Closed
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
17 changes: 17 additions & 0 deletions docs/docs/cmd/onenote/notebook/notebook-add.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ m365 onenote notebook add [options]

<Global />

## Permissions

<Tabs>
<TabItem value="Delegated">

| Resource | Permissions |
|-----------------|------------------------------------------------------------------|
| Microsoft Graph | Notes.Create, Sites.Read.All, User.ReadBasic.All, Group.Read.All |

</TabItem>
<TabItem value="Application">

This command does not support application permissions.

</TabItem>
</Tabs>

## Examples

Create a Microsoft OneNote notebook for the currently logged in user
Expand Down
17 changes: 17 additions & 0 deletions docs/docs/cmd/onenote/notebook/notebook-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@ m365 onenote notebook list [options]

<Global />

## Permissions

<Tabs>
<TabItem value="Delegated">

| Resource | Permissions |
|-----------------|--------------------------------------------------------------------|
| Microsoft Graph | Notes.Read.All, Sites.Read.All, User.ReadBasic.All, Group.Read.All |

</TabItem>
<TabItem value="Application">

This command does not support application permissions.

</TabItem>
</Tabs>

## Examples

List Microsoft OneNote notebooks for the currently logged in user
Expand Down
19 changes: 18 additions & 1 deletion docs/docs/cmd/onenote/page/page-list.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,24 @@ m365 onenote page list [options]

## Remarks

When we don't specify either `userId`, `userName`, `groupId`, `groupName` or `webUrl`, the OneNote pages will be retrieved of the currently logged in user.
When you don't specify either `userId`, `userName`, `groupId`, `groupName` or `webUrl`, the OneNote pages will be retrieved of the currently logged in user.

## Permissions

<Tabs>
<TabItem value="Delegated">

| Resource | Permissions |
|-----------------|--------------------------------------------------------------------|
| Microsoft Graph | Notes.Read.All, Sites.Read.All, User.ReadBasic.All, Group.Read.All |

</TabItem>
<TabItem value="Application">

This command does not support application permissions.

</TabItem>
</Tabs>

## Examples

Expand Down
16 changes: 14 additions & 2 deletions src/m365/onenote/commands/notebook/notebook-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import commands from '../../commands.js';
import command from './notebook-add.js';
import { entraGroup } from '../../../../utils/entraGroup.js';
import { spo } from '../../../../utils/spo.js';
import { accessToken } from '../../../../utils/accessToken.js';
import { formatting } from '../../../../utils/formatting.js';

describe(commands.NOTEBOOK_ADD, () => {
const name = 'My Notebook';
Expand Down Expand Up @@ -54,6 +56,7 @@ describe(commands.NOTEBOOK_ADD, () => {
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let accessTokenStub: sinon.SinonStub;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
Expand All @@ -78,11 +81,13 @@ describe(commands.NOTEBOOK_ADD, () => {
}
};
loggerLogSpy = sinon.spy(logger, 'log');
accessTokenStub = sinon.stub(accessToken, 'assertAccessTokenType').resolves();
});

afterEach(() => {
sinonUtil.restore([
request.post
request.post,
accessToken.assertAccessTokenType
]);
});

Expand Down Expand Up @@ -130,6 +135,13 @@ describe(commands.NOTEBOOK_ADD, () => {
assert.strictEqual(actual, true);
});

it('enforces the user to use delegated permissions', async () => {
sinon.stub(request, 'post').resolves();

await command.action(logger, { options: {} });
assert(accessTokenStub.calledOnceWithExactly('delegated'));
});

it('adds notebook for the currently logged in user', async () => {
sinon.stub(request, 'post').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/me/onenote/notebooks`) {
Expand Down Expand Up @@ -209,7 +221,7 @@ describe(commands.NOTEBOOK_ADD, () => {
const userName = '[email protected]';

sinon.stub(request, 'post').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/users/${userName}/onenote/notebooks`) {
if (opts.url === `https://graph.microsoft.com/v1.0/users/${formatting.encodeQueryParameter(userName)}/onenote/notebooks`) {
return addResponse;
}

Expand Down
7 changes: 4 additions & 3 deletions src/m365/onenote/commands/notebook/notebook-add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import GlobalOptions from '../../../../GlobalOptions.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { entraGroup } from '../../../../utils/entraGroup.js';
import { validation } from '../../../../utils/validation.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
import { spo } from '../../../../utils/spo.js';
import GraphDelegatedCommand from '../../../base/GraphDelegatedCommand.js';
import { formatting } from '../../../../utils/formatting.js';

interface CommandArgs {
options: Options;
Expand All @@ -20,7 +21,7 @@ interface Options extends GlobalOptions {
webUrl?: string;
}

class OneNoteNotebookAddCommand extends GraphCommand {
class OneNoteNotebookAddCommand extends GraphDelegatedCommand {
public get name(): string {
return commands.NOTEBOOK_ADD;
}
Expand Down Expand Up @@ -146,7 +147,7 @@ class OneNoteNotebookAddCommand extends GraphCommand {
endpoint += `users/${args.options.userId}`;
}
else if (args.options.userName) {
endpoint += `users/${args.options.userName}`;
endpoint += `users/${formatting.encodeQueryParameter(args.options.userName)}`;
}
else if (args.options.groupId) {
endpoint += `groups/${args.options.groupId}`;
Expand Down
17 changes: 14 additions & 3 deletions src/m365/onenote/commands/notebook/notebook-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './notebook-list.js';
import { accessToken } from '../../../../utils/accessToken.js';
import { formatting } from '../../../../utils/formatting.js';

describe(commands.NOTEBOOK_LIST, () => {
let log: string[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let accessTokenStub: sinon.SinonStub;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
Expand All @@ -42,12 +45,13 @@ describe(commands.NOTEBOOK_LIST, () => {
}
};
loggerLogSpy = sinon.spy(logger, 'log');
(command as any).items = [];
accessTokenStub = sinon.stub(accessToken, 'assertAccessTokenType').resolves();
});

afterEach(() => {
sinonUtil.restore([
request.get
request.get,
accessToken.assertAccessTokenType
]);
});

Expand Down Expand Up @@ -93,6 +97,13 @@ describe(commands.NOTEBOOK_LIST, () => {
assert.strictEqual(actual, true);
});

it('enforces the user to use delegated permissions', async () => {
sinon.stub(request, 'get').resolves([]);

await command.action(logger, { options: {} });
assert(accessTokenStub.calledOnceWithExactly('delegated'));
});

it('lists Microsoft OneNote notebooks for the currently logged in user (debug)', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/me/onenote/notebooks`) {
Expand Down Expand Up @@ -351,7 +362,7 @@ describe(commands.NOTEBOOK_LIST, () => {

it('lists Microsoft OneNote notebooks for user by name', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/users/[email protected]/onenote/notebooks`) {
if (opts.url === `https://graph.microsoft.com/v1.0/users/${formatting.encodeQueryParameter('[email protected]')}/onenote/notebooks`) {
return {
"value": [
{
Expand Down
25 changes: 6 additions & 19 deletions src/m365/onenote/commands/notebook/notebook-list.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Notebook } from '@microsoft/microsoft-graph-types';
import { Logger } from '../../../../cli/Logger.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { entraGroup } from '../../../../utils/entraGroup.js';
import { odata } from '../../../../utils/odata.js';
import { validation } from '../../../../utils/validation.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';
import { formatting } from '../../../../utils/formatting.js';
import GraphDelegatedCommand from '../../../base/GraphDelegatedCommand.js';
import { spo } from '../../../../utils/spo.js';

interface CommandArgs {
options: Options;
Expand All @@ -20,7 +21,7 @@ interface Options extends GlobalOptions {
webUrl?: string;
}

class OneNoteNotebookListCommand extends GraphCommand {
class OneNoteNotebookListCommand extends GraphDelegatedCommand {
public get name(): string {
return commands.NOTEBOOK_LIST;
}
Expand Down Expand Up @@ -101,7 +102,7 @@ class OneNoteNotebookListCommand extends GraphCommand {
endpoint += `users/${args.options.userId}`;
}
else if (args.options.userName) {
endpoint += `users/${args.options.userName}`;
endpoint += `users/${formatting.encodeQueryParameter(args.options.userName)}`;
}
else if (args.options.groupId) {
endpoint += `groups/${args.options.groupId}`;
Expand All @@ -111,7 +112,7 @@ class OneNoteNotebookListCommand extends GraphCommand {
endpoint += `groups/${groupId}`;
}
else if (args.options.webUrl) {
const siteId = await this.getSpoSiteId(args.options.webUrl);
const siteId = await spo.getSpoGraphSiteId(args.options.webUrl);
endpoint += `sites/${siteId}`;
}
else {
Expand All @@ -126,20 +127,6 @@ class OneNoteNotebookListCommand extends GraphCommand {
return group.id!;
}

private async getSpoSiteId(webUrl: string): Promise<string> {
const url = new URL(webUrl);
const requestOptions: CliRequestOptions = {
url: `${this.resource}/v1.0/sites/${url.hostname}:${url.pathname}`,
headers: {
accept: 'application/json;odata.metadata=none'
},
responseType: 'json'
};

const site = await request.get<{ id: string }>(requestOptions);
return site.id;
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
try {
const endpoint = await this.getEndpointUrl(args);
Expand Down
40 changes: 18 additions & 22 deletions src/m365/onenote/commands/page/page-list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './page-list.js';
import { accessToken } from '../../../../utils/accessToken.js';
import { settingsNames } from '../../../../settingsNames.js';

describe(commands.PAGE_LIST, () => {
Expand Down Expand Up @@ -76,6 +77,7 @@ describe(commands.PAGE_LIST, () => {
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let accessTokenStub: sinon.SinonStub;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
Expand All @@ -84,13 +86,7 @@ describe(commands.PAGE_LIST, () => {
sinon.stub(session, 'getId').returns('');
auth.connection.active = true;
commandInfo = cli.getCommandInfo(command);
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => {
if (settingName === 'prompt') {
return false;
}

return defaultValue;
});
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName: string, defaultValue: any) => settingName === settingsNames.prompt ? false : defaultValue);
});

beforeEach(() => {
Expand All @@ -107,14 +103,14 @@ describe(commands.PAGE_LIST, () => {
}
};
loggerLogSpy = sinon.spy(logger, 'log');
(command as any).items = [];
accessTokenStub = sinon.stub(accessToken, 'assertAccessTokenType').resolves();
});

afterEach(() => {
sinonUtil.restore([
request.get,
odata.getAllItems,
cli.getSettingWithDefaultValue
accessToken.assertAccessTokenType
]);
});

Expand Down Expand Up @@ -165,6 +161,13 @@ describe(commands.PAGE_LIST, () => {
assert.strictEqual(actual, true);
});

it('enforces the user to use delegated permissions', async () => {
sinon.stub(odata, 'getAllItems').resolves([]);

await command.action(logger, { options: {} });
assert(accessTokenStub.calledOnceWithExactly('delegated'));
});

it('lists Microsoft OneNote pages for the currently logged in user', async () => {
sinon.stub(odata, 'getAllItems').callsFake(async (url: string) => {
if (url === `https://graph.microsoft.com/v1.0/me/onenote/pages`) {
Expand All @@ -191,7 +194,7 @@ describe(commands.PAGE_LIST, () => {

it('lists Microsoft OneNote pages for user by name', async () => {
sinon.stub(odata, 'getAllItems').callsFake(async (url: string) => {
if (url === `https://graph.microsoft.com/v1.0/users/${userName}/onenote/pages`) {
if (url === `https://graph.microsoft.com/v1.0/users/${formatting.encodeQueryParameter(userName)}/onenote/pages`) {
return pageResponse.value;
}
throw 'Invalid request';
Expand All @@ -215,7 +218,7 @@ describe(commands.PAGE_LIST, () => {

it('lists Microsoft OneNote pages in group by name', async () => {
sinon.stub(odata, 'getAllItems').callsFake(async (url: string) => {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'`) {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'&$select=id`) {
return [{
"id": groupId,
"description": groupName,
Expand Down Expand Up @@ -277,7 +280,7 @@ describe(commands.PAGE_LIST, () => {

it('throws error if group by displayName returns no results', async () => {
sinon.stub(odata, 'getAllItems').callsFake(async (url: string) => {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'`) {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'&$select=id`) {
return [];
}
throw 'Invalid request';
Expand All @@ -287,17 +290,9 @@ describe(commands.PAGE_LIST, () => {
});

it('throws an error if group by displayName returns multiple results', async () => {
sinon.stub(cli, 'getSettingWithDefaultValue').callsFake((settingName, defaultValue) => {
if (settingName === settingsNames.prompt) {
return false;
}

return defaultValue;
});

const duplicateGroupId = '9f3c2c36-1682-4922-9ae1-f57d2caf0de1';
sinon.stub(odata, 'getAllItems').callsFake(async (url: string) => {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'`) {
if (url === `https://graph.microsoft.com/v1.0/groups?$filter=displayName eq '${formatting.encodeQueryParameter(groupName)}'&$select=id`) {
return [{
"id": groupId,
"description": groupName,
Expand All @@ -311,6 +306,7 @@ describe(commands.PAGE_LIST, () => {
throw 'Invalid request';
});

await assert.rejects(command.action(logger, { options: { groupName: groupName } } as any), new CommandError("Multiple groups with name 'Dummy Group A' found. Found: bba4c915-0ac8-47a1-bd05-087a44c92d3b, 9f3c2c36-1682-4922-9ae1-f57d2caf0de1."));
await assert.rejects(command.action(logger, { options: { groupName: groupName } }),
new CommandError("Multiple groups with name 'Dummy Group A' found. Found: bba4c915-0ac8-47a1-bd05-087a44c92d3b, 9f3c2c36-1682-4922-9ae1-f57d2caf0de1."));
});
});
Loading