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
106 changes: 106 additions & 0 deletions docs/docs/cmd/spo/web/web-alert-get.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import Global from '../../_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# spo web alert get

Retrieves details of a specific alert from a SharePoint site list

## Usage

```sh
m365 spo web alert get [options]
```

## Options

```md definition-list
`-u, --webUrl <webUrl>`
: The URL of the SharePoint site.

`--id <id>`
: The ID of the alert.
```

<Global />

## Permissions

<Tabs>
<TabItem value="Delegated">

| Resource | Permissions |
|------------|---------------|
| SharePoint | AllSites.Read |

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

| Resource | Permissions |
|------------|----------------|
| SharePoint | Sites.Read.All |

</TabItem>
</Tabs>

## Examples

Retrieve an alert by ID

```sh
m365 spo web alert get --webUrl https://contoso.sharepoint.com/sites/Marketing --id 7cbb4c8d-8e4d-4d2e-9c6f-3f1d8b2e6a0e
```

## Response

<Tabs>
<TabItem value="JSON">

```json
{
"AlertFrequency": 0,
"AlertTemplateName": "SPAlertTemplateType.DocumentLibrary",
"AlertType": 0,
"AlwaysNotify": false,
"DeliveryChannels": 1,
"EventType": -1,
"Filter": "",
"ID": "7cbb4c8d-8e4d-4d2e-9c6f-3f1d8b2e6a0e",
"Status": 0,
"Title": "Marketing documents",
"UserId": 8,
"List": {
"Id": "39d9e102-9e8f-4e74-8f17-84a92f972fcf",
"Title": "Documents",
"RootFolder": {
"ServerRelativeUrl": "/sites/Marketing/Shared Documents"
}
},
"User": {
"Id": 8,
"UserPrincipalName": "jane.doe@contoso.com"
}
}
```

</TabItem>
<TabItem value="Text">

```text
AlertFrequency : 0
AlertTemplateName: SPAlertTemplateType.DocumentLibrary
AlertType : 0
AlwaysNotify : false
DeliveryChannels : 1
EventType : -1
Filter :
ID : 7cbb4c8d-8e4d-4d2e-9c6f-3f1d8b2e6a0e
Status : 0
Title : Marketing documents
UserId : 8
List : {"Id":"39d9e102-9e8f-4e74-8f17-84a92f972fcf","Title":"Documents","RootFolder":{"ServerRelativeUrl":"/sites/Marketing/Shared Documents"}}
User : {"Id":8,"UserPrincipalName":"jane.doe@contoso.com"}
```

</TabItem>
</Tabs>
7 changes: 6 additions & 1 deletion docs/src/config/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4280,6 +4280,11 @@ const sidebars: SidebarsConfig = {
label: 'web set',
id: 'cmd/spo/web/web-set'
},
{
type: 'doc',
label: 'web alert get',
id: 'cmd/spo/web/web-alert-get'
},
{
type: 'doc',
label: 'web alert list',
Expand Down Expand Up @@ -5143,4 +5148,4 @@ const sidebars: SidebarsConfig = {
]
};

export default sidebars;
export default sidebars;
3 changes: 2 additions & 1 deletion src/m365/spo/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ export default {
USERPROFILE_GET: `${prefix} userprofile get`,
USERPROFILE_SET: `${prefix} userprofile set`,
WEB_ADD: `${prefix} web add`,
WEB_ALERT_GET: `${prefix} web alert get`,
WEB_ALERT_LIST: `${prefix} web alert list`,
WEB_ALERT_REMOVE: `${prefix} web alert remove`,
WEB_CLIENTSIDEWEBPART_LIST: `${prefix} web clientsidewebpart list`,
Expand All @@ -377,4 +378,4 @@ export default {
WEB_ROLEINHERITANCE_BREAK: `${prefix} web roleinheritance break`,
WEB_ROLEINHERITANCE_RESET: `${prefix} web roleinheritance reset`,
WEB_SET: `${prefix} web set`
};
};
156 changes: 156 additions & 0 deletions src/m365/spo/commands/web/web-alert-get.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import assert from 'assert';
import sinon from 'sinon';
import auth from '../../../../Auth.js';
import { cli } from '../../../../cli/cli.js';
import { CommandInfo } from '../../../../cli/CommandInfo.js';
import { Logger } from '../../../../cli/Logger.js';
import { CommandError } from '../../../../Command.js';
import request from '../../../../request.js';
import { telemetry } from '../../../../telemetry.js';
import { formatting } from '../../../../utils/formatting.js';
import { pid } from '../../../../utils/pid.js';
import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command, { options } from './web-alert-get.js';

