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-rule-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 rule get

Retrieves details of a specific rule from a SharePoint site list

## Usage

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

## Options

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

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

<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 a rule by ID

```sh
m365 spo web rule 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 @@ -4305,6 +4305,11 @@ const sidebars: SidebarsConfig = {
label: 'web retentionlabel list',
id: 'cmd/spo/web/web-retentionlabel-list'
},
{
type: 'doc',
label: 'web rule get',
id: 'cmd/spo/web/web-rule-get'
},
{
type: 'doc',
label: 'web roleassignment add',
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 @@ -372,9 +372,10 @@ export default {
WEB_REINDEX: `${prefix} web reindex`,
WEB_REMOVE: `${prefix} web remove`,
WEB_RETENTIONLABEL_LIST: `${prefix} web retentionlabel list`,
WEB_RULE_GET: `${prefix} web rule get`,
WEB_ROLEASSIGNMENT_REMOVE: `${prefix} web roleassignment remove`,
WEB_ROLEASSIGNMENT_ADD: `${prefix} web roleassignment add`,
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-rule-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-rule-get.js';

describe(commands.WEB_RULE_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_RULE_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 a rule 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 rule 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-rule-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 SpoWebRuleGetCommand extends SpoCommand {
public get name(): string {
return commands.WEB_RULE_GET;
}

public get description(): string {
return 'Retrieves details of a specific rule 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 rule 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 SpoWebRuleGetCommand();