@pnp/cli-microsoft365 5.0.0-beta.d025005 → 5.0.0-beta.d107c7a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +12 -9
- package/.eslintrc.js +1 -0
- package/.mocharc.json +9 -0
- package/dist/Command.js +1 -1
- package/dist/m365/aad/commands/app/app-delete.js +123 -0
- package/dist/m365/aad/commands/app/app-get.js +56 -11
- package/dist/m365/aad/commands/app/app-set.js +98 -3
- package/dist/m365/aad/commands/group/group-list.js +14 -1
- package/dist/m365/aad/commands/o365group/o365group-conversation-list.js +41 -0
- package/dist/m365/aad/commands.js +2 -0
- package/dist/m365/outlook/commands/room/room-list.js +43 -0
- package/dist/m365/outlook/commands/roomlist/roomlist-list.js +25 -0
- package/dist/m365/outlook/commands.js +2 -0
- package/dist/m365/planner/commands/task/task-get.js +1 -1
- package/dist/m365/planner/commands/task/task-list.js +37 -7
- package/dist/m365/spo/commands/group/group-user-add.js +15 -8
- package/dist/m365/teams/commands/app/app-install.js +75 -21
- package/dist/m365/teams/commands/channel/channel-get.js +29 -7
- package/dist/m365/teams/commands/chat/chat-message-send.js +225 -0
- package/dist/m365/teams/commands.js +1 -0
- package/docs/docs/cmd/aad/app/app-delete.md +51 -0
- package/docs/docs/cmd/aad/app/app-get.md +12 -1
- package/docs/docs/cmd/aad/app/app-set.md +21 -0
- package/docs/docs/cmd/aad/group/group-list.md +9 -0
- package/docs/docs/cmd/aad/o365group/o365group-conversation-list.md +24 -0
- package/docs/docs/cmd/outlook/room/room-list.md +30 -0
- package/docs/docs/cmd/outlook/roomlist/roomlist-list.md +21 -0
- package/docs/docs/cmd/planner/task/task-get.md +5 -0
- package/docs/docs/cmd/planner/task/task-list.md +5 -0
- package/docs/docs/cmd/spo/group/group-user-add.md +4 -0
- package/docs/docs/cmd/teams/app/app-install.md +22 -4
- package/docs/docs/cmd/teams/channel/channel-get.md +10 -1
- package/docs/docs/cmd/teams/chat/chat-message-send.md +55 -0
- package/npm-shrinkwrap.json +36 -3
- package/package.json +6 -2
|
@@ -25,25 +25,40 @@ class PlannerTaskListCommand extends GraphItemsListCommand_1.GraphItemsListComma
|
|
|
25
25
|
return ['id', 'title', 'startDateTime', 'dueDateTime', 'completedDateTime'];
|
|
26
26
|
}
|
|
27
27
|
commandAction(logger, args, cb) {
|
|
28
|
-
const bucketId = args.options.bucketId;
|
|
29
28
|
const bucketName = args.options.bucketName;
|
|
30
|
-
|
|
29
|
+
let bucketId = args.options.bucketId;
|
|
31
30
|
const planName = args.options.planName;
|
|
31
|
+
let planId = args.options.planId;
|
|
32
|
+
let taskItems = [];
|
|
32
33
|
if (bucketId || bucketName) {
|
|
33
34
|
this
|
|
34
35
|
.getBucketId(args)
|
|
35
|
-
.then((
|
|
36
|
+
.then((retrievedBucketId) => {
|
|
37
|
+
bucketId = retrievedBucketId;
|
|
38
|
+
return this.getAllItems(`${this.resource}/v1.0/planner/buckets/${bucketId}/tasks`, logger, true);
|
|
39
|
+
})
|
|
36
40
|
.then(() => {
|
|
37
|
-
|
|
41
|
+
taskItems = this.items;
|
|
42
|
+
return this.getAllItems(`${this.resource}/beta/planner/buckets/${bucketId}/tasks`, logger, true);
|
|
43
|
+
})
|
|
44
|
+
.then(() => {
|
|
45
|
+
logger.log(this.mergeTaskPriority(taskItems, this.items));
|
|
38
46
|
cb();
|
|
39
47
|
}, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
40
48
|
}
|
|
41
49
|
else if (planId || planName) {
|
|
42
50
|
this
|
|
43
51
|
.getPlanId(args)
|
|
44
|
-
.then((
|
|
52
|
+
.then((retrievedPlanId) => {
|
|
53
|
+
planId = retrievedPlanId;
|
|
54
|
+
return this.getAllItems(`${this.resource}/v1.0/planner/plans/${planId}/tasks`, logger, true);
|
|
55
|
+
})
|
|
56
|
+
.then(() => {
|
|
57
|
+
taskItems = this.items;
|
|
58
|
+
return this.getAllItems(`${this.resource}/beta/planner/plans/${planId}/tasks`, logger, true);
|
|
59
|
+
})
|
|
45
60
|
.then(() => {
|
|
46
|
-
logger.log(this.items);
|
|
61
|
+
logger.log(this.mergeTaskPriority(taskItems, this.items));
|
|
47
62
|
cb();
|
|
48
63
|
}, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
49
64
|
}
|
|
@@ -51,7 +66,11 @@ class PlannerTaskListCommand extends GraphItemsListCommand_1.GraphItemsListComma
|
|
|
51
66
|
this
|
|
52
67
|
.getAllItems(`${this.resource}/v1.0/me/planner/tasks`, logger, true)
|
|
53
68
|
.then(() => {
|
|
54
|
-
|
|
69
|
+
taskItems = this.items;
|
|
70
|
+
return this.getAllItems(`${this.resource}/beta/me/planner/tasks`, logger, true);
|
|
71
|
+
})
|
|
72
|
+
.then(() => {
|
|
73
|
+
logger.log(this.mergeTaskPriority(taskItems, this.items));
|
|
55
74
|
cb();
|
|
56
75
|
}, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
|
|
57
76
|
}
|
|
@@ -125,6 +144,17 @@ class PlannerTaskListCommand extends GraphItemsListCommand_1.GraphItemsListComma
|
|
|
125
144
|
return Promise.resolve(group.id);
|
|
126
145
|
});
|
|
127
146
|
}
|
|
147
|
+
mergeTaskPriority(taskItems, betaTaskItems) {
|
|
148
|
+
const findBetaTask = (id) => betaTaskItems.find(task => task.id === id);
|
|
149
|
+
taskItems.forEach(task => {
|
|
150
|
+
const betaTaskItem = findBetaTask(task.id);
|
|
151
|
+
if (betaTaskItem) {
|
|
152
|
+
const { priority } = betaTaskItem;
|
|
153
|
+
Object.assign(task, { priority });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return taskItems;
|
|
157
|
+
}
|
|
128
158
|
options() {
|
|
129
159
|
const options = [
|
|
130
160
|
{
|
|
@@ -22,7 +22,7 @@ class SpoGroupUserAddCommand extends SpoCommand_1.default {
|
|
|
22
22
|
.getGroupId(args)
|
|
23
23
|
.then((_groupId) => {
|
|
24
24
|
groupId = _groupId;
|
|
25
|
-
return this.
|
|
25
|
+
return this.getValidUsers(args, logger);
|
|
26
26
|
})
|
|
27
27
|
.then((resolvedUsernameList) => {
|
|
28
28
|
if (this.verbose) {
|
|
@@ -73,13 +73,15 @@ class SpoGroupUserAddCommand extends SpoCommand_1.default {
|
|
|
73
73
|
return groupId;
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
getValidUsers(args, logger) {
|
|
77
77
|
if (this.verbose) {
|
|
78
|
-
logger.logToStderr(`
|
|
78
|
+
logger.logToStderr(`Checking if the specified users exist`);
|
|
79
79
|
}
|
|
80
|
-
const
|
|
80
|
+
const validUserNames = [];
|
|
81
|
+
const invalidUserNames = [];
|
|
81
82
|
const userInfo = args.options.userName ? args.options.userName : args.options.email;
|
|
82
|
-
return Promise
|
|
83
|
+
return Promise
|
|
84
|
+
.all(userInfo.split(',').map(singleUserName => {
|
|
83
85
|
const options = {
|
|
84
86
|
output: 'json',
|
|
85
87
|
debug: args.options.debug,
|
|
@@ -91,20 +93,25 @@ class SpoGroupUserAddCommand extends SpoCommand_1.default {
|
|
|
91
93
|
else {
|
|
92
94
|
options.email = singleUserName.trim();
|
|
93
95
|
}
|
|
94
|
-
return cli_1.Cli
|
|
96
|
+
return cli_1.Cli
|
|
97
|
+
.executeCommandWithOutput(AadUserGetCommand, { options: Object.assign(Object.assign({}, options), { _: [] }) })
|
|
95
98
|
.then((getUserGetOutput) => {
|
|
96
99
|
if (this.debug) {
|
|
97
100
|
logger.logToStderr(getUserGetOutput.stderr);
|
|
98
101
|
}
|
|
99
|
-
|
|
102
|
+
validUserNames.push(JSON.parse(getUserGetOutput.stdout).userPrincipalName);
|
|
100
103
|
}, (err) => {
|
|
101
104
|
if (this.debug) {
|
|
102
105
|
logger.logToStderr(err.stderr);
|
|
103
106
|
}
|
|
107
|
+
invalidUserNames.push(singleUserName);
|
|
104
108
|
});
|
|
105
109
|
}))
|
|
106
110
|
.then(() => {
|
|
107
|
-
|
|
111
|
+
if (invalidUserNames.length > 0) {
|
|
112
|
+
return Promise.reject(`Users not added to the group because the following users don't exist: ${invalidUserNames.join(', ')}`);
|
|
113
|
+
}
|
|
114
|
+
return Promise.resolve(validUserNames);
|
|
108
115
|
});
|
|
109
116
|
}
|
|
110
117
|
getFormattedUserList(activeUserList) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cli_1 = require("../../../../cli");
|
|
3
4
|
const request_1 = require("../../../../request");
|
|
4
5
|
const Utils_1 = require("../../../../Utils");
|
|
6
|
+
const AadUserGetCommand = require("../../../aad/commands/user/user-get");
|
|
5
7
|
const GraphCommand_1 = require("../../../base/GraphCommand");
|
|
6
8
|
const commands_1 = require("../../commands");
|
|
7
9
|
class TeamsAppInstallCommand extends GraphCommand_1.default {
|
|
@@ -9,33 +11,70 @@ class TeamsAppInstallCommand extends GraphCommand_1.default {
|
|
|
9
11
|
return commands_1.default.APP_INSTALL;
|
|
10
12
|
}
|
|
11
13
|
get description() {
|
|
12
|
-
return 'Installs
|
|
14
|
+
return 'Installs a Microsoft Teams team app from the catalog in the specified team or for the specified user';
|
|
13
15
|
}
|
|
14
16
|
commandAction(logger, args, cb) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
responseType: 'json',
|
|
23
|
-
data: {
|
|
24
|
-
'teamsApp@odata.bind': `${endpoint}/appCatalogs/teamsApps/${args.options.appId}`
|
|
17
|
+
this
|
|
18
|
+
.validateUser(args, logger)
|
|
19
|
+
.then(_ => {
|
|
20
|
+
var _a;
|
|
21
|
+
let url = `${this.resource}/v1.0`;
|
|
22
|
+
if (args.options.teamId) {
|
|
23
|
+
url += `/teams/${encodeURIComponent(args.options.teamId)}/installedApps`;
|
|
25
24
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
else {
|
|
26
|
+
url += `/users/${encodeURIComponent(((_a = args.options.userId) !== null && _a !== void 0 ? _a : args.options.userName))}/teamwork/installedApps`;
|
|
27
|
+
}
|
|
28
|
+
const requestOptions = {
|
|
29
|
+
url: url,
|
|
30
|
+
headers: {
|
|
31
|
+
'content-type': 'application/json;odata=nometadata',
|
|
32
|
+
'accept': 'application/json;odata.metadata=none'
|
|
33
|
+
},
|
|
34
|
+
responseType: 'json',
|
|
35
|
+
data: {
|
|
36
|
+
'teamsApp@odata.bind': `${this.resource}/v1.0/appCatalogs/teamsApps/${args.options.appId}`
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return request_1.default.post(requestOptions);
|
|
40
|
+
})
|
|
29
41
|
.then(_ => cb(), (res) => this.handleRejectedODataJsonPromise(res, logger, cb));
|
|
30
42
|
}
|
|
43
|
+
// we need this method, because passing an invalid user ID to the API
|
|
44
|
+
// won't cause an error
|
|
45
|
+
validateUser(args, logger) {
|
|
46
|
+
if (!args.options.userId) {
|
|
47
|
+
return Promise.resolve(true);
|
|
48
|
+
}
|
|
49
|
+
if (this.verbose) {
|
|
50
|
+
logger.logToStderr(`Checking if user ${args.options.userId} exists...`);
|
|
51
|
+
}
|
|
52
|
+
const options = {
|
|
53
|
+
id: args.options.userId,
|
|
54
|
+
output: 'json',
|
|
55
|
+
debug: args.options.debug,
|
|
56
|
+
verbose: args.options.verbose
|
|
57
|
+
};
|
|
58
|
+
return cli_1.Cli
|
|
59
|
+
.executeCommandWithOutput(AadUserGetCommand, { options: Object.assign(Object.assign({}, options), { _: [] }) })
|
|
60
|
+
.then((res) => {
|
|
61
|
+
if (this.verbose) {
|
|
62
|
+
logger.logToStderr(res.stderr);
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}, (err) => {
|
|
66
|
+
if (this.verbose) {
|
|
67
|
+
logger.logToStderr(err.stderr);
|
|
68
|
+
}
|
|
69
|
+
return Promise.reject(`User with ID ${args.options.userId} not found. Original error: ${err.error.message}`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
31
72
|
options() {
|
|
32
73
|
const options = [
|
|
33
|
-
{
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
option: '--teamId <teamId>'
|
|
38
|
-
}
|
|
74
|
+
{ option: '--appId <appId>' },
|
|
75
|
+
{ option: '--teamId [teamId' },
|
|
76
|
+
{ option: '--userId [userId]' },
|
|
77
|
+
{ option: '--userName [userName]' }
|
|
39
78
|
];
|
|
40
79
|
const parentOptions = super.options();
|
|
41
80
|
return options.concat(parentOptions);
|
|
@@ -44,9 +83,24 @@ class TeamsAppInstallCommand extends GraphCommand_1.default {
|
|
|
44
83
|
if (!Utils_1.default.isValidGuid(args.options.appId)) {
|
|
45
84
|
return `${args.options.appId} is not a valid GUID`;
|
|
46
85
|
}
|
|
47
|
-
if (!
|
|
86
|
+
if (!args.options.teamId &&
|
|
87
|
+
!args.options.userId &&
|
|
88
|
+
!args.options.userName) {
|
|
89
|
+
return `Specify either teamId, userId or userName`;
|
|
90
|
+
}
|
|
91
|
+
if ((args.options.teamId && args.options.userId) ||
|
|
92
|
+
(args.options.teamId && args.options.userName) ||
|
|
93
|
+
(args.options.userId && args.options.userName)) {
|
|
94
|
+
return `Specify either teamId, userId or userName but not multiple`;
|
|
95
|
+
}
|
|
96
|
+
if (args.options.teamId &&
|
|
97
|
+
!Utils_1.default.isValidGuid(args.options.teamId)) {
|
|
48
98
|
return `${args.options.teamId} is not a valid GUID`;
|
|
49
99
|
}
|
|
100
|
+
if (args.options.userId &&
|
|
101
|
+
!Utils_1.default.isValidGuid(args.options.userId)) {
|
|
102
|
+
return `${args.options.userId} is not a valid GUID`;
|
|
103
|
+
}
|
|
50
104
|
return true;
|
|
51
105
|
}
|
|
52
106
|
}
|
|
@@ -21,6 +21,7 @@ class TeamsChannelGetCommand extends GraphCommand_1.default {
|
|
|
21
21
|
telemetryProps.teamName = typeof args.options.teamName !== 'undefined';
|
|
22
22
|
telemetryProps.channelId = typeof args.options.channelId !== 'undefined';
|
|
23
23
|
telemetryProps.channelName = typeof args.options.channelName !== 'undefined';
|
|
24
|
+
telemetryProps.primary = (!(!args.options.primary)).toString();
|
|
24
25
|
return telemetryProps;
|
|
25
26
|
}
|
|
26
27
|
getTeamId(args) {
|
|
@@ -54,6 +55,9 @@ class TeamsChannelGetCommand extends GraphCommand_1.default {
|
|
|
54
55
|
if (args.options.channelId) {
|
|
55
56
|
return Promise.resolve(args.options.channelId);
|
|
56
57
|
}
|
|
58
|
+
if (args.options.primary) {
|
|
59
|
+
return Promise.resolve('');
|
|
60
|
+
}
|
|
57
61
|
const channelRequestOptions = {
|
|
58
62
|
url: `${this.resource}/v1.0/teams/${encodeURIComponent(this.teamId)}/channels?$filter=displayName eq '${encodeURIComponent(args.options.channelName)}'`,
|
|
59
63
|
headers: {
|
|
@@ -79,15 +83,21 @@ class TeamsChannelGetCommand extends GraphCommand_1.default {
|
|
|
79
83
|
return this.getChannelId(args);
|
|
80
84
|
})
|
|
81
85
|
.then((channelId) => {
|
|
86
|
+
let url = '';
|
|
87
|
+
if (args.options.primary) {
|
|
88
|
+
url = `${this.resource}/v1.0/teams/${encodeURIComponent(this.teamId)}/primaryChannel`;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
url = `${this.resource}/v1.0/teams/${encodeURIComponent(this.teamId)}/channels/${encodeURIComponent(channelId)}`;
|
|
92
|
+
}
|
|
82
93
|
const requestOptions = {
|
|
83
|
-
url:
|
|
94
|
+
url: url,
|
|
84
95
|
headers: {
|
|
85
96
|
accept: 'application/json;odata.metadata=none'
|
|
86
97
|
},
|
|
87
98
|
responseType: 'json'
|
|
88
99
|
};
|
|
89
|
-
return request_1.default
|
|
90
|
-
.get(requestOptions);
|
|
100
|
+
return request_1.default.get(requestOptions);
|
|
91
101
|
})
|
|
92
102
|
.then((res) => {
|
|
93
103
|
logger.log(res);
|
|
@@ -107,6 +117,9 @@ class TeamsChannelGetCommand extends GraphCommand_1.default {
|
|
|
107
117
|
},
|
|
108
118
|
{
|
|
109
119
|
option: '--channelName [channelName]'
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
option: '--primary'
|
|
110
123
|
}
|
|
111
124
|
];
|
|
112
125
|
const parentOptions = super.options();
|
|
@@ -122,11 +135,20 @@ class TeamsChannelGetCommand extends GraphCommand_1.default {
|
|
|
122
135
|
if (args.options.teamId && !Utils_1.default.isValidGuid(args.options.teamId)) {
|
|
123
136
|
return `${args.options.teamId} is not a valid GUID`;
|
|
124
137
|
}
|
|
125
|
-
if (args.options.channelId && args.options.channelName) {
|
|
126
|
-
return 'Specify
|
|
138
|
+
if (args.options.channelId && args.options.channelName && args.options.primary) {
|
|
139
|
+
return 'Specify channelId, channelName or primary';
|
|
140
|
+
}
|
|
141
|
+
if (!args.options.channelId && args.options.channelName && args.options.primary) {
|
|
142
|
+
return 'Specify channelId, channelName or primary.';
|
|
143
|
+
}
|
|
144
|
+
if (args.options.channelId && !args.options.channelName && args.options.primary) {
|
|
145
|
+
return 'Specify channelId, channelName or primary.';
|
|
146
|
+
}
|
|
147
|
+
if (args.options.channelId && args.options.channelName && !args.options.primary) {
|
|
148
|
+
return 'Specify channelId, channelName or primary.';
|
|
127
149
|
}
|
|
128
|
-
if (!args.options.channelId && !args.options.channelName) {
|
|
129
|
-
return 'Specify channelId or
|
|
150
|
+
if (!args.options.channelId && !args.options.channelName && !args.options.primary) {
|
|
151
|
+
return 'Specify channelId, channelName or primary, one is required';
|
|
130
152
|
}
|
|
131
153
|
if (args.options.channelId && !Utils_1.default.isValidTeamsChannelId(args.options.channelId)) {
|
|
132
154
|
return `${args.options.channelId} is not a valid Teams ChannelId`;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const os = require("os");
|
|
13
|
+
const Auth_1 = require("../../../../Auth");
|
|
14
|
+
const request_1 = require("../../../../request");
|
|
15
|
+
const Utils_1 = require("../../../../Utils");
|
|
16
|
+
const GraphCommand_1 = require("../../../base/GraphCommand");
|
|
17
|
+
const commands_1 = require("../../commands");
|
|
18
|
+
class TeamsChatMessageSendCommand extends GraphCommand_1.default {
|
|
19
|
+
get name() {
|
|
20
|
+
return commands_1.default.CHAT_MESSAGE_SEND;
|
|
21
|
+
}
|
|
22
|
+
get description() {
|
|
23
|
+
return 'Send a message to an existing or new chat conversation.';
|
|
24
|
+
}
|
|
25
|
+
commandAction(logger, args, cb) {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
try {
|
|
28
|
+
const chatId = args.options.chatId
|
|
29
|
+
|| args.options.userEmails && (yield this.ensureChatIdByUserEmails(args.options.userEmails))
|
|
30
|
+
|| args.options.chatName && (yield this.getChatIdByName(args.options.chatName));
|
|
31
|
+
yield this.sendChatMessage(chatId, args.options);
|
|
32
|
+
cb();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
this.handleRejectedODataJsonPromise(error, logger, cb);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
options() {
|
|
40
|
+
const options = [
|
|
41
|
+
{
|
|
42
|
+
option: '--chatId [chatId]'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
option: '-e, --userEmails [userEmails]'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
option: '--chatName [chatName]'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
option: '-m, --message <message>'
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
const parentOptions = super.options();
|
|
55
|
+
return options.concat(parentOptions);
|
|
56
|
+
}
|
|
57
|
+
validate(args) {
|
|
58
|
+
if (!args.options.chatId && !args.options.userEmails && !args.options.chatName) {
|
|
59
|
+
return 'Specify chatId or userEmails or chatName, one is required.';
|
|
60
|
+
}
|
|
61
|
+
let nrOfMutuallyExclusiveOptionsInUse = 0;
|
|
62
|
+
if (args.options.chatId) {
|
|
63
|
+
nrOfMutuallyExclusiveOptionsInUse++;
|
|
64
|
+
}
|
|
65
|
+
if (args.options.userEmails) {
|
|
66
|
+
nrOfMutuallyExclusiveOptionsInUse++;
|
|
67
|
+
}
|
|
68
|
+
if (args.options.chatName) {
|
|
69
|
+
nrOfMutuallyExclusiveOptionsInUse++;
|
|
70
|
+
}
|
|
71
|
+
if (nrOfMutuallyExclusiveOptionsInUse > 1) {
|
|
72
|
+
return 'Specify either chatId or userEmails or chatName, but not multiple.';
|
|
73
|
+
}
|
|
74
|
+
if (!args.options.message) {
|
|
75
|
+
return 'Specify a message to send.';
|
|
76
|
+
}
|
|
77
|
+
if (args.options.chatId && !Utils_1.default.isValidTeamsChatId(args.options.chatId)) {
|
|
78
|
+
return `${args.options.chatId} is not a valid Teams ChatId.`;
|
|
79
|
+
}
|
|
80
|
+
if (args.options.userEmails) {
|
|
81
|
+
const userEmails = args.options.userEmails.toLowerCase().replace(/\s/g, '').split(',').filter(e => e && e !== '');
|
|
82
|
+
if (!userEmails || userEmails.length === 0 || userEmails.some(e => !Utils_1.default.isValidUserPrincipalName(e))) {
|
|
83
|
+
return `${args.options.userEmails} contains one or more invalid email addresses.`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
ensureChatIdByUserEmails(userEmailsOption) {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
const userEmails = userEmailsOption.toLowerCase().replace(/\s/g, '').split(',').filter(e => e && e !== '');
|
|
91
|
+
const currentUserEmail = Utils_1.default.getUserNameFromAccessToken(Auth_1.default.service.accessTokens[this.resource].accessToken).toLowerCase();
|
|
92
|
+
const existingChats = yield this.findExistingGroupChatsByMembers([currentUserEmail, ...userEmails]);
|
|
93
|
+
if (existingChats && existingChats.length > 0) {
|
|
94
|
+
if (existingChats.length > 1) {
|
|
95
|
+
const disambiguationText = existingChats.map(c => {
|
|
96
|
+
return `- ${c.id}${c.topic && ' - '}${c.topic} - ${c.createdDateTime && new Date(c.createdDateTime).toLocaleString()}`;
|
|
97
|
+
}).join(os.EOL);
|
|
98
|
+
throw new Error(`Multiple chat conversations with this topic found. Please disambiguate:${os.EOL}${disambiguationText}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
return existingChats[0].id;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const chat = yield this.createConversation([currentUserEmail, ...userEmails]);
|
|
105
|
+
return chat.id;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
getChatIdByName(chatName) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const existingChats = yield this.findExistingGroupChatsByTopic(chatName);
|
|
111
|
+
if (!existingChats || existingChats.length === 0) {
|
|
112
|
+
throw new Error('No chat conversation was found with this name.');
|
|
113
|
+
}
|
|
114
|
+
if (existingChats.length === 1) {
|
|
115
|
+
return existingChats[0].id;
|
|
116
|
+
}
|
|
117
|
+
const disambiguationText = existingChats.map(c => {
|
|
118
|
+
const memberstring = c.members.map(m => m.email).join(', ');
|
|
119
|
+
return `- ${c.id} - ${c.createdDateTime && new Date(c.createdDateTime).toLocaleString()} - ${memberstring}`;
|
|
120
|
+
}).join(os.EOL);
|
|
121
|
+
throw new Error(`Multiple chat conversations with this topic found. Please disambiguate:${os.EOL}${disambiguationText}`);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
// This Microsoft Graph API request throws an intermittent 404 exception, saying that it cannot find the principal.
|
|
125
|
+
// The same behavior occurs when creating the conversation through the Graph Explorer.
|
|
126
|
+
// It seems to happen when the userEmail casing does not match the casing of the actual UPN.
|
|
127
|
+
// When the first request throws an error, the second request does succeed.
|
|
128
|
+
// Therefore a retry-mechanism is implemented here.
|
|
129
|
+
createConversation(memberEmails, retried = 0) {
|
|
130
|
+
var _a;
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
try {
|
|
133
|
+
const jsonBody = {
|
|
134
|
+
chatType: memberEmails.length > 2 ? 'group' : 'oneOnOne',
|
|
135
|
+
members: memberEmails.map(email => {
|
|
136
|
+
return {
|
|
137
|
+
'@odata.type': '#microsoft.graph.aadUserConversationMember',
|
|
138
|
+
roles: ['owner'],
|
|
139
|
+
'user@odata.bind': `https://graph.microsoft.com/v1.0/users/${email}`
|
|
140
|
+
};
|
|
141
|
+
})
|
|
142
|
+
};
|
|
143
|
+
const requestOptions = {
|
|
144
|
+
url: `${this.resource}/v1.0/chats`,
|
|
145
|
+
headers: {
|
|
146
|
+
accept: 'application/json;odata.metadata=none',
|
|
147
|
+
'content-type': 'application/json;odata=nometadata'
|
|
148
|
+
},
|
|
149
|
+
responseType: 'json',
|
|
150
|
+
data: jsonBody
|
|
151
|
+
};
|
|
152
|
+
return yield request_1.default.post(requestOptions);
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
if (((_a = err.message) === null || _a === void 0 ? void 0 : _a.indexOf('404')) > -1 && retried < 4) {
|
|
156
|
+
return yield this.createConversation(memberEmails, retried + 1);
|
|
157
|
+
}
|
|
158
|
+
throw err;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
sendChatMessage(chatId, options) {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
const requestOptions = {
|
|
165
|
+
url: `${this.resource}/v1.0/chats/${chatId}/messages`,
|
|
166
|
+
headers: {
|
|
167
|
+
accept: 'application/json;odata.metadata=none',
|
|
168
|
+
'content-type': 'application/json;odata=nometadata'
|
|
169
|
+
},
|
|
170
|
+
responseType: 'json',
|
|
171
|
+
data: {
|
|
172
|
+
body: {
|
|
173
|
+
content: options.message
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
yield request_1.default.post(requestOptions);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
findExistingGroupChatsByMembers(expectedMemberEmails) {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
const endpoint = `${this.resource}/v1.0/chats?$filter=chatType eq 'group'&$expand=members&$select=id,topic,createdDateTime,members`;
|
|
183
|
+
const foundChats = [];
|
|
184
|
+
const chats = yield this.getAllChats(endpoint, []);
|
|
185
|
+
for (const chat of chats) {
|
|
186
|
+
const chatMembers = chat.members;
|
|
187
|
+
if (chatMembers.length === expectedMemberEmails.length) {
|
|
188
|
+
const chatMemberEmails = chatMembers.map(member => { var _a; return (_a = member.email) === null || _a === void 0 ? void 0 : _a.toLowerCase(); });
|
|
189
|
+
if (expectedMemberEmails.every(email => chatMemberEmails.some(memberEmail => memberEmail === email))) {
|
|
190
|
+
foundChats.push(chat);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return foundChats;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
findExistingGroupChatsByTopic(topic) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const endpoint = `${this.resource}/v1.0/chats?$filter=topic eq '${encodeURIComponent(topic)}'&$expand=members&$select=id,topic,createdDateTime,chatType`;
|
|
200
|
+
const chats = yield this.getAllChats(endpoint, []);
|
|
201
|
+
return chats;
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
getAllChats(url, items) {
|
|
205
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
const requestOptions = {
|
|
207
|
+
url: url,
|
|
208
|
+
headers: {
|
|
209
|
+
accept: 'application/json;odata.metadata=none'
|
|
210
|
+
},
|
|
211
|
+
responseType: 'json'
|
|
212
|
+
};
|
|
213
|
+
const res = yield request_1.default.get(requestOptions);
|
|
214
|
+
items = items.concat(res.value);
|
|
215
|
+
if (res['@odata.nextLink']) {
|
|
216
|
+
return yield this.getAllChats(res['@odata.nextLink'], items);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
return items;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
module.exports = new TeamsChatMessageSendCommand();
|
|
225
|
+
//# sourceMappingURL=chat-message-send.js.map
|
|
@@ -16,6 +16,7 @@ exports.default = {
|
|
|
16
16
|
CHAT_LIST: `${prefix} chat list`,
|
|
17
17
|
CHAT_MEMBER_LIST: `${prefix} chat member list`,
|
|
18
18
|
CHAT_MESSAGE_LIST: `${prefix} chat message list`,
|
|
19
|
+
CHAT_MESSAGE_SEND: `${prefix} chat message send`,
|
|
19
20
|
CONVERSATIONMEMBER_ADD: `${prefix} conversationmember add`,
|
|
20
21
|
CONVERSATIONMEMBER_LIST: `${prefix} conversationmember list`,
|
|
21
22
|
FUNSETTINGS_LIST: `${prefix} funsettings list`,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# aad app delete
|
|
2
|
+
|
|
3
|
+
Removes an Azure AD app registration
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
m365 aad app delete [options]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Options
|
|
12
|
+
|
|
13
|
+
`--appId [appId]`
|
|
14
|
+
: Application (client) ID of the Azure AD application registration to remove. Specify either `appId`, `objectId` or `name`
|
|
15
|
+
|
|
16
|
+
`--objectId [objectId]`
|
|
17
|
+
: Object ID of the Azure AD application registration to remove. Specify either `appId`, `objectId` or `name`
|
|
18
|
+
|
|
19
|
+
`--name [name]`
|
|
20
|
+
: Name of the Azure AD application registration to remove. Specify either `appId`, `objectId` or `name`
|
|
21
|
+
|
|
22
|
+
`--confirm`:
|
|
23
|
+
: Don't prompt for confirmation to delete the app
|
|
24
|
+
|
|
25
|
+
--8<-- "docs/cmd/_global.md"
|
|
26
|
+
|
|
27
|
+
## Remarks
|
|
28
|
+
|
|
29
|
+
For best performance use the `objectId` option to reference the Azure AD application registration to delete. If you use `appId` or `name`, this command will first need to find the corresponding object ID for that application.
|
|
30
|
+
|
|
31
|
+
If the command finds multiple Azure AD application registrations with the specified app name, it will prompt you to disambiguate which app it should use, listing the discovered object IDs.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
Delete the Azure AD application registration by its app (client) ID
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
m365 aad app delete --appId d75be2e1-0204-4f95-857d-51a37cf40be8
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Delete the Azure AD application registration by its object ID
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
m365 aad app delete --objectId d75be2e1-0204-4f95-857d-51a37cf40be8
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Delete the Azure AD application registration by its name. Will NOT prompt for confirmation before deleting.
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
m365 aad app delete --name "My app" --confirm
|
|
51
|
+
```
|
|
@@ -19,14 +19,19 @@ m365 aad app get [options]
|
|
|
19
19
|
`--name [name]`
|
|
20
20
|
: Name of the Azure AD application registration to get. Specify either `appId`, `objectId` or `name`
|
|
21
21
|
|
|
22
|
+
`--save`
|
|
23
|
+
: Use to store the information about the created app in a local file
|
|
24
|
+
|
|
22
25
|
--8<-- "docs/cmd/_global.md"
|
|
23
26
|
|
|
24
27
|
## Remarks
|
|
25
28
|
|
|
26
|
-
For best performance use the `objectId` option to reference the Azure AD application registration to
|
|
29
|
+
For best performance use the `objectId` option to reference the Azure AD application registration to get. If you use `appId` or `name`, this command will first need to find the corresponding object ID for that application.
|
|
27
30
|
|
|
28
31
|
If the command finds multiple Azure AD application registrations with the specified app name, it will prompt you to disambiguate which app it should use, listing the discovered object IDs.
|
|
29
32
|
|
|
33
|
+
If you want to store the information about the Azure AD app registration, use the `--save` option. This is useful when you build solutions connected to Microsoft 365 and want to easily manage app registrations used with your solution. When you use the `--save` option, after you get the app registration, the command will write its ID and name to the `.m365rc.json` file in the current directory. If the file already exists, it will add the information about the App registration to it if it's not already present, allowing you to track multiple apps. If the file doesn't exist, the command will create it.
|
|
34
|
+
|
|
30
35
|
## Examples
|
|
31
36
|
|
|
32
37
|
Get the Azure AD application registration by its app (client) ID
|
|
@@ -46,3 +51,9 @@ Get the Azure AD application registration by its name
|
|
|
46
51
|
```sh
|
|
47
52
|
m365 aad app get --name "My app"
|
|
48
53
|
```
|
|
54
|
+
|
|
55
|
+
Get the Azure AD application registration by its name. Store information about the retrieved app registration in the _.m365rc.json_ file in the current directory.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
m365 aad app get --name "My app" --save
|
|
59
|
+
```
|