@pnp/cli-microsoft365 7.8.0-beta.5f64790 → 7.8.0-beta.a83837a
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/allCommands.json +1 -1
- package/allCommandsFull.json +1 -1
- package/dist/cli/cli.js +3 -6
- package/dist/m365/entra/commands/app/app-permission-add.js +25 -6
- package/dist/m365/entra/commands/app/app-permission-list.js +17 -5
- package/dist/m365/entra/commands/group/group-add.js +12 -14
- package/dist/m365/entra/commands/group/group-user-add.js +6 -7
- package/dist/m365/entra/commands/user/user-registrationdetails-list.js +6 -7
- package/dist/m365/planner/commands/bucket/bucket-add.js +4 -7
- package/dist/m365/planner/commands/bucket/bucket-get.js +7 -30
- package/dist/m365/planner/commands/bucket/bucket-list.js +3 -6
- package/dist/m365/planner/commands/bucket/bucket-remove.js +8 -25
- package/dist/m365/planner/commands/bucket/bucket-set.js +12 -34
- package/dist/m365/planner/commands/plan/plan-add.js +17 -7
- package/dist/m365/planner/commands/plan/plan-get.js +12 -22
- package/dist/m365/planner/commands/plan/plan-list.js +1 -2
- package/dist/m365/planner/commands/plan/plan-remove.js +5 -3
- package/dist/m365/planner/commands/plan/plan-set.js +18 -10
- package/dist/m365/planner/commands/roster/roster-add.js +1 -2
- package/dist/m365/planner/commands/task/task-add.js +15 -21
- package/dist/m365/planner/commands/task/task-checklistitem-add.js +1 -1
- package/dist/m365/planner/commands/task/task-checklistitem-remove.js +1 -1
- package/dist/m365/planner/commands/task/task-get.js +5 -26
- package/dist/m365/planner/commands/task/task-list.js +11 -37
- package/dist/m365/planner/commands/task/task-reference-remove.js +1 -1
- package/dist/m365/planner/commands/task/task-remove.js +12 -29
- package/dist/m365/planner/commands/task/task-set.js +15 -21
- package/dist/m365/spo/commands/listitem/listitem-batch-remove.js +222 -0
- package/dist/m365/spo/commands/navigation/navigation-node-add.js +3 -2
- package/dist/m365/spo/commands/navigation/navigation-node-set.js +3 -2
- package/dist/m365/spo/commands/site/site-recyclebinitem-move.js +5 -2
- package/dist/m365/spo/commands/site/site-recyclebinitem-remove.js +3 -2
- package/dist/m365/spo/commands/spo-set.js +6 -2
- package/dist/m365/spo/commands.js +1 -0
- package/dist/m365/teams/commands/meeting/meeting-add.js +3 -3
- package/dist/m365/teams/commands/team/team-add.js +22 -16
- package/dist/utils/planner.js +87 -8
- package/dist/utils/validation.js +8 -5
- package/docs/docs/cmd/entra/app/app-permission-add.mdx +7 -4
- package/docs/docs/cmd/entra/app/app-permission-list.mdx +14 -5
- package/docs/docs/cmd/spo/listitem/listitem-batch-remove.mdx +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var _SpoListItemBatchRemoveCommand_instances, _SpoListItemBatchRemoveCommand_initTelemetry, _SpoListItemBatchRemoveCommand_initOptions, _SpoListItemBatchRemoveCommand_initValidators, _SpoListItemBatchRemoveCommand_initTypes, _SpoListItemBatchRemoveCommand_initOptionSets;
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import SpoCommand from '../../../base/SpoCommand.js';
|
|
10
|
+
import commands from '../../commands.js';
|
|
11
|
+
import { formatting } from '../../../../utils/formatting.js';
|
|
12
|
+
import { urlUtil } from '../../../../utils/urlUtil.js';
|
|
13
|
+
import { v4 } from 'uuid';
|
|
14
|
+
import request from '../../../../request.js';
|
|
15
|
+
import { validation } from '../../../../utils/validation.js';
|
|
16
|
+
import { cli } from '../../../../cli/cli.js';
|
|
17
|
+
class SpoListItemBatchRemoveCommand extends SpoCommand {
|
|
18
|
+
get name() {
|
|
19
|
+
return commands.LISTITEM_BATCH_REMOVE;
|
|
20
|
+
}
|
|
21
|
+
get description() {
|
|
22
|
+
return 'Removes items from a list in batch';
|
|
23
|
+
}
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
_SpoListItemBatchRemoveCommand_instances.add(this);
|
|
27
|
+
__classPrivateFieldGet(this, _SpoListItemBatchRemoveCommand_instances, "m", _SpoListItemBatchRemoveCommand_initTelemetry).call(this);
|
|
28
|
+
__classPrivateFieldGet(this, _SpoListItemBatchRemoveCommand_instances, "m", _SpoListItemBatchRemoveCommand_initOptions).call(this);
|
|
29
|
+
__classPrivateFieldGet(this, _SpoListItemBatchRemoveCommand_instances, "m", _SpoListItemBatchRemoveCommand_initValidators).call(this);
|
|
30
|
+
__classPrivateFieldGet(this, _SpoListItemBatchRemoveCommand_instances, "m", _SpoListItemBatchRemoveCommand_initTypes).call(this);
|
|
31
|
+
__classPrivateFieldGet(this, _SpoListItemBatchRemoveCommand_instances, "m", _SpoListItemBatchRemoveCommand_initOptionSets).call(this);
|
|
32
|
+
}
|
|
33
|
+
async commandAction(logger, args) {
|
|
34
|
+
const removeListItems = async () => {
|
|
35
|
+
try {
|
|
36
|
+
if (this.verbose) {
|
|
37
|
+
logger.logToStderr('Removing the listitems from SharePoint...');
|
|
38
|
+
}
|
|
39
|
+
let idsToRemove = [];
|
|
40
|
+
if (args.options.filePath) {
|
|
41
|
+
const csvContent = fs.readFileSync(args.options.filePath, 'utf-8');
|
|
42
|
+
const jsonContent = formatting.parseCsvToJson(csvContent);
|
|
43
|
+
idsToRemove = jsonContent.map((item) => item['ID']);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
idsToRemove = formatting.splitAndTrim(args.options.ids);
|
|
47
|
+
}
|
|
48
|
+
await this.removeItemsAsBatch(idsToRemove, args.options, logger);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
this.handleRejectedODataJsonPromise(err);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (args.options.force) {
|
|
55
|
+
await removeListItems();
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const result = await cli.promptForConfirmation({ message: `Are you sure you want to ${args.options.recycle ? "recycle" : "remove"} the list items from list ${args.options.listId || args.options.listTitle || args.options.listUrl} located in site ${args.options.webUrl}?` });
|
|
59
|
+
if (result) {
|
|
60
|
+
await removeListItems();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async removeItemsAsBatch(items, options, logger) {
|
|
65
|
+
const itemsChunks = this.getChunkedArray(items, 10);
|
|
66
|
+
for (const [index, chunk] of itemsChunks.entries()) {
|
|
67
|
+
if (this.verbose) {
|
|
68
|
+
await logger.logToStderr(`Processing chunk ${index + 1} of ${itemsChunks.length}...`);
|
|
69
|
+
}
|
|
70
|
+
await this.postBatchData(chunk, options.webUrl, options);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async postBatchData(chunk, webUrl, options) {
|
|
74
|
+
const batchId = v4();
|
|
75
|
+
const requestBody = this.getRequestBody(chunk, batchId, options);
|
|
76
|
+
const requestOptions = {
|
|
77
|
+
url: `${webUrl}/_api/$batch`,
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': `multipart/mixed; boundary=batch_${batchId}`,
|
|
80
|
+
'Accept': 'application/json;odata=verbose'
|
|
81
|
+
},
|
|
82
|
+
data: requestBody.join('')
|
|
83
|
+
};
|
|
84
|
+
const response = await request.post(requestOptions);
|
|
85
|
+
const errors = this.parseBatchResponseBody(response, chunk);
|
|
86
|
+
if (errors.length > 0) {
|
|
87
|
+
throw `Creating some items failed with the following errors: ${os.EOL}${errors.map(error => { return `- ${error}`; }).join(os.EOL)}`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
getRequestBody(chunk, batchId, options) {
|
|
91
|
+
const changeSetId = v4();
|
|
92
|
+
const batchBody = [];
|
|
93
|
+
batchBody.push(`--batch_${batchId}\n`);
|
|
94
|
+
batchBody.push(`Content-Type: multipart/mixed; boundary="changeset_${changeSetId}"\n\n`);
|
|
95
|
+
batchBody.push('Content-Transfer-Encoding: binary\n\n');
|
|
96
|
+
for (const item of chunk) {
|
|
97
|
+
const actionUrl = this.getActionUrl(options, item);
|
|
98
|
+
batchBody.push(`--changeset_${changeSetId}\n`);
|
|
99
|
+
batchBody.push('Content-Type: application/http\n');
|
|
100
|
+
batchBody.push('Content-Transfer-Encoding: binary\n\n');
|
|
101
|
+
batchBody.push(`DELETE ${actionUrl} HTTP/1.1\n`);
|
|
102
|
+
batchBody.push(`Accept: application/json;odata=nometadata\n`);
|
|
103
|
+
batchBody.push(`If-Match: *\n\n`);
|
|
104
|
+
}
|
|
105
|
+
batchBody.push(`\n\n`);
|
|
106
|
+
batchBody.push(`--changeset_${changeSetId}--\n\n`);
|
|
107
|
+
batchBody.push(`--batch_${batchId}--\n`);
|
|
108
|
+
return batchBody;
|
|
109
|
+
}
|
|
110
|
+
parseBatchResponseBody(response, chunk) {
|
|
111
|
+
const errors = [];
|
|
112
|
+
response.split('\r\n')
|
|
113
|
+
.filter((line) => line.startsWith('{'))
|
|
114
|
+
.forEach((line, index) => {
|
|
115
|
+
const parsedResponse = JSON.parse(line);
|
|
116
|
+
if (parsedResponse.error) {
|
|
117
|
+
const error = parsedResponse.error;
|
|
118
|
+
errors.push(`Item ID ${chunk[index]}: ${error.message.value}`);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return errors;
|
|
122
|
+
}
|
|
123
|
+
;
|
|
124
|
+
getChunkedArray(inputArray, chunkSize) {
|
|
125
|
+
const result = [];
|
|
126
|
+
for (let i = 0; i < inputArray.length; i += chunkSize) {
|
|
127
|
+
result.push(inputArray.slice(i, i + chunkSize));
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
getActionUrl(options, item) {
|
|
132
|
+
let requestUrl = '';
|
|
133
|
+
if (options.listId) {
|
|
134
|
+
requestUrl += `lists(guid'${formatting.encodeQueryParameter(options.listId)}')`;
|
|
135
|
+
}
|
|
136
|
+
else if (options.listTitle) {
|
|
137
|
+
requestUrl += `lists/getByTitle('${formatting.encodeQueryParameter(options.listTitle)}')`;
|
|
138
|
+
}
|
|
139
|
+
else if (options.listUrl) {
|
|
140
|
+
const listServerRelativeUrl = urlUtil.getServerRelativePath(options.webUrl, options.listUrl);
|
|
141
|
+
requestUrl += `GetList('${formatting.encodeQueryParameter(listServerRelativeUrl)}')`;
|
|
142
|
+
}
|
|
143
|
+
requestUrl += `/items(${item})`;
|
|
144
|
+
if (options.recycle) {
|
|
145
|
+
requestUrl += '/recycle()';
|
|
146
|
+
}
|
|
147
|
+
return requestUrl;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
_SpoListItemBatchRemoveCommand_instances = new WeakSet(), _SpoListItemBatchRemoveCommand_initTelemetry = function _SpoListItemBatchRemoveCommand_initTelemetry() {
|
|
151
|
+
this.telemetry.push((args) => {
|
|
152
|
+
Object.assign(this.telemetryProperties, {
|
|
153
|
+
filePath: typeof args.options.filePath !== 'undefined',
|
|
154
|
+
ids: typeof args.options.ids !== 'undefined',
|
|
155
|
+
listId: typeof args.options.listId !== 'undefined',
|
|
156
|
+
listTitle: typeof args.options.listTitle !== 'undefined',
|
|
157
|
+
listUrl: typeof args.options.listUrl !== 'undefined',
|
|
158
|
+
recycle: !!args.options.recycle,
|
|
159
|
+
force: !!args.options.force
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}, _SpoListItemBatchRemoveCommand_initOptions = function _SpoListItemBatchRemoveCommand_initOptions() {
|
|
163
|
+
this.options.unshift({
|
|
164
|
+
option: '-u, --webUrl <webUrl>'
|
|
165
|
+
}, {
|
|
166
|
+
option: '-l, --listId [listId]'
|
|
167
|
+
}, {
|
|
168
|
+
option: '-t, --listTitle [listTitle]'
|
|
169
|
+
}, {
|
|
170
|
+
option: '--listUrl [listUrl]'
|
|
171
|
+
}, {
|
|
172
|
+
option: '-p, --filePath [filePath]'
|
|
173
|
+
}, {
|
|
174
|
+
option: '-i, --ids [ids]'
|
|
175
|
+
}, {
|
|
176
|
+
option: '-r, --recycle'
|
|
177
|
+
}, {
|
|
178
|
+
option: '-f, --force'
|
|
179
|
+
});
|
|
180
|
+
}, _SpoListItemBatchRemoveCommand_initValidators = function _SpoListItemBatchRemoveCommand_initValidators() {
|
|
181
|
+
this.validators.push(async (args) => {
|
|
182
|
+
const isValidSharePointUrl = validation.isValidSharePointUrl(args.options.webUrl);
|
|
183
|
+
if (isValidSharePointUrl !== true) {
|
|
184
|
+
return isValidSharePointUrl;
|
|
185
|
+
}
|
|
186
|
+
if (args.options.listId &&
|
|
187
|
+
!validation.isValidGuid(args.options.listId)) {
|
|
188
|
+
return `${args.options.listId} in option listId is not a valid GUID.`;
|
|
189
|
+
}
|
|
190
|
+
if (args.options.filePath) {
|
|
191
|
+
if (!fs.existsSync(args.options.filePath)) {
|
|
192
|
+
return `File with path ${args.options.filePath} does not exist.`;
|
|
193
|
+
}
|
|
194
|
+
const fileContent = fs.readFileSync(args.options.filePath, 'utf-8');
|
|
195
|
+
const jsonContent = formatting.parseCsvToJson(fileContent);
|
|
196
|
+
if (!jsonContent[0].hasOwnProperty('ID')) {
|
|
197
|
+
return `The file does not contain the required header row with the column name 'ID'.`;
|
|
198
|
+
}
|
|
199
|
+
const nonNumbers = jsonContent.filter(element => isNaN(Number(element['ID'].toString().trim()))).map(element => element['ID']);
|
|
200
|
+
if (nonNumbers.length > 0) {
|
|
201
|
+
return `The specified ids '${nonNumbers.join(', ')}' are invalid numbers.`;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (args.options.ids) {
|
|
205
|
+
const nonNumbers = formatting.splitAndTrim(args.options.ids).filter(element => isNaN(Number(element)));
|
|
206
|
+
if (nonNumbers.length > 0) {
|
|
207
|
+
return `The specified ids '${nonNumbers.join(', ')}' are invalid numbers.`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
});
|
|
212
|
+
}, _SpoListItemBatchRemoveCommand_initTypes = function _SpoListItemBatchRemoveCommand_initTypes() {
|
|
213
|
+
this.types.string.push('webUrl', 'listId', 'listTitle', 'listUrl', 'ids', 'filePath');
|
|
214
|
+
}, _SpoListItemBatchRemoveCommand_initOptionSets = function _SpoListItemBatchRemoveCommand_initOptionSets() {
|
|
215
|
+
this.optionSets.push({
|
|
216
|
+
options: ['listId', 'listTitle', 'listUrl']
|
|
217
|
+
}, {
|
|
218
|
+
options: ['filePath', 'ids']
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
export default new SpoListItemBatchRemoveCommand();
|
|
222
|
+
//# sourceMappingURL=listitem-batch-remove.js.map
|
|
@@ -135,8 +135,9 @@ _SpoNavigationNodeAddCommand_instances = new WeakSet(), _SpoNavigationNodeAddCom
|
|
|
135
135
|
if (audienceIdsSplitted.length > 10) {
|
|
136
136
|
return 'The maximum amount of audienceIds per navigation node exceeded. The maximum amount of auciendeIds is 10.';
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.audienceIds);
|
|
139
|
+
if (isValidGUIDArrayResult !== true) {
|
|
140
|
+
return `The following GUIDs are invalid for the option 'audienceIds': ${isValidGUIDArrayResult}.`;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
return true;
|
|
@@ -127,8 +127,9 @@ _SpoNavigationNodeSetCommand_instances = new WeakSet(), _SpoNavigationNodeSetCom
|
|
|
127
127
|
if (audienceIdsSplitted.length > 10) {
|
|
128
128
|
return 'The maximum amount of audienceIds per navigation node exceeded. The maximum amount of audienceIds is 10.';
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.audienceIds);
|
|
131
|
+
if (isValidGUIDArrayResult !== true) {
|
|
132
|
+
return `The following GUIDs are invalid for the option 'audienceIds': ${isValidGUIDArrayResult}.`;
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
return true;
|
|
@@ -89,8 +89,11 @@ _SpoSiteRecycleBinItemMoveCommand_instances = new WeakSet(), _SpoSiteRecycleBinI
|
|
|
89
89
|
if (isValidSharePointUrl !== true) {
|
|
90
90
|
return isValidSharePointUrl;
|
|
91
91
|
}
|
|
92
|
-
if (args.options.ids
|
|
93
|
-
|
|
92
|
+
if (args.options.ids) {
|
|
93
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.ids);
|
|
94
|
+
if (isValidGUIDArrayResult !== true) {
|
|
95
|
+
return `The following GUIDs are invalid for the option 'ids': ${isValidGUIDArrayResult}.`;
|
|
96
|
+
}
|
|
94
97
|
}
|
|
95
98
|
return true;
|
|
96
99
|
});
|
|
@@ -76,8 +76,9 @@ _SpoSiteRecycleBinItemRemoveCommand_instances = new WeakSet(), _SpoSiteRecycleBi
|
|
|
76
76
|
if (isValidSharePointUrl !== true) {
|
|
77
77
|
return isValidSharePointUrl;
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.ids);
|
|
80
|
+
if (isValidGUIDArrayResult !== true) {
|
|
81
|
+
return `The following GUIDs are invalid for the option 'ids': ${isValidGUIDArrayResult}.`;
|
|
81
82
|
}
|
|
82
83
|
return true;
|
|
83
84
|
});
|
|
@@ -3,8 +3,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
3
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
|
-
var _SpoSetCommand_instances, _SpoSetCommand_initOptions, _SpoSetCommand_initValidators;
|
|
6
|
+
var _SpoSetCommand_instances, _SpoSetCommand_initOptions, _SpoSetCommand_initValidators, _SpoSetCommand_initTypes;
|
|
7
7
|
import auth from '../../../Auth.js';
|
|
8
|
+
import { urlUtil } from '../../../utils/urlUtil.js';
|
|
8
9
|
import { validation } from '../../../utils/validation.js';
|
|
9
10
|
import SpoCommand from '../../base/SpoCommand.js';
|
|
10
11
|
import commands from '../commands.js';
|
|
@@ -20,9 +21,10 @@ class SpoSetCommand extends SpoCommand {
|
|
|
20
21
|
_SpoSetCommand_instances.add(this);
|
|
21
22
|
__classPrivateFieldGet(this, _SpoSetCommand_instances, "m", _SpoSetCommand_initOptions).call(this);
|
|
22
23
|
__classPrivateFieldGet(this, _SpoSetCommand_instances, "m", _SpoSetCommand_initValidators).call(this);
|
|
24
|
+
__classPrivateFieldGet(this, _SpoSetCommand_instances, "m", _SpoSetCommand_initTypes).call(this);
|
|
23
25
|
}
|
|
24
26
|
async commandAction(logger, args) {
|
|
25
|
-
auth.connection.spoUrl = args.options.url;
|
|
27
|
+
auth.connection.spoUrl = urlUtil.removeTrailingSlashes(args.options.url);
|
|
26
28
|
try {
|
|
27
29
|
await auth.storeConnectionInfo();
|
|
28
30
|
}
|
|
@@ -37,6 +39,8 @@ _SpoSetCommand_instances = new WeakSet(), _SpoSetCommand_initOptions = function
|
|
|
37
39
|
});
|
|
38
40
|
}, _SpoSetCommand_initValidators = function _SpoSetCommand_initValidators() {
|
|
39
41
|
this.validators.push(async (args) => validation.isValidSharePointUrl(args.options.url));
|
|
42
|
+
}, _SpoSetCommand_initTypes = function _SpoSetCommand_initTypes() {
|
|
43
|
+
this.types.string.push('url');
|
|
40
44
|
};
|
|
41
45
|
export default new SpoSetCommand();
|
|
42
46
|
//# sourceMappingURL=spo-set.js.map
|
|
@@ -164,6 +164,7 @@ export default {
|
|
|
164
164
|
LISTITEM_ATTACHMENT_REMOVE: `${prefix} listitem attachment remove`,
|
|
165
165
|
LISTITEM_ATTACHMENT_SET: `${prefix} listitem attachment set`,
|
|
166
166
|
LISTITEM_BATCH_ADD: `${prefix} listitem batch add`,
|
|
167
|
+
LISTITEM_BATCH_REMOVE: `${prefix} listitem batch remove`,
|
|
167
168
|
LISTITEM_BATCH_SET: `${prefix} listitem batch set`,
|
|
168
169
|
LISTITEM_GET: `${prefix} listitem get`,
|
|
169
170
|
LISTITEM_ISRECORD: `${prefix} listitem isrecord`,
|
|
@@ -136,9 +136,9 @@ _TeamsMeetingAddCommand_instances = new WeakSet(), _TeamsMeetingAddCommand_initT
|
|
|
136
136
|
return 'The endTime value must be in the future.';
|
|
137
137
|
}
|
|
138
138
|
if (args.options.participantUserNames) {
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
141
|
-
return `
|
|
139
|
+
const isValidUPNArrayResult = validation.isValidUserPrincipalNameArray(args.options.participantUserNames);
|
|
140
|
+
if (isValidUPNArrayResult !== true) {
|
|
141
|
+
return `The following user principal names are invalid for the option 'participantUserNames': ${isValidUPNArrayResult}.`;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
if (args.options.organizerEmail && !validation.isValidUserPrincipalName(args.options.organizerEmail)) {
|
|
@@ -226,34 +226,40 @@ _TeamsTeamAddCommand_instances = new WeakSet(), _TeamsTeamAddCommand_initTelemet
|
|
|
226
226
|
}, _TeamsTeamAddCommand_initValidators = function _TeamsTeamAddCommand_initValidators() {
|
|
227
227
|
this.validators.push(async (args) => {
|
|
228
228
|
if (args.options.ownerUserNames) {
|
|
229
|
-
const
|
|
230
|
-
if (
|
|
231
|
-
return `
|
|
229
|
+
const isValidUPNArrayResult = validation.isValidUserPrincipalNameArray(args.options.ownerUserNames);
|
|
230
|
+
if (isValidUPNArrayResult !== true) {
|
|
231
|
+
return `The following user principal names are invalid for the option 'ownerUserNames': ${isValidUPNArrayResult}.`;
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
if (args.options.ownerEmails) {
|
|
235
|
-
const
|
|
236
|
-
if (
|
|
237
|
-
return `
|
|
235
|
+
const isValidUPNArrayResult = validation.isValidUserPrincipalNameArray(args.options.ownerEmails);
|
|
236
|
+
if (isValidUPNArrayResult !== true) {
|
|
237
|
+
return `The following user principal names are invalid for the option 'ownerEmails': ${isValidUPNArrayResult}.`;
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
|
-
if (args.options.ownerIds
|
|
241
|
-
|
|
240
|
+
if (args.options.ownerIds) {
|
|
241
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.ownerIds);
|
|
242
|
+
if (isValidGUIDArrayResult !== true) {
|
|
243
|
+
return `The following GUIDs are invalid for the option 'ownerIds': ${isValidGUIDArrayResult}.`;
|
|
244
|
+
}
|
|
242
245
|
}
|
|
243
246
|
if (args.options.memberUserNames) {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
return `
|
|
247
|
+
const isValidUPNArrayResult = validation.isValidUserPrincipalNameArray(args.options.memberUserNames);
|
|
248
|
+
if (isValidUPNArrayResult !== true) {
|
|
249
|
+
return `The following user principal names are invalid for the option 'memberUserNames': ${isValidUPNArrayResult}.`;
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
if (args.options.memberEmails) {
|
|
250
|
-
const
|
|
251
|
-
if (
|
|
252
|
-
return `
|
|
253
|
+
const isValidUPNArrayResult = validation.isValidUserPrincipalNameArray(args.options.memberEmails);
|
|
254
|
+
if (isValidUPNArrayResult !== true) {
|
|
255
|
+
return `The following user principal names are invalid for the option 'memberEmails': ${isValidUPNArrayResult}.`;
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
|
-
if (args.options.memberIds
|
|
256
|
-
|
|
258
|
+
if (args.options.memberIds) {
|
|
259
|
+
const isValidGUIDArrayResult = validation.isValidGuidArray(args.options.memberIds);
|
|
260
|
+
if (isValidGUIDArrayResult !== true) {
|
|
261
|
+
return `The following GUIDs are invalid for the option 'memberIds': ${isValidGUIDArrayResult}.`;
|
|
262
|
+
}
|
|
257
263
|
}
|
|
258
264
|
return true;
|
|
259
265
|
});
|
package/dist/utils/planner.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { cli } from "../cli/cli.js";
|
|
1
2
|
import request from "../request.js";
|
|
3
|
+
import { formatting } from "./formatting.js";
|
|
2
4
|
import { odata } from "./odata.js";
|
|
3
5
|
const graphResource = 'https://graph.microsoft.com';
|
|
4
6
|
const getRequestOptions = (url, metadata) => ({
|
|
@@ -12,7 +14,8 @@ export const planner = {
|
|
|
12
14
|
/**
|
|
13
15
|
* Get Planner plan by ID.
|
|
14
16
|
* @param id Planner ID.
|
|
15
|
-
* @param metadata OData metadata level. Default is none
|
|
17
|
+
* @param metadata OData metadata level. Default is none.
|
|
18
|
+
* @throws Error when the plan is not found.
|
|
16
19
|
*/
|
|
17
20
|
async getPlanById(id, metadata = 'none') {
|
|
18
21
|
const requestOptions = getRequestOptions(`${graphResource}/v1.0/planner/plans/${id}`, metadata);
|
|
@@ -26,6 +29,7 @@ export const planner = {
|
|
|
26
29
|
/**
|
|
27
30
|
* Get all Planner plans for a specific group.
|
|
28
31
|
* @param groupId Group ID.
|
|
32
|
+
* @param metadata OData metadata level. Default is none.
|
|
29
33
|
*/
|
|
30
34
|
getPlansByGroupId(groupId, metadata = 'none') {
|
|
31
35
|
return odata.getAllItems(`${graphResource}/v1.0/groups/${groupId}/planner/plans`, metadata);
|
|
@@ -33,30 +37,105 @@ export const planner = {
|
|
|
33
37
|
/**
|
|
34
38
|
* Get the Planner plan for a specific Roster.
|
|
35
39
|
* @param rosterId Roster ID.
|
|
40
|
+
* @param metadata OData metadata level. Default is none.
|
|
41
|
+
* @throws Error when the roster has no plan.
|
|
36
42
|
*/
|
|
37
43
|
async getPlanByRosterId(rosterId, metadata = 'none') {
|
|
38
44
|
const plans = await odata.getAllItems(`${graphResource}/beta/planner/rosters/${rosterId}/plans`, metadata);
|
|
45
|
+
if (plans.length === 0) {
|
|
46
|
+
throw Error(`The specified roster '${rosterId}' does not have a plan.`);
|
|
47
|
+
}
|
|
39
48
|
return plans[0];
|
|
40
49
|
},
|
|
50
|
+
/**
|
|
51
|
+
* Get the Planner plan ID for a specific Roster.
|
|
52
|
+
* @param rosterId Roster ID.
|
|
53
|
+
* @throws Error when the roster has no plan.
|
|
54
|
+
*/
|
|
55
|
+
async getPlanIdByRosterId(rosterId) {
|
|
56
|
+
const plans = await odata.getAllItems(`${graphResource}/beta/planner/rosters/${rosterId}/plans?$select=id`);
|
|
57
|
+
if (plans.length === 0) {
|
|
58
|
+
throw Error(`The specified roster '${rosterId}' does not have a plan.`);
|
|
59
|
+
}
|
|
60
|
+
return plans[0].id;
|
|
61
|
+
},
|
|
41
62
|
/**
|
|
42
63
|
* Get Planner plan by title in a specific group.
|
|
43
64
|
* @param title Title of the Planner plan. Case insensitive.
|
|
44
65
|
* @param groupId Owner group ID.
|
|
45
|
-
* @param
|
|
66
|
+
* @param metadata OData metadata level. Default is none.
|
|
67
|
+
* @throws Error when the plan is not found.
|
|
46
68
|
*/
|
|
47
69
|
async getPlanByTitle(title, groupId, metadata = 'none') {
|
|
48
|
-
|
|
49
|
-
if (groupId) {
|
|
50
|
-
plans = await this.getPlansByGroupId(groupId, metadata);
|
|
51
|
-
}
|
|
70
|
+
const plans = await this.getPlansByGroupId(groupId, metadata);
|
|
52
71
|
const filteredPlans = plans.filter(p => p.title && p.title.toLowerCase() === title.toLowerCase());
|
|
53
|
-
if (
|
|
72
|
+
if (filteredPlans.length === 0) {
|
|
54
73
|
throw Error(`The specified plan '${title}' does not exist.`);
|
|
55
74
|
}
|
|
56
75
|
if (filteredPlans.length > 1) {
|
|
57
|
-
|
|
76
|
+
const plansKeyValuePair = formatting.convertArrayToHashTable('id', filteredPlans);
|
|
77
|
+
const plan = await cli.handleMultipleResultsFound(`Multiple plans with title '${title}' found.`, plansKeyValuePair);
|
|
78
|
+
return plan;
|
|
58
79
|
}
|
|
59
80
|
return filteredPlans[0];
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Get Planner plan ID by title in a specific group.
|
|
84
|
+
* @param title Title of the Planner plan. Case insensitive.
|
|
85
|
+
* @param groupId Owner group ID.
|
|
86
|
+
* @throws Error when the plan is not found.
|
|
87
|
+
*/
|
|
88
|
+
async getPlanIdByTitle(title, groupId) {
|
|
89
|
+
const plans = await odata.getAllItems(`${graphResource}/v1.0/groups/${groupId}/planner/plans?$select=id,title`);
|
|
90
|
+
const filteredPlans = plans.filter(p => p.title && p.title.toLowerCase() === title.toLowerCase());
|
|
91
|
+
if (filteredPlans.length === 0) {
|
|
92
|
+
throw Error(`The specified plan '${title}' does not exist.`);
|
|
93
|
+
}
|
|
94
|
+
if (filteredPlans.length > 1) {
|
|
95
|
+
const plansKeyValuePair = formatting.convertArrayToHashTable('id', filteredPlans);
|
|
96
|
+
const plan = await cli.handleMultipleResultsFound(`Multiple plans with title '${title}' found.`, plansKeyValuePair);
|
|
97
|
+
return plan.id;
|
|
98
|
+
}
|
|
99
|
+
return filteredPlans[0].id;
|
|
100
|
+
},
|
|
101
|
+
/**
|
|
102
|
+
* Get Planner bucket by title in a specific plan.
|
|
103
|
+
* @param title Title of the Planner bucket. Case insensitive.
|
|
104
|
+
* @param planId ID of the plan that contains the bucket.
|
|
105
|
+
* @param metadata OData metadata level. Default is none.
|
|
106
|
+
* @throws Error when the bucket is not found.
|
|
107
|
+
*/
|
|
108
|
+
async getBucketByTitle(title, planId, metadata = 'none') {
|
|
109
|
+
const buckets = await odata.getAllItems(`${graphResource}/v1.0/planner/plans/${planId}/buckets`, metadata);
|
|
110
|
+
const filteredBuckets = buckets.filter(b => b.name && b.name.toLowerCase() === title.toLowerCase());
|
|
111
|
+
if (filteredBuckets.length === 0) {
|
|
112
|
+
throw Error(`The specified bucket '${title}' does not exist.`);
|
|
113
|
+
}
|
|
114
|
+
if (filteredBuckets.length > 1) {
|
|
115
|
+
const bucketsKeyValuePair = formatting.convertArrayToHashTable('id', filteredBuckets);
|
|
116
|
+
const bucket = await cli.handleMultipleResultsFound(`Multiple buckets with name '${title}' found.`, bucketsKeyValuePair);
|
|
117
|
+
return bucket;
|
|
118
|
+
}
|
|
119
|
+
return filteredBuckets[0];
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Get Planner bucket ID by title in a specific plan.
|
|
123
|
+
* @param title Title of the Planner bucket. Case insensitive.
|
|
124
|
+
* @param planId ID of the plan that contains the bucket.
|
|
125
|
+
* @throws Error when the bucket is not found.
|
|
126
|
+
*/
|
|
127
|
+
async getBucketIdByTitle(title, planId) {
|
|
128
|
+
const buckets = await odata.getAllItems(`${graphResource}/v1.0/planner/plans/${planId}/buckets?$select=id,name`);
|
|
129
|
+
const filteredBuckets = buckets.filter(b => b.name && b.name.toLowerCase() === title.toLowerCase());
|
|
130
|
+
if (filteredBuckets.length === 0) {
|
|
131
|
+
throw Error(`The specified bucket '${title}' does not exist.`);
|
|
132
|
+
}
|
|
133
|
+
if (filteredBuckets.length > 1) {
|
|
134
|
+
const bucketsKeyValuePair = formatting.convertArrayToHashTable('id', filteredBuckets);
|
|
135
|
+
const bucket = await cli.handleMultipleResultsFound(`Multiple buckets with name '${title}' found.`, bucketsKeyValuePair);
|
|
136
|
+
return bucket.id;
|
|
137
|
+
}
|
|
138
|
+
return filteredBuckets[0].id;
|
|
60
139
|
}
|
|
61
140
|
};
|
|
62
141
|
//# sourceMappingURL=planner.js.map
|
package/dist/utils/validation.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const validation = {
|
|
2
|
-
isValidGuidArray(
|
|
3
|
-
|
|
2
|
+
isValidGuidArray(guidsString) {
|
|
3
|
+
const guids = guidsString.split(',').map(guid => guid.trim());
|
|
4
|
+
const invalidGuids = guids.filter(guid => !this.isValidGuid(guid));
|
|
5
|
+
return invalidGuids.length > 0 ? invalidGuids.join(', ') : true;
|
|
4
6
|
},
|
|
5
7
|
isValidGuid(guid) {
|
|
6
8
|
if (!guid) {
|
|
@@ -21,9 +23,10 @@ export const validation = {
|
|
|
21
23
|
const guidRegEx = new RegExp(/^19:[0-9a-zA-Z-_]+(@thread\.v2|@unq\.gbl\.spaces)$/i);
|
|
22
24
|
return guidRegEx.test(guid);
|
|
23
25
|
},
|
|
24
|
-
isValidUserPrincipalNameArray(
|
|
25
|
-
const
|
|
26
|
-
|
|
26
|
+
isValidUserPrincipalNameArray(upnsString) {
|
|
27
|
+
const upns = upnsString.split(',').map(upn => upn.trim());
|
|
28
|
+
const invalidUPNs = upns.filter(upn => !this.isValidUserPrincipalName(upn));
|
|
29
|
+
return invalidUPNs.length > 0 ? invalidUPNs.join(', ') : true;
|
|
27
30
|
},
|
|
28
31
|
isValidUserPrincipalName(upn) {
|
|
29
32
|
const upnRegEx = new RegExp(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/i);
|
|
@@ -23,10 +23,13 @@ m365 entra appregistration permission add [options]
|
|
|
23
23
|
|
|
24
24
|
```md definition-list
|
|
25
25
|
`-i, --appId [appId]`
|
|
26
|
-
: Client ID of the Microsoft Entra app to add the API permissions to. Specify `appId` or `appObjectId
|
|
26
|
+
: Client ID of the Microsoft Entra app to add the API permissions to. Specify either `appId`, `appName` or `appObjectId`.
|
|
27
|
+
|
|
28
|
+
`-n, --appName [appName]`
|
|
29
|
+
: Display name of the Microsoft Entra app to add the API permissions to. Specify either `appId`, `appName` or `appObjectId`.
|
|
27
30
|
|
|
28
31
|
`--appObjectId [appObjectId]`
|
|
29
|
-
: Object ID of the Microsoft Entra app to add the API permissions to. Specify `appId` or `appObjectId
|
|
32
|
+
: Object ID of the Microsoft Entra app to add the API permissions to. Specify either `appId`, `appName` or `appObjectId`.
|
|
30
33
|
|
|
31
34
|
`-a, --applicationPermissions [applicationPermissions]`
|
|
32
35
|
: Space-separated list of application permissions to add. Specify at least `applicationPermissions` or `delegatedPermissions`.
|
|
@@ -58,10 +61,10 @@ Grant multiple delegated API permissions to a Microsoft Entra app specified by o
|
|
|
58
61
|
m365 entra app permission add --appObjectId 'e0306bb2-bf0b-4cc5-a845-a0b2cf11f690' --delegatedPermissions 'https://management.azure.com/user_impersonation https://service.flow.microsoft.com/Flows.Read.All https://graph.microsoft.com/Agreement.Read.All'
|
|
59
62
|
```
|
|
60
63
|
|
|
61
|
-
Grant multiple app-only permissions to a Microsoft Entra app specified by
|
|
64
|
+
Grant multiple app-only permissions to a Microsoft Entra app specified by app display name and grant admin consent.
|
|
62
65
|
|
|
63
66
|
```sh
|
|
64
|
-
m365 entra app permission add --
|
|
67
|
+
m365 entra app permission add --appName 'Contoso App' --applicationPermissions 'https://graph.microsoft.com/Sites.FullControl.All https://microsoft.sharepoint-df.com/Sites.FullControl.All' --grantAdminConsent
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
## Response
|
|
@@ -22,10 +22,13 @@ m365 aad app permission list [options]
|
|
|
22
22
|
|
|
23
23
|
```md definition-list
|
|
24
24
|
`-i, --appId [appId]`
|
|
25
|
-
: Client ID of the
|
|
25
|
+
: Client ID of the Entra application registration to retrieve the permissions for. Specify either `appId`, `appName` or `appObjectId`.
|
|
26
|
+
|
|
27
|
+
`-n, --appName [appName]`
|
|
28
|
+
: Display name of the Entra application registration to add the API permissions to. Specify either `appId`, `appName` or `appObjectId`.
|
|
26
29
|
|
|
27
30
|
`--appObjectId [appObjectId]`
|
|
28
|
-
: Object ID of the
|
|
31
|
+
: Object ID of the Entra application registration to retrieve the permissions for. Specify either `appId`, `appName` or `appObjectId`.
|
|
29
32
|
|
|
30
33
|
`--type [type]`
|
|
31
34
|
: The type of permissions to retrieve. Allowed values: `delegated`, `application`, `all`. Defaults to `all`.
|
|
@@ -39,16 +42,22 @@ For best performance use the `objectId` option to reference the Entra applicatio
|
|
|
39
42
|
|
|
40
43
|
## Examples
|
|
41
44
|
|
|
42
|
-
Retrieves all permissions for an Entra
|
|
45
|
+
Retrieves all permissions for an Entra application registration specified by client id.
|
|
43
46
|
|
|
44
47
|
```sh
|
|
45
48
|
m365 entra app permission list --appId 'f1417aa3-bf0b-4cc5-a845-a0b2cf11f690'
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
Retrieves all
|
|
51
|
+
Retrieves all permissions for an Entra application registration by specified by app display name.
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
m365 entra app permission list --appName 'Contoso App'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Retrieves all delegated permissions for an Entra application registration specified by object id.
|
|
49
58
|
|
|
50
59
|
```sh
|
|
51
|
-
m365 entra app permission list --
|
|
60
|
+
m365 entra app permission list --appObjectId '64381cda-d40d-4fb0-bce2-bece391546a2' --type delegated
|
|
52
61
|
```
|
|
53
62
|
|
|
54
63
|
## Response
|