diff --git a/docs/docs/cmd/outlook/event/event-get.mdx b/docs/docs/cmd/outlook/event/event-get.mdx new file mode 100644 index 00000000000..fe1af28c8d1 --- /dev/null +++ b/docs/docs/cmd/outlook/event/event-get.mdx @@ -0,0 +1,280 @@ +import Global from '../../_global.mdx'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# outlook event get + +Retrieve an event from a specific calendar of a user + +## Usage + +```sh +m365 outlook event get [options] +``` + +## Options + +```md definition-list +`-i, --id ` +: ID of the event. + +`--userId [userId]` +: ID of the user. Specify either `userId` or `userName`, but not both. + +`--userName [userName]` +: UPN of the user. Specify either `userId` or `userName`, but not both. + +`--calendarId [calendarId]` +: ID of the calendar. Specify either `calendarId` or `calendarName`, but not both. + +`--calendarName [calendarName]` +: Name of the calendar. Specify either `calendarId` or `calendarName`, but not both. + +`--timeZone [timeZone]` +: The time zone for the event start and end times. If not specified, the start and end times are in UTC. +``` + + + +## Permissions + + + + + | Resource | Permissions | + |-----------------|-------------------------------------| + | Microsoft Graph | Calendars.ReadBasic, Calendars.Read | + + + + + | Resource | Permissions | + |-----------------|-------------------------------------| + | Microsoft Graph | Calendars.ReadBasic, Calendars.Read | + + + + +:::note + +When you specify a value for timeZone, consider the options of the [time zone list](https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones), or [additional time zone list](https://learn.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0#additional-time-zones). + +::: + +## Examples + +Get an event for the current signed-in user from a calendar specified by id. + +```sh +m365 outlook event get --id "AAMkAGVmMDEzMTM4L" --userId "@meId" --calendarId "AAMkAGRkZ" +``` + +Get an event for the user from a calendar specified by id and return event times in Pacific Standard Time time zone. + +```sh +m365 outlook event get --id "AAMkAGVmMDEzMTM4L" --userName "john.doe@contoso.com" --calendarId "AAMkAGRkZ" --timeZone 'Pacific Standard Time' +``` + +## Response + + + + + ```json + { + "id": "AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA==", + "createdDateTime": "2026-04-04T11:03:22.881996Z", + "lastModifiedDateTime": "2026-04-04T11:05:26.2216557Z", + "changeKey": "xMBBaLl1lk+dAn8KkjfXKQAGLmp8jA==", + "categories": [], + "transactionId": "localevent:93639269-b1b2-d604-5170-283b0e470da5", + "originalStartTimeZone": "UTC", + "originalEndTimeZone": "UTC", + "iCalUId": "040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF", + "uid": "040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF", + "reminderMinutesBeforeStart": 15, + "isReminderOn": true, + "hasAttachments": false, + "subject": "New Product Regulations Touchpoint", + "bodyPreview": "New Product Regulations Strategy Online Touchpoint Meeting\r\\\n\r\\\nYou're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox.\r\\\n\r\\\n________", + "importance": "normal", + "sensitivity": "normal", + "isAllDay": false, + "isCancelled": false, + "isOrganizer": true, + "responseRequested": true, + "seriesMasterId": null, + "showAs": "busy", + "type": "singleInstance", + "webLink": "https://outlook.office365.com/owa/?itemid=AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN%2F7V4K8g0q%2Badetip1DygcAxMBBaLl1lk%2BdAn8KkjfXKQAAAgENAAAAxMBBaLl1lk%2BdAn8KkjfXKQAGMVCCQQAAAA%3D%3D&exvsurl=1&path=/calendar/item", + "onlineMeetingUrl": null, + "isOnlineMeeting": true, + "onlineMeetingProvider": "teamsForBusiness", + "allowNewTimeProposals": true, + "occurrenceId": null, + "isDraft": false, + "hideAttendees": false, + "responseStatus": { + "response": "organizer", + "time": "0001-01-01T00:00:00Z" + }, + "body": { + "contentType": "html", + "content": "\r\\\n\r\\\n\r\\\n\r\\\n\r\\\n
\r\\\nNew Product Regulations Strategy Online Touchpoint Meeting
\r\\\n
\r\\\n
\r\\\n
\r\\\n
\r\\\nYou're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox.
\r\\\n
\r\\\n
\r\\\n
\r\\\n________________________________________________________________________________
\r\\\n
Microsoft Teams meeting\r\\\n
\r\\\n\r\\\n
Meeting ID:\r\\\n488 031 372 636 31\r\\\n
\r\\\n
Passcode:\r\\\nuN2Np6PN\r\\\n
\r\\\n
\r\\\n
\r\\\n
\r\\\n\r\\\n
For organizers:\r\\\nMeeting\r\\\n options
\r\\\n
\r\\\n
\r\\\n
\r\\\n________________________________________________________________________________
\r\\\n
\r\\\n\r\\\n\r\\\n" + }, + "start": { + "dateTime": "2026-04-04T11:30:00.0000000", + "timeZone": "UTC" + }, + "end": { + "dateTime": "2026-04-04T12:00:00.0000000", + "timeZone": "UTC" + }, + "location": { + "displayName": "Microsoft Teams Meeting", + "locationType": "default", + "uniqueId": "Microsoft Teams Meeting", + "uniqueIdType": "private" + }, + "locations": [ + { + "displayName": "Microsoft Teams Meeting", + "locationType": "default", + "uniqueId": "Microsoft Teams Meeting", + "uniqueIdType": "private" + } + ], + "recurrence": null, + "attendees": [ + { + "type": "required", + "status": { + "response": "none", + "time": "0001-01-01T00:00:00Z" + }, + "emailAddress": { + "name": "Debra Berger", + "address": "debraB@contoso.com" + } + } + ], + "organizer": { + "emailAddress": { + "name": "John Doe", + "address": "john.doe@contoso.com" + } + }, + "onlineMeeting": { + "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZjE4ZGNmODktODg3ZS00MTRjLTg4ZmMtZWMzMjBkZTE5YjBl%40thread.v2/0?context=%7b%22Tid%22%3a%22f2c94a41-d33d-4b60-bb3d-0bed4cdf9855%22%2c%22Oid%22%3a%229bd29c6c-181e-41f5-a1b6-bc30bbf652d3%22%7d" + } + } + ``` + +
+ + + ```text + allowNewTimeProposals : true + attendees : [{"type":"required","status":{"response":"none","time":"0001-01-01T00:00:00Z"},"emailAddress":{"name":"Debra Berger","address":"debraB@contoso.com"}}] + body : {"contentType":"html","content":"\r\n\r\n\r\n\r\n\r\n
\r\nNew Product Regulations Strategy Online Touchpoint Meeting
\r\n
\r\n
\r\n
\r\n
\r\nYou're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox.
\r\n
\r\n
\r\n
\r\n________________________________________________________________________________
\r\n
Microsoft Teams meeting\r\n
\r\n\r\n
Meeting ID:\r\n488 031 372 636 31\r\n
\r\n
Passcode:\r\nuN2Np6PN\r\n
\r\n
\r\n
\r\n
\r\n\r\n
For organizers:\r\nMeeting\r\n options
\r\n
\r\n
\r\n
\r\n________________________________________________________________________________
\r\n
\r\n\r\n\r\n"} + bodyPreview : New Product Regulations Strategy Online Touchpoint Meeting + + You're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox. + + ________ + categories : [] + changeKey : xMBBaLl1lk+dAn8KkjfXKQAGLmp8jA== + createdDateTime : 2026-04-04T11:03:22.881996Z + end : {"dateTime":"2026-04-04T12:00:00.0000000","timeZone":"UTC"} + hasAttachments : false + hideAttendees : false + iCalUId : 040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF + id : AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA== + importance : normal + isAllDay : false + isCancelled : false + isDraft : false + isOnlineMeeting : true + isOrganizer : true + isReminderOn : true + lastModifiedDateTime : 2026-04-04T11:05:26.2216557Z + location : {"displayName":"Microsoft Teams Meeting","locationType":"default","uniqueId":"Microsoft Teams Meeting","uniqueIdType":"private"} + locations : [{"displayName":"Microsoft Teams Meeting","locationType":"default","uniqueId":"Microsoft Teams Meeting","uniqueIdType":"private"}] + occurrenceId : null + onlineMeeting : {"joinUrl":"https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZjE4ZGNmODktODg3ZS00MTRjLTg4ZmMtZWMzMjBkZTE5YjBl%40thread.v2/0?context=%7b%22Tid%22%3a%22f2c94a41-d33d-4b60-bb3d-0bed4cdf9855%22%2c%22Oid%22%3a%229bd29c6c-181e-41f5-a1b6-bc30bbf652d3%22%7d"} + onlineMeetingProvider : teamsForBusiness + onlineMeetingUrl : null + organizer : {"emailAddress":{"name":"John Doe","address":"john.doe@contoso.com"}} + originalEndTimeZone : UTC + originalStartTimeZone : UTC + recurrence : null + reminderMinutesBeforeStart: 15 + responseRequested : true + responseStatus : {"response":"organizer","time":"0001-01-01T00:00:00Z"} + sensitivity : normal + seriesMasterId : null + showAs : busy + start : {"dateTime":"2026-04-04T11:30:00.0000000","timeZone":"UTC"} + subject : New Product Regulations Touchpoint + transactionId : localevent:93639269-b1b2-d604-5170-283b0e470da5 + type : singleInstance + uid : 040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF + webLink : https://outlook.office365.com/owa/?itemid=AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN%2F7V4K8g0q%2Badetip1DygcAxMBBaLl1lk%2BdAn8KkjfXKQAAAgENAAAAxMBBaLl1lk%2BdAn8KkjfXKQAGMVCCQQAAAA%3D%3D&exvsurl=1&path=/calendar/item + ``` + +
+ + + ```csv + id,createdDateTime,lastModifiedDateTime,changeKey,transactionId,originalStartTimeZone,originalEndTimeZone,iCalUId,uid,reminderMinutesBeforeStart,isReminderOn,hasAttachments,subject,bodyPreview,importance,sensitivity,isAllDay,isCancelled,isOrganizer,responseRequested,seriesMasterId,showAs,type,webLink,onlineMeetingUrl,isOnlineMeeting,onlineMeetingProvider,allowNewTimeProposals,occurrenceId,isDraft,hideAttendees,recurrence + AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA==,2026-04-04T11:03:22.881996Z,2026-04-04T11:05:26.2216557Z,xMBBaLl1lk+dAn8KkjfXKQAGLmp8jA==,localevent:93639269-b1b2-d604-5170-283b0e470da5,UTC,UTC,040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF,040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF,15,1,0,New Product Regulations Touchpoint,"New Product Regulations Strategy Online Touchpoint Meeting + + You're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox. + + ________",normal,normal,0,0,1,1,,busy,singleInstance,https://outlook.office365.com/owa/?itemid=AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN%2F7V4K8g0q%2Badetip1DygcAxMBBaLl1lk%2BdAn8KkjfXKQAAAgENAAAAxMBBaLl1lk%2BdAn8KkjfXKQAGMVCCQQAAAA%3D%3D&exvsurl=1&path=/calendar/item,,1,teamsForBusiness,1,,0,0, + ``` + + + + + ```md + # outlook event get --debug "false" --verbose "false" --id "AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA==" --userId "9bd29c6c-181e-41f5-a1b6-bc30bbf652d3" --calendarName "Calendar" + + Date: 4/4/2026 + + ## AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA== + + Property | Value + ---------|------- + id | AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q\_adetip1DygcAxMBBaLl1lk\_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk\_dAn8KkjfXKQAGMVCCQQAAAA== + createdDateTime | 2026-04-04T11:03:22.881996Z + lastModifiedDateTime | 2026-04-04T11:05:26.2216557Z + changeKey | xMBBaLl1lk+dAn8KkjfXKQAGLmp8jA== + transactionId | localevent:93639269-b1b2-d604-5170-283b0e470da5 + originalStartTimeZone | UTC + originalEndTimeZone | UTC + iCalUId | 040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF + uid | 040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF + reminderMinutesBeforeStart | 15 + isReminderOn | true + hasAttachments | false + subject | New Product Regulations Touchpoint +
You're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from thi
\_\_\_\_\_\_\_\_ing it in your inbox. + importance | normal + sensitivity | normal + isAllDay | false + isCancelled | false + isOrganizer | true + responseRequested | true + showAs | busy + type | singleInstance + webLink | https://outlook.office365.com/owa/?itemid=AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN%2F7V4K8g0q%2Badetip1DygcAxMBBaLl1lk%2BdAn8KkjfXKQAAAgENAAAAxMBBaLl1lk%2BdAn8KkjfXKQAGMVCCQQAAAA%3D%3D&exvsurl=1&path=/calendar/item + isOnlineMeeting | true + onlineMeetingProvider | teamsForBusiness + allowNewTimeProposals | true + isDraft | false + hideAttendees | false + ``` + +
+
diff --git a/docs/src/config/sidebars.ts b/docs/src/config/sidebars.ts index e6fa9eb9ecc..a7a20e4b48f 100644 --- a/docs/src/config/sidebars.ts +++ b/docs/src/config/sidebars.ts @@ -1337,6 +1337,11 @@ const sidebars: SidebarsConfig = { }, { event: [ + { + type: 'doc', + label: 'event get', + id: 'cmd/outlook/event/event-get' + }, { type: 'doc', label: 'event cancel', diff --git a/src/m365/outlook/commands.ts b/src/m365/outlook/commands.ts index fefaf8800c9..d0425d2a4b9 100644 --- a/src/m365/outlook/commands.ts +++ b/src/m365/outlook/commands.ts @@ -7,6 +7,7 @@ export default { CALENDARGROUP_LIST: `${prefix} calendargroup list`, CALENDARGROUP_SET: `${prefix} calendargroup set`, EVENT_CANCEL: `${prefix} event cancel`, + EVENT_GET: `${prefix} event get`, EVENT_LIST: `${prefix} event list`, EVENT_REMOVE: `${prefix} event remove`, MAIL_SEARCHFOLDER_ADD: `${prefix} mail searchfolder add`, diff --git a/src/m365/outlook/commands/event/event-get.spec.ts b/src/m365/outlook/commands/event/event-get.spec.ts new file mode 100644 index 00000000000..c4405a01c1b --- /dev/null +++ b/src/m365/outlook/commands/event/event-get.spec.ts @@ -0,0 +1,262 @@ +import assert from 'assert'; +import sinon from 'sinon'; +import auth from '../../../../Auth.js'; +import { CommandError } from '../../../../Command.js'; +import { CommandInfo } from '../../../../cli/CommandInfo.js'; +import { Logger } from '../../../../cli/Logger.js'; +import { cli } from '../../../../cli/cli.js'; +import request from '../../../../request.js'; +import { telemetry } from '../../../../telemetry.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 './event-get.js'; +import { calendar } from '../../../../utils/calendar.js'; + +describe(commands.EVENT_GET, () => { + const id = "AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA=="; + const userId = "9bd29c6c-181e-41f5-a1b6-bc30bbf652d3"; + const userName = "john.doe@contoso.com"; + const calendarId = "AAMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAAAAAAw3-tXgryDSr5p162KnUPKBwDEwEFouXWWT50CfwqSN9cpAAAAAAEGAADEwEFouXWWT50CfwqSN9cpAAAkuACjAAA="; + const calendarName = "Calendar"; + + const eventResponse = { + "id": "AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN-7V4K8g0q_adetip1DygcAxMBBaLl1lk_dAn8KkjfXKQAAAgENAAAAxMBBaLl1lk_dAn8KkjfXKQAGMVCCQQAAAA==", + "createdDateTime": "2026-04-04T11:03:22.881996Z", + "lastModifiedDateTime": "2026-04-04T11:05:26.2216557Z", + "changeKey": "xMBBaLl1lk+dAn8KkjfXKQAGLmp8jA==", + "categories": [], + "transactionId": "localevent:93639269-b1b2-d604-5170-283b0e470da5", + "originalStartTimeZone": "UTC", + "originalEndTimeZone": "UTC", + "iCalUId": "040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF", + "uid": "040000008200E00074C5B7101A82E0080000000051EE49A722C4DC0100000000000000001000000065853ABD35D4FE438112E0B9CF451ABF", + "reminderMinutesBeforeStart": 15, + "isReminderOn": true, + "hasAttachments": false, + "subject": "New Product Regulations Touchpoint", + "bodyPreview": "New Product Regulations Strategy Online Touchpoint Meeting\r\\\n\r\\\nYou're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox.\r\\\n\r\\\n________", + "importance": "normal", + "sensitivity": "normal", + "isAllDay": false, + "isCancelled": false, + "isOrganizer": true, + "responseRequested": true, + "seriesMasterId": null, + "showAs": "busy", + "type": "singleInstance", + "webLink": "https://outlook.office365.com/owa/?itemid=AQMkAGYzNjMxYTU4LTJjZjYtNDlhMi1iMzQ2LWVmMTU3YmUzOGM5MABGAAADMN%2F7V4K8g0q%2Badetip1DygcAxMBBaLl1lk%2BdAn8KkjfXKQAAAgENAAAAxMBBaLl1lk%2BdAn8KkjfXKQAGMVCCQQAAAA%3D%3D&exvsurl=1&path=/calendar/item", + "onlineMeetingUrl": null, + "isOnlineMeeting": true, + "onlineMeetingProvider": "teamsForBusiness", + "allowNewTimeProposals": true, + "occurrenceId": null, + "isDraft": false, + "hideAttendees": false, + "responseStatus": { + "response": "organizer", + "time": "0001-01-01T00:00:00Z" + }, + "body": { + "contentType": "html", + "content": "\r\\\n\r\\\n\r\\\n\r\\\n\r\\\n
\r\\\nNew Product Regulations Strategy Online Touchpoint Meeting
\r\\\n
\r\\\n
\r\\\n
\r\\\n
\r\\\nYou're receiving this message because you're a member of the Engineering group. If you don't want to receive any messages or events from this group, stop following it in your inbox.
\r\\\n
\r\\\n
\r\\\n
\r\\\n________________________________________________________________________________
\r\\\n
Microsoft Teams meeting\r\\\n
\r\\\n
Join:\r\\\nhttps://teams.microsoft.com/meet/48803137263631?p=YXe9K6OhVD94VIC23M\r\\\n
\r\\\n
Meeting ID:\r\\\n488 031 372 636 31\r\\\n
\r\\\n
Passcode:\r\\\nuN2Np6PN\r\\\n
\r\\\n
\r\\\n
\r\\\n
\r\\\n
Need help?\r\\\n| \r\\\nSystem reference
\r\\\n
For organizers:\r\\\nMeeting\r\\\n options
\r\\\n
\r\\\n
\r\\\n
\r\\\n________________________________________________________________________________
\r\\\n
\r\\\n\r\\\n\r\\\n" + }, + "start": { + "dateTime": "2026-04-04T11:30:00.0000000", + "timeZone": "UTC" + }, + "end": { + "dateTime": "2026-04-04T12:00:00.0000000", + "timeZone": "UTC" + }, + "location": { + "displayName": "Microsoft Teams Meeting", + "locationType": "default", + "uniqueId": "Microsoft Teams Meeting", + "uniqueIdType": "private" + }, + "locations": [ + { + "displayName": "Microsoft Teams Meeting", + "locationType": "default", + "uniqueId": "Microsoft Teams Meeting", + "uniqueIdType": "private" + } + ], + "recurrence": null, + "attendees": [ + { + "type": "required", + "status": { + "response": "none", + "time": "0001-01-01T00:00:00Z" + }, + "emailAddress": { + "name": "Debra Berger", + "address": "debraB@contoso.com" + } + } + ], + "organizer": { + "emailAddress": { + "name": "John Doe", + "address": "john.doe@contoso.com" + } + }, + "onlineMeeting": { + "joinUrl": "https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZjE4ZGNmODktODg3ZS00MTRjLTg4ZmMtZWMzMjBkZTE5YjBl%40thread.v2/0?context=%7b%22Tid%22%3a%22f2c94a41-d33d-4b60-bb3d-0bed4cdf9855%22%2c%22Oid%22%3a%229bd29c6c-181e-41f5-a1b6-bc30bbf652d3%22%7d" + } + }; + + let log: string[]; + let logger: Logger; + let commandInfo: CommandInfo; + let loggerLogSpy: sinon.SinonSpy; + let commandOptionsSchema: typeof options; + + before(() => { + sinon.stub(auth, 'restoreAuth').resolves(); + sinon.stub(telemetry, 'trackEvent').resolves(); + sinon.stub(pid, 'getProcessName').returns(''); + sinon.stub(session, 'getId').returns(''); + auth.connection.active = true; + if (!auth.connection.accessTokens[auth.defaultResource]) { + auth.connection.accessTokens[auth.defaultResource] = { + expiresOn: 'abc', + accessToken: 'abc' + }; + } + commandInfo = cli.getCommandInfo(command); + commandOptionsSchema = commandInfo.command.getSchemaToParse() as typeof options; + }); + + 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'); + }); + + afterEach(() => { + sinonUtil.restore([ + request.get, + calendar.getUserCalendarByName + ]); + }); + + after(() => { + sinon.restore(); + auth.connection.active = false; + }); + + it('has correct name', () => { + assert.strictEqual(command.name, commands.EVENT_GET); + }); + + it('has a description', () => { + assert.notStrictEqual(command.description, null); + }); + + it('passes validation with userId', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userId: userId, calendarId: calendarId }); + assert.strictEqual(actual.success, true); + }); + + it('passes validation with userName', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userName: userName, calendarName: calendarName }); + assert.strictEqual(actual.success, true); + }); + + it('fails validation if both userId and userName are specified', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userId: userId, userName: userName, calendarId: calendarId }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if neither userId nor userName is specified', () => { + const actual = commandOptionsSchema.safeParse({ id: id, calendarId: calendarId }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if userId is not a valid GUID', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userId: 'foo', calendarId: calendarId }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if userName is not a valid UPN', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userName: 'foo', calendarId: calendarId }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation if both calendarId and calendarName are specified', () => { + const actual = commandOptionsSchema.safeParse({ id: id, userId: userId, calendarId: calendarId, calendarName: calendarName }); + assert.notStrictEqual(actual.success, true); + }); + + it('fails validation with unknown options', () => { + const actual = commandOptionsSchema.safeParse({ id: id, unknownOption: 'value' }); + assert.notStrictEqual(actual.success, true); + }); + + it('retrieves event by id for the user specified with userId and calendarId', async () => { + sinon.stub(request, 'get').callsFake(async (opts) => { + if (opts.url === `https://graph.microsoft.com/v1.0/users/${userId}/calendars/${calendarId}/events/${id}`) { + return eventResponse; + } + + throw 'Invalid request'; + }); + + await command.action(logger, { + options: commandOptionsSchema.parse({ + id: id, + userId: userId, + calendarId: calendarId, + verbose: true + }) + }); + assert(loggerLogSpy.calledOnceWith(eventResponse)); + }); + + 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)); + }); + + it('correctly handles API OData error', async () => { + const errorMessage = 'Something went wrong'; + sinon.stub(request, 'get').rejects({ error: { error: { message: errorMessage } } }); + + await assert.rejects( + command.action(logger, { options: commandOptionsSchema.parse({ id: id, userId: userId, calendarId: calendarId }) }), + new CommandError(errorMessage) + ); + }); +}); \ No newline at end of file diff --git a/src/m365/outlook/commands/event/event-get.ts b/src/m365/outlook/commands/event/event-get.ts new file mode 100644 index 00000000000..1ba4454e7c3 --- /dev/null +++ b/src/m365/outlook/commands/event/event-get.ts @@ -0,0 +1,89 @@ +import { Event } from '@microsoft/microsoft-graph-types'; +import { z } from 'zod'; +import { Logger } from '../../../../cli/Logger.js'; +import GraphCommand from '../../../base/GraphCommand.js'; +import commands from '../../commands.js'; +import { validation } from '../../../../utils/validation.js'; +import { globalOptionsZod } from '../../../../Command.js'; +import request, { CliRequestOptions } from '../../../../request.js'; +import { calendar } from '../../../../utils/calendar.js'; + +export const options = z.strictObject({ + ...globalOptionsZod.shape, + id: z.string().alias('i'), + userId: z.string().refine(id => validation.isValidGuid(id), { + error: e => `'${e.input}' is not a valid GUID.` + }).optional(), + userName: z.string().refine(name => validation.isValidUserPrincipalName(name), { + error: e => `'${e.input}' is not a valid UPN.` + }).optional(), + calendarId: z.string().optional(), + calendarName: z.string().optional(), + timeZone: z.string().optional() +}); + +declare type Options = z.infer; + +interface CommandArgs { + options: Options; +} + +class OutlookEventGetCommand extends GraphCommand { + public get name(): string { + return commands.EVENT_GET; + } + + public get description(): string { + return `Retrieve an event from a specific calendar of a user`; + } + + public get schema(): z.ZodType | undefined { + return options; + } + + public getRefinedSchema(schema: typeof options): z.ZodObject | undefined { + return schema + .refine(options => [options.userId, options.userName].filter(x => x !== undefined).length === 1, { + error: 'Specify either userId or userName, but not both' + }) + .refine(options => !(options.calendarId && options.calendarName), { + error: 'Specify either calendarId or calendarName, but not both.' + }); + } + + public async commandAction(logger: Logger, args: CommandArgs): Promise { + 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}`; + + const requestOptions: CliRequestOptions = { + url: requestUrl, + headers: { + accept: 'application/json;odata.metadata=none' + }, + responseType: 'json' + }; + + if (args.options.timeZone) { + requestOptions.headers!.Prefer = `outlook.timezone="${args.options.timeZone}"`; + } + + try { + const result = await request.get(requestOptions); + await logger.log(result); + } + catch (err: any) { + this.handleRejectedODataJsonPromise(err); + } + } +} + +export default new OutlookEventGetCommand(); \ No newline at end of file