Skip to content

Adds outlook event get command. Closes #7122#7189

Open
nanddeepn wants to merge 6 commits intopnp:mainfrom
nanddeepn:issue-7122
Open

Adds outlook event get command. Closes #7122#7189
nanddeepn wants to merge 6 commits intopnp:mainfrom
nanddeepn:issue-7122

Conversation

@nanddeepn
Copy link
Copy Markdown
Contributor

Closes #7122

Copilot AI review requested due to automatic review settings April 5, 2026 03:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Microsoft Graph-based CLI command to retrieve a specific Outlook calendar event, plus supporting calendar lookup utilities, tests, and documentation.

Changes:

  • Introduces outlook event get command with options for user, calendar, and timezone.
  • Adds src/utils/calendar helper for resolving calendars by id/name (with interactive disambiguation).
  • Adds tests and Docusaurus docs/sidebar entry for the new command.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/utils/calendar.ts New utility to resolve a user calendar by id or by name (with multi-result prompting).
src/utils/calendar.spec.ts Unit tests for the new calendar utility.
src/m365/outlook/commands/event/event-get.ts Implements the new outlook event get command (builds Graph request, supports timezone preference).
src/m365/outlook/commands/event/event-get.spec.ts Tests for command validation, happy path, and OData error handling.
src/m365/outlook/commands.ts Registers the EVENT_GET command name.
docs/src/config/sidebars.ts Adds the new command to the Outlook docs sidebar.
docs/docs/cmd/outlook/event/event-get.mdx New documentation page for outlook event get.

Comment on lines +44 to +48
public getRefinedSchema(schema: typeof options): z.ZodObject<any> | undefined {
return schema
.refine(options => [options.userId, options.userName].filter(x => x !== undefined).length === 1, {
error: 'Specify either userId or userName, but not both'
})
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refined schema currently enforces that exactly one of userId/userName must be provided. This prevents the common delegated-permissions flow of defaulting to the signed-in user ("me"), which is supported by other Outlook commands (for example src/m365/outlook/commands/calendargroup/calendargroup-list.ts allows omitting both and uses /me). Consider changing validation to disallow specifying both, and enforce the requirement for userId/userName only when running with app-only permissions in commandAction.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +51
.refine(options => !(options.calendarId && options.calendarName), {
error: 'Specify either calendarId or calendarName, but not both.'
});
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema only prevents specifying both calendarId and calendarName, but it allows specifying neither. In commandAction you always construct a URL that includes /calendars/${calendarId}/..., so when neither option is provided the request URL will contain calendars/undefined and fail. Either require one of calendarId/calendarName in the refined schema, or update commandAction to handle the no-calendar case (for example by calling the /users/{id}/events/{id} endpoint).

Copilot uses AI. Check for mistakes.
Comment on lines +55 to +66
const userIdentifier = args.options.userId ?? args.options.userName;
if (this.verbose) {
await logger.logToStderr(`Retrieving event ${args.options.id} for user ${userIdentifier}...`);
}

let calendarId = args.options.calendarId;
if (args.options.calendarName) {
calendarId = (await calendar.getUserCalendarByName(userIdentifier!, args.options.calendarName))!.id;
}

let requestUrl = `${this.resource}/v1.0/users/${userIdentifier}/calendars/${calendarId}/events/${args.options.id}`;

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When building the request URL, userIdentifier can be a UPN and is inserted into the path without encoding, and the command always uses the /users/... form (no support for /me). In other Outlook commands, UPNs are typically passed via users('...') and/or encoded with formatting.encodeQueryParameter, and delegated calls can default to me (see calendargroup-list.ts and message-list.ts). Updating the user segment construction would improve correctness for special characters and align behavior with existing commands.

Copilot uses AI. Check for mistakes.
Comment on lines +230 to +251
it('retrieves event by id for the user specified with userName and calendarName', async () => {
sinon.stub(calendar, 'getUserCalendarByName').resolves({ id: calendarId });

sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://graph.microsoft.com/v1.0/users/${userName}/calendars/${calendarId}/events/${id}`) {
return eventResponse;
}

throw 'Invalid request';
});

await command.action(logger, {
options: commandOptionsSchema.parse({
id: id,
userName: userName,
calendarName: calendarName,
timeZone: 'Pacific Standard Time',
verbose: true
})
});
assert(loggerLogSpy.calledOnceWith(eventResponse));
});
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test that passes timeZone doesn't assert that the request includes the Prefer: outlook.timezone="..." header. Since this header controls a key part of the command's behavior, add an assertion in the stubbed request.get to verify opts.headers.Prefer is set when timeZone is provided (and absent when it isn't).

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +10
async getUserCalendarById(userId: string, calendarId: string, calendarGroupId?: string, properties?: string): Promise<Calendar> {
let url = `https://graph.microsoft.com/v1.0/users('${userId}')/${calendarGroupId ? `calendarGroups/${calendarGroupId}/` : ''}calendars/${calendarId}`;

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL is built using users('${userId}') without encoding userId. UPNs for guest users commonly contain #EXT#, and an unencoded # will be treated as a URL fragment delimiter and won’t be sent to Microsoft Graph. Consider URL-encoding the userId value when composing the request URL (for example using formatting.encodeQueryParameter).

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +28
async getUserCalendarByName(userId: string, name: string, calendarGroupId?: string, properties?: string): Promise<Calendar> {
let url = `https://graph.microsoft.com/v1.0/users('${userId}')/${calendarGroupId ? `calendarGroups/${calendarGroupId}/` : ''}calendars?$filter=name eq '${formatting.encodeQueryParameter(name)}'`;

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The URL is built using users('${userId}') without encoding userId. UPNs for guest users commonly contain #EXT#, and an unencoded # will be treated as a URL fragment delimiter and won’t be sent to Microsoft Graph. Consider URL-encoding the userId value when composing the request URL (for example using formatting.encodeQueryParameter).

Copilot uses AI. Check for mistakes.
@milanholemans
Copy link
Copy Markdown
Contributor

Thanks, we'll try to review it ASAP!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New command: outlook event get

3 participants