describe(commands.WEB_ALERT_GET, () => {
let log: any[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;
let loggerLogToStderrSpy: sinon.SinonSpy;
let commandInfo: CommandInfo;
let commandOptionsSchema: typeof options;

const webUrl = 'https://contoso.sharepoint.com/sites/marketing';
const alertId = '39d9e102-9e8f-4e74-8f17-84a92f972fcf';
const alertResponse = {
AlertFrequency: 0,
AlertTemplateName: 'SPAlertTemplateType.DocumentLibrary',
AlertType: 0,
AlwaysNotify: false,
DeliveryChannels: 1,
EventType: -1,
Filter: '',
ID: alertId,
Properties: [
{
Key: 'webUrl',
Value: 'https://contoso.sharepoint.com',
ValueType: 'Edm.String'
}
],
Status: 0,
Title: 'Marketing documents',
UserId: 8,
List: {
Id: '7cbb4c8d-8e4d-4d2e-9c6f-3f1d8b2e6a0e',
Title: 'Documents',
RootFolder: {
ServerRelativeUrl: '/sites/marketing/Shared Documents'
}
},
User: {
Id: 8,
UserPrincipalName: 'jane.doe@contoso.com'
}
};

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
sinon.stub(telemetry, 'trackEvent').resolves();
sinon.stub(pid, 'getProcessName').returns('');
sinon.stub(session, 'getId').returns('');
commandInfo = cli.getCommandInfo(command);
commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options;
auth.connection.active = true;
});

beforeEach(() => {
log = [];
logger = {
log: async (msg: string) => {
log.push(msg);
},
logRaw: async (msg: string) => {
log.push(msg);
},
logToStderr: async (msg: string) => {
log.push(msg);
}
};
loggerLogSpy = sinon.spy(logger, 'log');
loggerLogToStderrSpy = sinon.spy(logger, 'logToStderr');
});

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

after(() => {
sinon.restore();
auth.connection.active = false;
});

it('has correct name', () => {
assert.strictEqual(command.name, commands.WEB_ALERT_GET);
});

it('has a description', () => {
assert.notStrictEqual(command.description, null);
});

it('fails validation if webUrl is not a valid URL', async () => {
const actual = commandOptionsSchema.safeParse({ webUrl: 'foo', id: alertId });
assert.strictEqual(actual.success, false);
});

it('fails validation if alertId is not a valid GUID', async () => {
const actual = commandOptionsSchema.safeParse({ webUrl, id: 'invalid' });
assert.strictEqual(actual.success, false);
});

it('passes validation when valid webUrl and alertId are provided', async () => {
const actual = commandOptionsSchema.safeParse({ webUrl, id: alertId });
assert.strictEqual(actual.success, true);
});

it('retrieves an alert by id', async () => {
sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `${webUrl}/_api/Web/Alerts/GetById('${formatting.encodeQueryParameter(alertId)}')?$expand=List,User,List/Rootfolder&$select=*,List/Id,List/Title,List/Rootfolder/ServerRelativeUrl`) {
return alertResponse;
}

throw new Error(`Invalid request: ${opts.url}`);
});

await command.action(logger, { options: { webUrl, id: alertId, verbose: true } });
assert(loggerLogSpy.calledWith(alertResponse));
});

it('logs verbose output to stderr', async () => {
sinon.stub(request, 'get').resolves(alertResponse);

await command.action(logger, { options: { webUrl, id: alertId, verbose: true } });
assert(loggerLogToStderrSpy.calledWith(`Retrieving alert with id '${alertId}' from site '${webUrl}'...`));
});

it('handles error correctly', async () => {
const error = {
error: {
'odata.error': {
code: '-2146232832, Microsoft.SharePoint.SPException',
message: {
value: 'The alert you are trying to access does not exist or has just been deleted.'
}
}
}
};
sinon.stub(request, 'get').rejects(error);

await assert.rejects(command.action(logger, { options: { webUrl, id: alertId } }),
new CommandError(error.error['odata.error'].message.value));
});
});
61 changes: 61 additions & 0 deletions src/m365/spo/commands/web/web-alert-get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { z } from 'zod';
import { Logger } from '../../../../cli/Logger.js';
import { globalOptionsZod } from '../../../../Command.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { formatting } from '../../../../utils/formatting.js';
import { validation } from '../../../../utils/validation.js';
import SpoCommand from '../../../base/SpoCommand.js';
import commands from '../../commands.js';

export const options = z.strictObject({
...globalOptionsZod.shape,
webUrl: z.string().alias('u')
.refine(url => validation.isValidSharePointUrl(url) === true, {
error: e => `'${e.input}' is not a valid SharePoint URL.`
}),
id: z.uuid()
});

declare type Options = z.infer<typeof options>;

interface CommandArgs {
options: Options;
}

class SpoWebAlertGetCommand extends SpoCommand {
public get name(): string {
return commands.WEB_ALERT_GET;
}

public get description(): string {
return 'Retrieves details of a specific alert from a SharePoint site list';
}

public get schema(): z.ZodTypeAny | undefined {
return options;
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
try {
if (this.verbose) {
await logger.logToStderr(`Retrieving alert with id '${args.options.id}' from site '${args.options.webUrl}'...`);
}

const requestOptions: CliRequestOptions = {
url: `${args.options.webUrl}/_api/Web/Alerts/GetById('${formatting.encodeQueryParameter(args.options.id)}')?$expand=List,User,List/Rootfolder&$select=*,List/Id,List/Title,List/Rootfolder/ServerRelativeUrl`,
headers: {
accept: 'application/json;odata=nometadata'
},
responseType: 'json'
};

const res = await request.get<any>(requestOptions);
await logger.log(res);
}
catch (err: any) {
this.handleRejectedODataJsonPromise(err);
}
}
}

export default new SpoWebAlertGetCommand();