diff --git a/docs/docs/cmd/teams/meeting/meeting-add.mdx b/docs/docs/cmd/teams/meeting/meeting-add.mdx
new file mode 100644
index 00000000000..b42a5671038
--- /dev/null
+++ b/docs/docs/cmd/teams/meeting/meeting-add.mdx
@@ -0,0 +1,277 @@
+import Global from '/docs/cmd/_global.mdx';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# teams meeting add
+
+Create a new online meeting
+
+## Usage
+
+```sh
+m365 teams meeting add [options]
+```
+
+## Options
+
+```md definition-list
+`-s, --startTime [startTime]`
+: The start time of the meeting. If not specified, the startTime will be set to the current time.
+
+`-e, --endTime [endTime]`
+: The end time of the meeting. If not specified, the endTime will be set to one hour after the startTime.
+
+`--subject [subject]`
+: The subject of the meeting.
+
+`-p, --participantUserNames [participantUserNames]`
+: A comma-separated list of participant UPNs.
+
+`--organizerEmail [organizerEmail]`
+: The organizer's email address.
+
+`-r, --recordAutomatically`
+: When using this flag, the meeting will be recorded automatically.
+```
+
+
+
+## Remarks
+
+To create an online meeting for a specific organizer, use the **--organizerEmail** parameter along with app-only permissions. The registered app should have the **OnlineMeetings.ReadWrite.All** permissions, and a special policy should be assigned to the user specified in the **organizerEmail** option. You can find more information on how to assign this policy to a user [here](https://learn.microsoft.com/en-us/graph/cloud-communication-online-meeting-application-access-policy).
+
+## Examples
+
+Create a new online meeting for the currently logged-in user, starting immediately and ending after one hour.
+
+```sh
+m365 teams meeting add
+```
+
+Create a new online meeting for the currently logged-in user, with a specified start date and a duration of one hour.
+
+```sh
+m365 teams meeting add --startTime "2023-09-21T13:30:00Z"
+```
+
+Create a new online meeting for the currently logged-in user, with specified end date the current date as the start date.
+
+```sh
+m365 teams meeting add --endTime "2023-09-21T23:55:00Z"
+```
+
+Create a new online meeting for the currently logged-in user, with specified start and end dates.
+
+```sh
+m365 teams meeting add --startTime "2023-09-21T13:30:00Z" --endTime "2023-09-21T23:55:00Z"
+```
+
+Create a new online meeting for the currently logged-in user, with a specified subject.
+
+```sh
+m365 teams meeting add --startTime "2023-09-21T13:30:00Z" --endTime "2023-09-21T23:55:00Z" --subject "Test Subject"
+```
+
+Create a new online meeting for the currently logged-in user, with a specified subject and a list of participantUserNames.
+
+```sh
+m365 teams meeting add --subject "Test Subject" --participantUserNames "john.doe@contoso.com,olga.manager@contoso.com"
+```
+
+Create a new online meeting for the currently logged-in user, with a specified subject, a list of participantUserNames, and automatic meeting recording.
+
+```sh
+m365 teams meeting add --subject "Test Subject" --participantUserNames "john.doe@contoso.com,olga.manager@contoso.com" --recordAutomatically
+```
+
+Create a new online meeting for a selected organizer, with a specified subject, a list of participantUserNames, and automatic meeting recording. This option is available for app-only permissions.
+
+```sh
+m365 teams meeting add --organizerEmail "john.doe@contoso.com" --subject "Test Subject" --participantUserNames "olga.manager@contoso.com" --recordAutomatically
+```
+
+## Response
+
+
+
+
+ ```json
+ {
+ "id": "abc",
+ "creationDateTime": "2023-07-25T19:29:32.033109Z",
+ "startDateTime": "2023-07-17T03:00:00Z",
+ "endDateTime": "2023-07-17T04:00:00Z",
+ "joinUrl": "https://teams.microsoft.com/l/meetup-join/abc",
+ "joinWebUrl": "https://teams.microsoft.com/l/meetup-join/abc",
+ "meetingCode": "12345",
+ "subject": "Subject",
+ "isBroadcast": false,
+ "autoAdmittedUsers": "unknownFutureValue",
+ "outerMeetingAutoAdmittedUsers": null,
+ "isEntryExitAnnounced": false,
+ "allowedPresenters": "everyone",
+ "allowMeetingChat": "enabled",
+ "shareMeetingChatHistoryDefault": "none",
+ "allowTeamworkReactions": true,
+ "allowAttendeeToEnableMic": true,
+ "allowAttendeeToEnableCamera": true,
+ "recordAutomatically": false,
+ "anonymizeIdentityForRoles": [],
+ "capabilities": [],
+ "videoTeleconferenceId": null,
+ "externalId": null,
+ "iCalUid": null,
+ "meetingType": null,
+ "allowParticipantsToChangeName": false,
+ "allowRecording": true,
+ "allowTranscription": true,
+ "meetingMigrationMode": null,
+ "broadcastSettings": null,
+ "audioConferencing": null,
+ "meetingInfo": null,
+ "participants": {
+ "organizer": {
+ "upn": "john.doe@contoso.com",
+ "role": "presenter",
+ "identity": {
+ "application": null,
+ "device": null,
+ "user": {
+ "id": "12345678-1234-1234-1234-12345678",
+ "displayName": null,
+ "tenantId": "12345678-1234-1234-1234-12345678",
+ "identityProvider": "AAD"
+ }
+ }
+ },
+ "attendees": [
+ {
+ "upn": "adele.vance@contoso.com",
+ "role": "attendee",
+ "identity": {
+ "application": null,
+ "device": null,
+ "user": {
+ "id": "12345678-1234-1234-1234-12345678",
+ "displayName": null,
+ "tenantId": "12345678-1234-1234-1234-12345678,
+ "identityProvider": "AAD"
+ }
+ }
+ }
+ ]
+ },
+ "lobbyBypassSettings": {
+ "scope": "unknownFutureValue",
+ "isDialInBypassEnabled": false
+ },
+ "joinMeetingIdSettings": {
+ "isPasscodeRequired": true,
+ "joinMeetingId": "12345",
+ "passcode": "123456"
+ },
+ "chatInfo": {
+ "threadId": "abc",
+ "messageId": "0",
+ "replyChainMessageId": null
+ },
+ "joinInformation": {
+ "content": "textContent",
+ "contentType": "html"
+ },
+ "watermarkProtection": {
+ "isEnabledForContentSharing": false,
+ "isEnabledForVideo": false
+ }
+ }
+ ```
+
+
+
+
+ ```text
+ allowAttendeeToEnableCamera : true
+ allowAttendeeToEnableMic : true
+ allowMeetingChat : enabled
+ allowParticipantsToChangeName : false
+ allowRecording : true
+ allowTeamworkReactions : true
+ allowTranscription : true
+ allowedPresenters : everyone
+ anonymizeIdentityForRoles : []
+ audioConferencing : null
+ autoAdmittedUsers : everyoneInCompany
+ broadcastSettings : null
+ capabilities : []
+ chatInfo : {"threadId":"19:meeting_ID@thread.v2","messageId":"0","replyChainMessageId":null}
+ chatRestrictions : null
+ creationDateTime : 2023-11-13T16:02:12.5012352Z
+ endDateTime : 2023-11-13T17:02:11.9711697Z
+ externalId : null
+ iCalUid : null
+ id : meetingID
+ isBroadcast : false
+ isEntryExitAnnounced : true
+ joinInformation : {"content":"data:text/html,htmlMessageTemplate","contentType":"html"}
+ joinMeetingIdSettings : {"isPasscodeRequired":false,"joinMeetingId":"123456789012","passcode":null}
+ joinUrl : https://teams.microsoft.com/l/meetup-join/abc
+ joinWebUrl : https://teams.microsoft.com/l/meetup-join/abc
+ lobbyBypassSettings : {"scope":"organization","isDialInBypassEnabled":false}
+ meetingCode : 123456789012
+ meetingInfo : null
+ meetingMigrationMode : null
+ meetingType : null
+ outerMeetingAutoAdmittedUsers : null
+ participants : {"organizer":{"upn":"user@contoso.com","role":"presenter","identity":{"application":null,"device":null,"user":{"id":"12345678-1234-1234-1234-12345678","displayName":null,"tenantId":"12345678-1234-1234-1234-12345678","identityProvider":"AAD"}}},"attendees":[]}
+ recordAutomatically : false
+ shareMeetingChatHistoryDefault: none
+ startDateTime : 2023-11-13T16:02:11.9711697Z
+ subject : null
+ videoTeleconferenceId : null
+ watermarkProtection : null
+ ```
+
+
+
+
+ ```csv
+ id,creationDateTime,startDateTime,endDateTime,joinUrl,meetingCode,isBroadcast,autoAdmittedUsers,joinWebUrl,isEntryExitAnnounced,allowedPresenters,allowAttendeeToEnableMic,allowAttendeeToEnableCamera,allowMeetingChat,shareMeetingChatHistoryDefault,allowTeamworkReactions,recordAutomatically,allowParticipantsToChangeName,allowTranscription,allowRecording
+meetingId,2023-11-13T16:03:03.5669316Z,2023-11-13T16:03:03.2213499Z,2023-11-13T17:03:03.2213499Z,https://teams.microsoft.com/l/meetup-join/abc,123456789012,,everyoneInCompany,https://teams.microsoft.com/l/meetup-join/abc,1,everyone,1,1,enabled,none,1,,,1,1
+ ```
+
+
+
+
+ ```md
+ # teams meeting add
+
+ Date: 13/11/2023
+
+ ## meetingId
+
+ Property | Value
+ ---------|-------
+ id | meetingId
+ creationDateTime | 2023-11-13T16:03:57.6531542Z
+ startDateTime | 2023-11-13T16:03:56.8464734Z
+ endDateTime | 2023-11-13T17:03:56.8464734Z
+ joinUrl | https://teams.microsoft.com/l/meetup-join/abc
+ meetingCode | 123456789012
+ isBroadcast | false
+ autoAdmittedUsers | everyoneInCompany
+ joinWebUrl | https://teams.microsoft.com/l/meetup-join/abc
+ isEntryExitAnnounced | true
+ allowedPresenters | everyone
+ allowAttendeeToEnableMic | true
+ allowAttendeeToEnableCamera | true
+ allowMeetingChat | enabled
+ shareMeetingChatHistoryDefault | none
+ allowTeamworkReactions | true
+ recordAutomatically | false
+ allowParticipantsToChangeName | false
+ allowTranscription | true
+ allowRecording | true
+ ```
+
+
+
\ No newline at end of file
diff --git a/docs/docs/cmd/teams/meeting/meeting-create.mdx b/docs/docs/cmd/teams/meeting/meeting-create.mdx
deleted file mode 100644
index 7169a3821cd..00000000000
--- a/docs/docs/cmd/teams/meeting/meeting-create.mdx
+++ /dev/null
@@ -1,241 +0,0 @@
-import Global from '/docs/cmd/_global.mdx';
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-# teams meeting create
-
-Create a new online meeting
-
-## Usage
-
-```sh
-m365 teams meeting create [options]
-```
-
-## Options
-
-```md definition-list
-`-s --startTime [startTime]`
-: The start time of the meeting. If not specified, the startTime will be set to the current time.
-
-`-e --endTime [endTime]`
-: The end time of the meeting. If not specified, the endTime will be set to one hour after the startTime.
-
-`--subject [subject]`
-: The subject of the meeting.
-
-`-p, --participants [participants]`
-: A comma-separated list of participant UPNs.
-
-`--organizerEmail [organizerEmail]`
-: The organizer's email address. Requires application permissions.
-
-`-r, --recordAutomatically`
-: When using this flag, the meeting will be recorded automatically.
-```
-
-
-
-## Examples
-
-Create a new online meeting for the currently logged-in user, starting immediately and ending after one hour.
-
-```sh
-m365 teams meeting create
-```
-
-Create a new online meeting for the currently logged-in user, with a specified start date and a duration of one hour.
-
-```sh
-m365 teams meeting create --startTime "2023-09-21T13:30:00Z"
-```
-
-Create a new online meeting for the currently logged-in user, with specified start and end dates.
-
-```sh
-m365 teams meeting create --startTime "2023-09-21T13:30:00Z" --endTime "2023-09-21T23:55:00Z"
-```
-
-Create a new online meeting for the currently logged-in user, with a specified subject.
-
-```sh
-m365 teams meeting create --startTime "2023-09-21T13:30:00Z" --endTime "2023-09-21T23:55:00Z" --subject "Test Subject"
-```
-
-Create a new online meeting for the currently logged-in user, with a specified subject and a list of participants.
-
-```sh
-m365 teams meeting create --subject "Test Subject" --participantEmails "john.doe@contoso.com,olga.manager@contoso.com"
-```
-
-Create a new online meeting for the currently logged-in user, with a specified subject, a list of participants, and automatic meeting recording.
-
-```sh
-m365 teams meeting create --subject "Test Subject" --participantEmails "john.doe@contoso.com,olga.manager@contoso.com" --recordAutomatically
-```
-
-Create a new online meeting for a selected organizer, with a specified subject, a list of participants, and automatic meeting recording. This option is available for app-only permissions.
-
-```sh
-m365 teams meeting create --organizerEmail "john.doe@contoso.com" --subject "Test Subject" --participantEmails "olga.manager@contoso.com" --recordAutomatically
-```
-
-## Response
-
-
-
-
- ```json
- [
- {
- "id": "abc",
- "creationDateTime": "2023-07-25T19:29:32.033109Z",
- "startDateTime": "2023-07-17T03:00:00Z",
- "endDateTime": "2023-07-17T04:00:00Z",
- "joinUrl": "https://teams.microsoft.com/l/meetup-join/abc",
- "joinWebUrl": "https://teams.microsoft.com/l/meetup-join/abc",
- "meetingCode": "12345",
- "subject": "Subject",
- "isBroadcast": false,
- "autoAdmittedUsers": "unknownFutureValue",
- "outerMeetingAutoAdmittedUsers": null,
- "isEntryExitAnnounced": false,
- "allowedPresenters": "everyone",
- "allowMeetingChat": "enabled",
- "shareMeetingChatHistoryDefault": "none",
- "allowTeamworkReactions": true,
- "allowAttendeeToEnableMic": true,
- "allowAttendeeToEnableCamera": true,
- "recordAutomatically": false,
- "anonymizeIdentityForRoles": [],
- "capabilities": [],
- "videoTeleconferenceId": null,
- "externalId": null,
- "iCalUid": null,
- "meetingType": null,
- "allowParticipantsToChangeName": false,
- "allowRecording": true,
- "allowTranscription": true,
- "meetingMigrationMode": null,
- "broadcastSettings": null,
- "audioConferencing": null,
- "meetingInfo": null,
- "participants": {
- "organizer": {
- "upn": "john.doe@contoso.com",
- "role": "presenter",
- "identity": {
- "application": null,
- "device": null,
- "user": {
- "id": "b2091e18-7882-4efe-b7d1-90703f5a5c65",
- "displayName": null,
- "tenantId": "ad4f158a-97c7-4914-a9bd-038ecde40ff3",
- "identityProvider": "AAD"
- }
- }
- },
- "attendees": [
- {
- "upn": "adele.vance@contoso.com",
- "role": "attendee",
- "identity": {
- "application": null,
- "device": null,
- "user": {
- "id": "52bd2d9c-2d89-416f-96c4-ca94245e22c8",
- "displayName": null,
- "tenantId": "ad4f158a-97c7-4914-a9bd-038ecde40ff3",
- "identityProvider": "AAD"
- }
- }
- }
- ]
- },
- "lobbyBypassSettings": {
- "scope": "unknownFutureValue",
- "isDialInBypassEnabled": false
- },
- "joinMeetingIdSettings": {
- "isPasscodeRequired": true,
- "joinMeetingId": "12345",
- "passcode": "Z3GYtQ"
- },
- "chatInfo": {
- "threadId": "abc",
- "messageId": "0",
- "replyChainMessageId": null
- },
- "joinInformation": {
- "content": "textContent",
- "contentType": "html"
- },
- "watermarkProtection": {
- "isEnabledForContentSharing": false,
- "isEnabledForVideo": false
- }
- }
- ]
- ```
-
-
-
-
- ```text
- endDateTime : 2023-10-05T13:57:09.1889486Z
- joinUrl : https://teams.microsoft.com/l/meetup-join/abc
- recordAutomatically: false
- startDateTime : 2023-10-05T12:57:09.1889486Z
- subject : null
- ```
-
-
-
-
- ```csv
- @odata.context,id,creationDateTime,startDateTime,endDateTime,joinUrl,joinWebUrl,meetingCode,isBroadcast,autoAdmittedUsers,isEntryExitAnnounced,allowedPresenters,allowMeetingChat,shareMeetingChatHistoryDefault,allowTeamworkReactions,allowAttendeeToEnableMic,allowAttendeeToEnableCamera,recordAutomatically,allowParticipantsToChangeName,allowRecording,allowTranscription
-https://graph.microsoft.com/v1.0/$metadata#users('id')/onlineMeetings/$entity,abc,2023-10-05T12:56:20.9466495Z,2023-10-05T12:56:20.6594121Z,2023-10-05T13:56:20.6594121Z,https://teams.microsoft.com/l/meetup-join/abc,https://teams.microsoft.com/l/meetup-join/abc,388012423843,,everyoneInCompany,1,everyone,enabled,none,1,1,1,,,1,1
- ```
-
-
-
-
- ```md
-# teams meeting create --organizerEmail "john.doe@contoso.com"
-
-Date: 05/10/2023
-
-## abc
-
- Property | Value
----------|-------
-@odata.context | https://graph.microsoft.com/v1.0/$metadata#users('id')/onlineMeetings/$entity
-id | abc
-creationDateTime | 2023-10-05T12:56:00.6719952Z
-startDateTime | 2023-10-05T12:56:00.2303956Z
-endDateTime | 2023-10-05T13:56:00.2303956Z
-joinUrl | https://teams.microsoft.com/l/meetup-join/abc
-joinWebUrl | https://teams.microsoft.com/l/meetup-join/abc
-meetingCode | 12345
-isBroadcast | false
-autoAdmittedUsers | everyoneInCompany
-isEntryExitAnnounced | true
-allowedPresenters | everyone
-allowMeetingChat | enabled
-shareMeetingChatHistoryDefault | none
-allowTeamworkReactions | true
-allowAttendeeToEnableMic | true
-allowAttendeeToEnableCamera | true
-recordAutomatically | false
-allowParticipantsToChangeName | false
-allowRecording | true
-allowTranscription | true
- ```
-
-
-
-
-
-## Remarks
-
-To create an online meeting for a specific organizer, use the **--organizerEmail** parameter along with app-only permissions. The registered app should have the **OnlineMeetings.ReadWrite.All** permissions, and a special policy should be assigned to the user specified in the **organizerEmail** option. You can find more information on how to assign this policy to a user [here](https://learn.microsoft.com/en-us/graph/cloud-communication-online-meeting-application-access-policy).
\ No newline at end of file
diff --git a/docs/src/config/sidebars.js b/docs/src/config/sidebars.js
index 4a2b86d79d5..542978cace2 100644
--- a/docs/src/config/sidebars.js
+++ b/docs/src/config/sidebars.js
@@ -3806,8 +3806,8 @@ const sidebars = {
meeting: [
{
type: 'doc',
- label: 'meeting create',
- id: 'cmd/teams/meeting/meeting-create'
+ label: 'meeting add',
+ id: 'cmd/teams/meeting/meeting-add'
},
{
type: 'doc',
diff --git a/src/m365/teams/commands.ts b/src/m365/teams/commands.ts
index 8f16b9abae4..5aff8ea3d7d 100644
--- a/src/m365/teams/commands.ts
+++ b/src/m365/teams/commands.ts
@@ -29,7 +29,7 @@ export default {
GUESTSETTINGS_LIST: `${prefix} guestsettings list`,
GUESTSETTINGS_SET: `${prefix} guestsettings set`,
MEETING_ATTENDANCEREPORT_LIST: `${prefix} meeting attendancereport list`,
- MEETING_CREATE: `${prefix} meeting create`,
+ MEETING_ADD: `${prefix} meeting add`,
MEETING_GET: `${prefix} meeting get`,
MEETING_LIST: `${prefix} meeting list`,
MEETING_TRANSCRIPT_LIST: `${prefix} meeting transcript list`,
diff --git a/src/m365/teams/commands/meeting/meeting-create.spec.ts b/src/m365/teams/commands/meeting/meeting-add.spec.ts
similarity index 74%
rename from src/m365/teams/commands/meeting/meeting-create.spec.ts
rename to src/m365/teams/commands/meeting/meeting-add.spec.ts
index 4f6ee32ae24..0270dc8e882 100644
--- a/src/m365/teams/commands/meeting/meeting-create.spec.ts
+++ b/src/m365/teams/commands/meeting/meeting-add.spec.ts
@@ -13,13 +13,13 @@ 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 from './meeting-create.js';
+import command from './meeting-add.js';
-describe(commands.MEETING_CREATE, () => {
+describe(commands.MEETING_ADD, () => {
const startTime = '2022-04-04T03:00:00Z';
const endTime = '2022-04-04T04:00:00Z';
const subject = 'test subject';
- const participants = 'abc@email.com,abc2@email.com';
+ const participantUserNames = 'abc@email.com,abc2@email.com';
const organizerEmail = 'organizer@email.com';
// #region responses
@@ -78,90 +78,101 @@ describe(commands.MEETING_CREATE, () => {
});
it('has correct name', () => {
- assert.strictEqual(command.name, commands.MEETING_CREATE);
+ assert.strictEqual(command.name, commands.MEETING_ADD);
});
it('has a description', () => {
assert.notStrictEqual(command.description, null);
});
- it('completes validation when no parameter is provided', async () => {
- const actual = await command.validate({ options: { startTime: undefined, endTime: undefined, subject: undefined, participants: undefined, organizerEmail: undefined, recordAutomatically: undefined } }, commandInfo);
+ it('completes validation when no parameters are provided', async () => {
+ const actual = await command.validate({ options: { startTime: undefined, endTime: undefined, subject: undefined, participantUserNames: undefined, organizerEmail: undefined, recordAutomatically: undefined } }, commandInfo);
assert.strictEqual(actual, true);
});
- it('completes validation when only startTime is provided and it is a valid ISODateTime', async () => {
+ it('completes validation when only the startTime parameter is provided, and it is a valid ISODateTime', async () => {
const actual = await command.validate({ options: { startTime: startTime } }, commandInfo);
assert.strictEqual(actual, true);
});
- it('completes validation when the startTime and endTime are provided and they are valid ISODateTime', async () => {
+ it('completes validation when both the startTime and endTime parameters are provided, and they are valid ISODateTimes', async () => {
const actual = await command.validate({ options: { startTime: startTime, endTime: endTime } }, commandInfo);
assert.strictEqual(actual, true);
});
- it('completes validation when only subject is provided', async () => {
+ it('completes validation when only the subject parameter is provided', async () => {
const actual = await command.validate({ options: { subject: subject } }, commandInfo);
assert.strictEqual(actual, true);
});
- it('completes validation when only organizerEmail is provided', async () => {
+ it('completes validation when only the organizerEmail parameter is provided', async () => {
const actual = await command.validate({ options: { organizerEmail: organizerEmail } }, commandInfo);
assert.strictEqual(actual, true);
});
- it('completes validation when only participants parameter is provided', async () => {
- const actual = await command.validate({ options: { participants: participants } }, commandInfo);
+ it('completes validation when only the participantUserNames parameter is provided', async () => {
+ const actual = await command.validate({ options: { participantUserNames: participantUserNames } }, commandInfo);
assert.strictEqual(actual, true);
});
+ it('completes validation when the correct endTime is provided, and the startTime is not provided', async () => {
+ const fakeTimers = sinon.useFakeTimers(new Date('2020-01-01T12:00:00.000Z'));
+
+ const actual = await command.validate({ options: { endTime: endTime } }, commandInfo);
+ assert.strictEqual(actual, true);
+
+ fakeTimers.restore();
+ });
+
it('fails validation when the startTime is not a valid ISODateTime', async () => {
const actual = await command.validate({ options: { startTime: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when the correct startTime is provided and the endTime is not a valid ISODateTime', async () => {
+ it('fails validation when the correct startTime is provided, and the endTime is not a valid ISODateTime', async () => {
const actual = await command.validate({ options: { startTime: startTime, endTime: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when the correct endTime is provided and the startTime is not provided', async () => {
- const actual = await command.validate({ options: { endTime: endTime } }, commandInfo);
+ it('fails validation when the endTime is before the startTime', async () => {
+ const actual = await command.validate({ options: { startTime: '2023-01-01', endTime: '2022-12-31' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
-
- it('fails validation when endTime is before startTime', async () => {
- const actual = await command.validate({ options: { startTime: '2023-01-01', endTime: '2022-12-31' } }, commandInfo);
+ it('fails validation when only the endTime is provided and occurs before the current time.', async () => {
+ const fakeTimers = sinon.useFakeTimers(new Date('2020-01-01T12:00:00.000Z'));
+ const actual = await command.validate({ options: { endTime: '1990-12-31' } }, commandInfo);
assert.notStrictEqual(actual, true);
+
+ fakeTimers.restore();
});
- it('fails validation when the organizerEmail is not a valid', async () => {
+ it('fails validation when the organizerEmail parameter is not a valid email address', async () => {
const actual = await command.validate({ options: { organizerEmail: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when the participants is not a valid', async () => {
- const actual = await command.validate({ options: { participants: 'foo' } }, commandInfo);
+ it('fails validation when the participantUserNames is not a valid', async () => {
+ const actual = await command.validate({ options: { participantUserNames: 'foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when the participants are not separated by comma', async () => {
- const actual = await command.validate({ options: { participants: 'abc@email.com|abc2@email.com' } }, commandInfo);
+ it('fails validation when the participantUserNames are not separated by comma', async () => {
+ const actual = await command.validate({ options: { participantUserNames: 'abc@email.com|abc2@email.com' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when the participants has incorrect email', async () => {
- const actual = await command.validate({ options: { participants: 'abc@email.com,foo' } }, commandInfo);
+ it('fails validation when the participantUserNames has incorrect format', async () => {
+ const actual = await command.validate({ options: { participantUserNames: 'abc@email.com,foo' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('fails validation when startDateTime is behind endDateTime', async () => {
- const actual = await command.validate({ options: { startDateTime: '2023-01-01', endDateTime: '2022-12-31' } }, commandInfo);
+ it('fails validation when the startDate is after the endDate', async () => {
+ const actual = await command.validate({ options: { startTime: '2023-01-01', endTime: '2022-12-31' } }, commandInfo);
assert.notStrictEqual(actual, true);
});
- it('throws an error when the organizerEmail is not filled in when signed in using app-only authentication', async () => {
+ it('throws an error when the organizerEmail is not provided while signed in using app-only authentication', async () => {
sinonUtil.restore(accessToken.isAppOnlyAccessToken);
sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true);
@@ -169,6 +180,18 @@ describe(commands.MEETING_CREATE, () => {
new CommandError(`The option 'organizerEmail' is required when creating a meeting using app only permissions`));
});
+ it('throws an error when the organizerEmail parameter is set and delegated permissions are used', async () => {
+ sinonUtil.restore(accessToken.isAppOnlyAccessToken);
+ sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(false);
+
+ await assert.rejects(command.action(logger, {
+ options: {
+ verbose: true,
+ organizerEmail: organizerEmail
+ }
+ }), new CommandError(`The option 'organizerEmail' is not supported when creating a meeting using delegated permissions`));
+ });
+
it('create a meeting for the currently logged in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -194,7 +217,37 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('create a meeting with defined startDate for the currently logged in user', async () => {
+ it('create a meeting with a defined startDate for the currently logged-in user', async () => {
+ const fakeTimers = sinon.useFakeTimers(new Date('2020-01-01T12:00:00.000Z'));
+
+ let calledUrl = '';
+ let calledData = '';
+
+ sinon.stub(request, 'post').callsFake(async opts => {
+ if (opts.url === 'https://graph.microsoft.com/v1.0/me/onlineMeetings') {
+ calledUrl = opts.url;
+ calledData = opts.data;
+ return meeting;
+ }
+
+ throw 'Invalid request: ' + opts.url;
+ });
+
+ await command.action(logger, {
+ options: {
+ verbose: true,
+ endTime: endTime
+ }
+ });
+
+ assert.strictEqual(calledUrl, 'https://graph.microsoft.com/v1.0/me/onlineMeetings');
+ assert.deepEqual(calledData, { startDateTime: '2020-01-01T12:00:00.000Z', endDateTime: endTime });
+ assert(loggerLogSpy.calledWith(meeting));
+
+ fakeTimers.restore();
+ });
+
+ it('create a meeting with a defined endDate and current date as startDate for the currently logged-in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -220,7 +273,7 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('create a meeting with defined startDate and endDate for the currently logged in user', async () => {
+ it('create a meeting with defined startDate and endDate for the currently logged-in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -247,7 +300,7 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('create a meeting with defined startDate, endDate and subject for the currently logged in user', async () => {
+ it('create a meeting with defined startDate, endDate and subject for the currently logged-in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -275,7 +328,7 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('create a meeting with defined startDate, endDate, subject, and participants for the currently logged in user', async () => {
+ it('create a meeting with defined startDate, endDate, subject, and participantUserNames for the currently logged-in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -295,7 +348,7 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants
+ participantUserNames: participantUserNames
}
});
@@ -315,7 +368,7 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('create a meeting with defined startDate, endDate, subject, participants and recordAutomatically for the currently logged in user', async () => {
+ it('create a meeting with defined startDate, endDate, subject, participantUserNames and recordAutomatically for the currently logged-in user', async () => {
let calledUrl = '';
let calledData = '';
@@ -335,7 +388,7 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants,
+ participantUserNames: participantUserNames,
recordAutomatically: true
}
});
@@ -357,7 +410,7 @@ describe(commands.MEETING_CREATE, () => {
});
- it('create a meeting with defined startDate, endDate, subject, participants and recordAutomatically for the specified organizerEmail when app only authorization', async () => {
+ it('create a meeting with defined startDate, endDate, subject, participantUserNames and recordAutomatically for the specified organizerEmail when app only authorization', async () => {
let calledUrl = '';
let calledData = '';
const testOrganizerId = '12345678-7882-4efe-b7d1-90703f5a5c65';
@@ -388,7 +441,7 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants,
+ participantUserNames: participantUserNames,
recordAutomatically: true,
organizerEmail: organizerEmail
}
@@ -410,7 +463,7 @@ describe(commands.MEETING_CREATE, () => {
assert(loggerLogSpy.calledWith(meeting));
});
- it('handles error correctly when organizer Id is not found', async () => {
+ it('handles error appropriately when organizer Id is not found', async () => {
const testOrganizerId = '12345678-7882-4efe-b7d1-90703f5a5c65';
sinonUtil.restore(accessToken.isAppOnlyAccessToken);
@@ -437,7 +490,7 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants,
+ participantUserNames: participantUserNames,
recordAutomatically: true,
organizerEmail: organizerEmail
}
@@ -445,7 +498,7 @@ describe(commands.MEETING_CREATE, () => {
);
});
- it('handles error forbidden correctly', async () => {
+ it('handles the forbidden error correctly', async () => {
sinon.stub(request, 'post').callsFake(async opts => {
if (opts.url === 'https://graph.microsoft.com/v1.0/me/onlineMeetings') {
@@ -453,7 +506,7 @@ describe(commands.MEETING_CREATE, () => {
response: {
status: 403
},
- message: 'Forbidden'
+ message: "Forbidden. You do not have permission to perform this action. Please verify the command's details for more information."
};
}
@@ -467,14 +520,14 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants,
+ participantUserNames: participantUserNames,
recordAutomatically: true
}
}), new CommandError(`Forbidden. You do not have permission to perform this action. Please verify the command's details for more information.`)
);
});
- it('handles error correctly', async () => {
+ it('handles error appropriately', async () => {
sinon.stub(request, 'post').callsFake(async opts => {
if (opts.url === 'https://graph.microsoft.com/v1.0/me/onlineMeetings') {
@@ -497,7 +550,7 @@ describe(commands.MEETING_CREATE, () => {
startTime: startTime,
endTime: endTime,
subject: subject,
- participants: participants,
+ participantUserNames: participantUserNames,
recordAutomatically: true
}
}), new CommandError('Error message')
diff --git a/src/m365/teams/commands/meeting/meeting-create.ts b/src/m365/teams/commands/meeting/meeting-add.ts
similarity index 64%
rename from src/m365/teams/commands/meeting/meeting-create.ts
rename to src/m365/teams/commands/meeting/meeting-add.ts
index c49dbfbfbc7..fe0cf521318 100644
--- a/src/m365/teams/commands/meeting/meeting-create.ts
+++ b/src/m365/teams/commands/meeting/meeting-add.ts
@@ -17,22 +17,18 @@ interface Options extends GlobalOptions {
startTime?: string;
endTime?: string;
subject?: string;
- participants?: string;
+ participantUserNames?: string;
organizerEmail?: string;
recordAutomatically?: boolean;
}
-class TeamsMeetingCreateCommand extends GraphCommand {
+class TeamsMeetingAddCommand extends GraphCommand {
public get name(): string {
- return commands.MEETING_CREATE;
+ return commands.MEETING_ADD;
}
public get description(): string {
- return 'Create a new online meeting';
- }
-
- public defaultProperties(): string[] | undefined {
- return ['subject', 'startDateTime', 'endDateTime', 'joinUrl', 'recordAutomatically'];
+ return 'Creates a new online meeting';
}
constructor() {
@@ -49,7 +45,7 @@ class TeamsMeetingCreateCommand extends GraphCommand {
startTime: typeof args.options.startTime !== 'undefined',
endTime: typeof args.options.endTime !== 'undefined',
subject: typeof args.options.subject !== 'undefined',
- participants: typeof args.options.participants !== 'undefined',
+ participantUserNames: typeof args.options.participantUserNames !== 'undefined',
organizerEmail: typeof args.options.organizerEmail !== 'undefined',
recordAutomatically: !!args.options.recordAutomatically
});
@@ -59,22 +55,22 @@ class TeamsMeetingCreateCommand extends GraphCommand {
#initOptions(): void {
this.options.unshift(
{
- option: '-s --startTime [startTime]'
+ option: '-s, --startTime [startTime]'
},
{
- option: '-e --endTime [endTime]'
+ option: '-e, --endTime [endTime]'
},
{
- option: '-s --subject [subject]'
+ option: '--subject [subject]'
},
{
- option: '-p --participants [participants]'
+ option: '-p, --participantUserNames [participantUserNames]'
},
{
option: '--organizerEmail [organizerEmail]'
},
{
- option: '-r --recordAutomatically'
+ option: '-r, --recordAutomatically'
}
);
}
@@ -82,30 +78,40 @@ class TeamsMeetingCreateCommand extends GraphCommand {
#initValidators(): void {
this.validators.push(
async (args: CommandArgs) => {
+
if (args.options.startTime && !validation.isValidISODateTime(args.options.startTime)) {
return `'${args.options.startTime}' is not a valid ISO date string for startTime.`;
}
+
if (args.options.endTime && !validation.isValidISODateTime(args.options.endTime)) {
return `'${args.options.endTime}' is not a valid ISO date string for endTime.`;
}
+
if (args.options.startTime && args.options.endTime && new Date(args.options.startTime) >= new Date(args.options.endTime)) {
- return 'startTime value must be before endTime.';
+ return 'The startTime value must be before endTime.';
}
- if (args.options.endTime && !args.options.startTime) {
- return 'startTime should be specified when endTime is specified.';
+
+ if (args.options.endTime && !args.options.startTime && new Date() >= new Date(args.options.endTime)) {
+ return 'When only the endTime is specified, it needs to be after the current time.';
}
- if (args.options.participants) {
- if (args.options.participants.indexOf(',') === -1 && !validation.isValidUserPrincipalName(args.options.participants)) {
- return `${args.options.participants} contains invalid UPN.`;
+
+ if (args.options.participantUserNames) {
+
+ if (args.options.participantUserNames.indexOf(',') === -1 && !validation.isValidUserPrincipalName(args.options.participantUserNames)) {
+ return `${args.options.participantUserNames} contains invalid UPN.`;
}
- const participants = args.options.participants.trim().toLowerCase().split(',').filter(e => e && e !== '');
+
+ const participants = args.options.participantUserNames.trim().toLowerCase().split(',').filter(e => e && e !== '');
+
if (!participants || participants.length === 0 || participants.some(e => !validation.isValidUserPrincipalName(e))) {
- return `${args.options.participants} contains one or more invalid UPN.`;
+ return `${args.options.participantUserNames} contains one or more invalid UPN.`;
}
}
+
if (args.options.organizerEmail && !validation.isValidUserPrincipalName(args.options.organizerEmail)) {
return `'${args.options.organizerEmail}' is not a valid email for organizerEmail.`;
}
+
return true;
}
);
@@ -119,10 +125,25 @@ class TeamsMeetingCreateCommand extends GraphCommand {
public async commandAction(logger: Logger, args: CommandArgs): Promise {
try {
const isAppOnlyAccessToken = accessToken.isAppOnlyAccessToken(auth.service.accessTokens[this.resource].accessToken)!;
+
if (isAppOnlyAccessToken && !args.options.organizerEmail) {
throw `The option 'organizerEmail' is required when creating a meeting using app only permissions`;
}
- const graphBaseUrl = await this.getGraphBaseUrl(args.options);
+
+ if (!isAppOnlyAccessToken && args.options.organizerEmail) {
+ throw `The option 'organizerEmail' is not supported when creating a meeting using delegated permissions`;
+ }
+
+ let graphBaseUrl = `${this.resource}/v1.0/`;
+
+ if (args.options.organizerEmail) {
+ const organizerId = await aadUser.getUserIdByEmail(args.options.organizerEmail);
+ graphBaseUrl = `${graphBaseUrl}users/${organizerId}`;
+ }
+ else {
+ graphBaseUrl = `${graphBaseUrl}me`;
+ }
+
const meeting = await this.createMeeting(logger, graphBaseUrl, args.options);
await logger.log(meeting);
}
@@ -130,24 +151,6 @@ class TeamsMeetingCreateCommand extends GraphCommand {
this.handleRejectedODataJsonPromise(err);
}
}
-
- /**
- * Gets the base MS Graph URL for the request
- * @param options
- * @returns correct MS Graph URL for the request
- */
- private async getGraphBaseUrl(options: Options): Promise {
- let requestUrl = `${this.resource}/v1.0/`;
- if (options.organizerEmail) {
- const organizerId = await aadUser.getUserIdByEmail(options.organizerEmail);
- requestUrl += `users/${organizerId}`;
- }
- else {
- requestUrl += 'me';
- }
- return requestUrl;
- }
-
/**
* Creates a new online meeting
* @param logger
@@ -155,52 +158,59 @@ class TeamsMeetingCreateCommand extends GraphCommand {
* @param options
* @returns MS Graph online meeting response
*/
- private async createMeeting(logger: Logger, graphBaseUrl: string, options: Options): Promise {
+ private async createMeeting(logger: Logger, graphBaseUrl: string, options: Options): Promise {
+
if (this.verbose) {
- logger.logToStderr(`Creation of a meeting...`);
+ await logger.logToStderr(`Creating the meeting...`);
}
+
const requestData: any = {};
- if (options.participants) {
- const attendees = options.participants.trim().toLowerCase().split(',').map(p => ({
+
+ if (options.participantUserNames) {
+ const attendees = options.participantUserNames.trim().toLowerCase().split(',').map(p => ({
upn: p.trim()
}));
requestData.participants = { attendees };
}
+
if (options.startTime) {
requestData.startDateTime = options.startTime;
}
+
if (options.endTime) {
requestData.endDateTime = options.endTime;
+
+ if (!options.startTime) {
+ requestData.startDateTime = new Date().toISOString();
+ }
}
+
if (options.subject) {
requestData.subject = options.subject;
}
+
if (options.recordAutomatically !== undefined) {
- requestData.recordAutomatically = options.recordAutomatically;
+ requestData.recordAutomatically = true;
}
- const requestOption: CliRequestOptions = {
+
+ const requestOptions: CliRequestOptions = {
headers: {
- accept: 'application/json',
+ accept: 'application/json;odata.metadata=none',
'content-type': 'application/json'
},
responseType: 'json',
- method: 'POST',
url: `${graphBaseUrl}/onlineMeetings`,
data: requestData
};
try {
- const requestResponse = await request.post(requestOption);
+ const requestResponse = await request.post(requestOptions);
return requestResponse;
}
catch (error: any) {
- if (error.response.status === 403) {
- throw `Forbidden. You do not have permission to perform this action. Please verify the command's details for more information.`;
- }
-
throw error.message;
}
}
}
-export default new TeamsMeetingCreateCommand();
\ No newline at end of file
+export default new TeamsMeetingAddCommand();
\ No newline at end of file