@pnp/cli-microsoft365 7.1.0-beta.99de250 → 7.1.0-beta.ba486fa
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/dist/Auth.js +1 -1
- package/dist/Command.js +18 -28
- package/dist/cli/Cli.js +3 -2
- package/dist/m365/aad/commands/group/group-user-list.js +146 -0
- package/dist/m365/aad/commands/m365group/m365group-remove.js +109 -15
- package/dist/m365/aad/commands.js +1 -0
- package/dist/m365/cli/commands/cli-doctor.js +11 -6
- package/dist/m365/commands/setup.js +6 -1
- package/dist/m365/spo/commands/sitedesign/sitedesign-get.js +15 -14
- package/dist/m365/spo/commands/sitedesign/sitedesign-remove.js +22 -22
- package/dist/m365/todo/commands/task/task-list.js +8 -10
- package/dist/m365/todo/commands/task/task-remove.js +36 -36
- package/dist/m365/todo/commands/task/task-set.js +11 -13
- package/dist/m365/yammer/commands/group/group-user-remove.js +22 -23
- package/dist/utils/odata.js +20 -19
- package/dist/utils/prompt.js +16 -0
- package/docs/docs/cmd/aad/group/group-user-list.mdx +135 -0
- package/docs/docs/cmd/aad/m365group/m365group-remove.mdx +11 -1
- package/docs/docs/cmd/spo/field/field-get.mdx +1 -1
- package/package.json +33 -13
package/dist/Auth.js
CHANGED
|
@@ -289,7 +289,7 @@ export class Auth {
|
|
|
289
289
|
// but also stub it for testing
|
|
290
290
|
/* c8 ignore next 3 */
|
|
291
291
|
if (!this._authServer) {
|
|
292
|
-
this._authServer = (await import('./AuthServer')).default;
|
|
292
|
+
this._authServer = (await import('./AuthServer.js')).default;
|
|
293
293
|
}
|
|
294
294
|
this._authServer.initializeServer(this.service, resource, resolve, reject, logger, debug);
|
|
295
295
|
});
|
package/dist/Command.js
CHANGED
|
@@ -12,6 +12,7 @@ import { settingsNames } from './settingsNames.js';
|
|
|
12
12
|
import { telemetry } from './telemetry.js';
|
|
13
13
|
import { accessToken } from './utils/accessToken.js';
|
|
14
14
|
import { md } from './utils/md.js';
|
|
15
|
+
import { prompt } from './utils/prompt.js';
|
|
15
16
|
export class CommandError {
|
|
16
17
|
constructor(message, code) {
|
|
17
18
|
this.message = message;
|
|
@@ -75,7 +76,6 @@ class Command {
|
|
|
75
76
|
}
|
|
76
77
|
async validateRequiredOptions(args, command) {
|
|
77
78
|
const shouldPrompt = Cli.getInstance().getSettingWithDefaultValue(settingsNames.prompt, true);
|
|
78
|
-
let inquirer;
|
|
79
79
|
let prompted = false;
|
|
80
80
|
for (let i = 0; i < command.options.length; i++) {
|
|
81
81
|
if (!command.options[i].required ||
|
|
@@ -87,19 +87,17 @@ class Command {
|
|
|
87
87
|
}
|
|
88
88
|
if (!prompted) {
|
|
89
89
|
prompted = true;
|
|
90
|
-
Cli.
|
|
90
|
+
Cli.error('🌶️ Provide values for the following parameters:');
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
inquirer = await import('inquirer');
|
|
94
|
-
}
|
|
95
|
-
const missingRequireOptionValue = await inquirer.default
|
|
96
|
-
.prompt({
|
|
92
|
+
const missingRequireOptionValue = await prompt.forInput({
|
|
97
93
|
name: 'missingRequireOptionValue',
|
|
98
94
|
message: `${command.options[i].name}: `
|
|
99
|
-
})
|
|
100
|
-
.then(result => result.missingRequireOptionValue);
|
|
95
|
+
}).then(result => result.missingRequireOptionValue);
|
|
101
96
|
args.options[command.options[i].name] = missingRequireOptionValue;
|
|
102
97
|
}
|
|
98
|
+
if (prompted) {
|
|
99
|
+
Cli.error('');
|
|
100
|
+
}
|
|
103
101
|
this.processOptions(args.options);
|
|
104
102
|
return true;
|
|
105
103
|
}
|
|
@@ -108,7 +106,6 @@ class Command {
|
|
|
108
106
|
if (!optionsSets || optionsSets.length === 0) {
|
|
109
107
|
return true;
|
|
110
108
|
}
|
|
111
|
-
let inquirer;
|
|
112
109
|
const shouldPrompt = Cli.getInstance().getSettingWithDefaultValue(settingsNames.prompt, true);
|
|
113
110
|
const argsOptions = Object.keys(args.options);
|
|
114
111
|
for (const optionSet of optionsSets.sort(opt => opt.runsWhen ? 0 : 1)) {
|
|
@@ -120,50 +117,43 @@ class Command {
|
|
|
120
117
|
if (!shouldPrompt) {
|
|
121
118
|
return `Specify one of the following options: ${optionSet.options.join(', ')}.`;
|
|
122
119
|
}
|
|
123
|
-
await this.promptForOptionSetNameAndValue(args, optionSet
|
|
120
|
+
await this.promptForOptionSetNameAndValue(args, optionSet);
|
|
124
121
|
}
|
|
125
122
|
if (commonOptions.length > 1) {
|
|
126
123
|
if (!shouldPrompt) {
|
|
127
124
|
return `Specify one of the following options: ${optionSet.options.join(', ')}, but not multiple.`;
|
|
128
125
|
}
|
|
129
|
-
await this.promptForSpecificOption(args, commonOptions
|
|
126
|
+
await this.promptForSpecificOption(args, commonOptions);
|
|
130
127
|
}
|
|
131
128
|
}
|
|
132
129
|
return true;
|
|
133
130
|
}
|
|
134
|
-
async promptForOptionSetNameAndValue(args, optionSet
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
Cli.log(`Please specify one of the following options:`);
|
|
139
|
-
const resultOptionName = await inquirer.default.prompt({
|
|
131
|
+
async promptForOptionSetNameAndValue(args, optionSet) {
|
|
132
|
+
Cli.error(`🌶️ Please specify one of the following options:`);
|
|
133
|
+
const resultOptionName = await prompt.forInput({
|
|
140
134
|
type: 'list',
|
|
141
135
|
name: 'missingRequiredOptionName',
|
|
142
136
|
message: `Option to use:`,
|
|
143
137
|
choices: optionSet.options
|
|
144
138
|
});
|
|
145
139
|
const missingRequiredOptionName = resultOptionName.missingRequiredOptionName;
|
|
146
|
-
const resultOptionValue = await
|
|
147
|
-
.prompt({
|
|
140
|
+
const resultOptionValue = await prompt.forInput({
|
|
148
141
|
name: 'missingRequiredOptionValue',
|
|
149
142
|
message: `${missingRequiredOptionName}:`
|
|
150
143
|
});
|
|
151
144
|
args.options[missingRequiredOptionName] = resultOptionValue.missingRequiredOptionValue;
|
|
152
|
-
Cli.
|
|
145
|
+
Cli.error('');
|
|
153
146
|
}
|
|
154
|
-
async promptForSpecificOption(args, commonOptions
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
Cli.log(`Multiple options for an option set specified. Please specify the correct option that you wish to use.`);
|
|
159
|
-
const requiredOptionNameResult = await inquirer.default.prompt({
|
|
147
|
+
async promptForSpecificOption(args, commonOptions) {
|
|
148
|
+
Cli.error(`🌶️ Multiple options for an option set specified. Please specify the correct option that you wish to use.`);
|
|
149
|
+
const requiredOptionNameResult = await prompt.forInput({
|
|
160
150
|
type: 'list',
|
|
161
151
|
name: 'missingRequiredOptionName',
|
|
162
152
|
message: `Option to use:`,
|
|
163
153
|
choices: commonOptions
|
|
164
154
|
});
|
|
165
155
|
commonOptions.filter(y => y !== requiredOptionNameResult.missingRequiredOptionName).map(optionName => args.options[optionName] = undefined);
|
|
166
|
-
Cli.
|
|
156
|
+
Cli.error('');
|
|
167
157
|
}
|
|
168
158
|
async validateOutput(args) {
|
|
169
159
|
if (args.options.output &&
|
package/dist/cli/Cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import { formatting } from '../utils/formatting.js';
|
|
|
15
15
|
import { fsUtil } from '../utils/fsUtil.js';
|
|
16
16
|
import { md } from '../utils/md.js';
|
|
17
17
|
import { validation } from '../utils/validation.js';
|
|
18
|
+
import { prompt } from '../utils/prompt.js';
|
|
18
19
|
export class Cli {
|
|
19
20
|
get config() {
|
|
20
21
|
if (!this._config) {
|
|
@@ -803,14 +804,13 @@ export class Cli {
|
|
|
803
804
|
}
|
|
804
805
|
}
|
|
805
806
|
static async prompt(options, answers) {
|
|
806
|
-
const inquirer = await import('inquirer');
|
|
807
807
|
const cli = Cli.getInstance();
|
|
808
808
|
const spinnerSpinning = cli.spinner.isSpinning;
|
|
809
809
|
/* c8 ignore next 3 */
|
|
810
810
|
if (spinnerSpinning) {
|
|
811
811
|
cli.spinner.stop();
|
|
812
812
|
}
|
|
813
|
-
const response = await
|
|
813
|
+
const response = await prompt.forInput(options, answers);
|
|
814
814
|
// Restart the spinner if it was running before the prompt
|
|
815
815
|
/* c8 ignore next 3 */
|
|
816
816
|
if (spinnerSpinning) {
|
|
@@ -827,6 +827,7 @@ export class Cli {
|
|
|
827
827
|
type: 'list',
|
|
828
828
|
name: 'select',
|
|
829
829
|
default: 0,
|
|
830
|
+
prefix: '🌶️ ',
|
|
830
831
|
message: `${message} Please choose one:`,
|
|
831
832
|
choices: Object.keys(values)
|
|
832
833
|
});
|
|
@@ -0,0 +1,146 @@
|
|
|
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 _AadGroupUserListCommand_instances, _AadGroupUserListCommand_initTelemetry, _AadGroupUserListCommand_initOptions, _AadGroupUserListCommand_initOptionSets, _AadGroupUserListCommand_initValidators;
|
|
7
|
+
import { aadGroup } from '../../../../utils/aadGroup.js';
|
|
8
|
+
import { odata } from '../../../../utils/odata.js';
|
|
9
|
+
import { validation } from '../../../../utils/validation.js';
|
|
10
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
11
|
+
import commands from '../../commands.js';
|
|
12
|
+
class AadGroupUserListCommand extends GraphCommand {
|
|
13
|
+
get name() {
|
|
14
|
+
return commands.GROUP_USER_LIST;
|
|
15
|
+
}
|
|
16
|
+
get description() {
|
|
17
|
+
return 'Lists users of a specific Azure AD group';
|
|
18
|
+
}
|
|
19
|
+
defaultProperties() {
|
|
20
|
+
return ['id', 'displayName', 'userPrincipalName', 'roles'];
|
|
21
|
+
}
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
_AadGroupUserListCommand_instances.add(this);
|
|
25
|
+
__classPrivateFieldGet(this, _AadGroupUserListCommand_instances, "m", _AadGroupUserListCommand_initTelemetry).call(this);
|
|
26
|
+
__classPrivateFieldGet(this, _AadGroupUserListCommand_instances, "m", _AadGroupUserListCommand_initOptions).call(this);
|
|
27
|
+
__classPrivateFieldGet(this, _AadGroupUserListCommand_instances, "m", _AadGroupUserListCommand_initOptionSets).call(this);
|
|
28
|
+
__classPrivateFieldGet(this, _AadGroupUserListCommand_instances, "m", _AadGroupUserListCommand_initValidators).call(this);
|
|
29
|
+
}
|
|
30
|
+
async commandAction(logger, args) {
|
|
31
|
+
try {
|
|
32
|
+
const groupId = await this.getGroupId(args.options, logger);
|
|
33
|
+
const users = [];
|
|
34
|
+
if (!args.options.role || args.options.role === 'Owner') {
|
|
35
|
+
const owners = await this.getUsers(args.options, 'Owners', groupId, logger);
|
|
36
|
+
owners.forEach(owner => users.push({ ...owner, roles: ['Owner'] }));
|
|
37
|
+
}
|
|
38
|
+
if (!args.options.role || args.options.role === 'Member') {
|
|
39
|
+
const members = await this.getUsers(args.options, 'Members', groupId, logger);
|
|
40
|
+
members.forEach((member) => {
|
|
41
|
+
const user = users.find((u) => u.id === member.id);
|
|
42
|
+
if (user !== undefined) {
|
|
43
|
+
user.roles.push('Member');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
users.push({ ...member, roles: ['Member'] });
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
await logger.log(users);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
this.handleRejectedODataJsonPromise(err);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async getGroupId(options, logger) {
|
|
57
|
+
if (options.groupId) {
|
|
58
|
+
return options.groupId;
|
|
59
|
+
}
|
|
60
|
+
if (this.verbose) {
|
|
61
|
+
await logger.logToStderr('Retrieving Group Id...');
|
|
62
|
+
}
|
|
63
|
+
return await aadGroup.getGroupIdByDisplayName(options.groupDisplayName);
|
|
64
|
+
}
|
|
65
|
+
async getUsers(options, role, groupId, logger) {
|
|
66
|
+
const { properties, filter } = options;
|
|
67
|
+
if (this.verbose) {
|
|
68
|
+
await logger.logToStderr(`Retrieving ${role} of the group with id ${groupId}`);
|
|
69
|
+
}
|
|
70
|
+
const selectProperties = properties ?
|
|
71
|
+
`${properties.split(',').filter(f => f.toLowerCase() !== 'id').concat('id').map(p => p.trim()).join(',')}` :
|
|
72
|
+
'id,displayName,userPrincipalName,givenName,surname';
|
|
73
|
+
const allSelectProperties = selectProperties.split(',');
|
|
74
|
+
const propertiesWithSlash = allSelectProperties.filter(item => item.includes('/'));
|
|
75
|
+
let fieldExpand = '';
|
|
76
|
+
propertiesWithSlash.forEach(p => {
|
|
77
|
+
if (fieldExpand.length > 0) {
|
|
78
|
+
fieldExpand += ',';
|
|
79
|
+
}
|
|
80
|
+
fieldExpand += `${p.split('/')[0]}($select=${p.split('/')[1]})`;
|
|
81
|
+
});
|
|
82
|
+
const expandParam = fieldExpand.length > 0 ? `&$expand=${fieldExpand}` : '';
|
|
83
|
+
const selectParam = allSelectProperties.filter(item => !item.includes('/'));
|
|
84
|
+
const endpoint = `${this.resource}/v1.0/groups/${groupId}/${role}/microsoft.graph.user?$select=${selectParam}${expandParam}`;
|
|
85
|
+
let users = [];
|
|
86
|
+
if (filter) {
|
|
87
|
+
// While using the filter, we need to specify the ConsistencyLevel header.
|
|
88
|
+
// Can be refactored when the header is no longer necessary.
|
|
89
|
+
const requestOptions = {
|
|
90
|
+
url: `${endpoint}&$filter=${encodeURIComponent(filter)}&$count=true`,
|
|
91
|
+
headers: {
|
|
92
|
+
accept: 'application/json;odata.metadata=none',
|
|
93
|
+
ConsistencyLevel: 'eventual'
|
|
94
|
+
},
|
|
95
|
+
responseType: 'json'
|
|
96
|
+
};
|
|
97
|
+
users = await odata.getAllItems(requestOptions);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
users = await odata.getAllItems(endpoint);
|
|
101
|
+
}
|
|
102
|
+
return users;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_AadGroupUserListCommand_instances = new WeakSet(), _AadGroupUserListCommand_initTelemetry = function _AadGroupUserListCommand_initTelemetry() {
|
|
106
|
+
this.telemetry.push((args) => {
|
|
107
|
+
Object.assign(this.telemetryProperties, {
|
|
108
|
+
groupId: typeof args.options.groupId !== 'undefined',
|
|
109
|
+
groupDisplayName: typeof args.options.groupDisplayName !== 'undefined',
|
|
110
|
+
role: typeof args.options.role !== 'undefined',
|
|
111
|
+
properties: typeof args.options.properties !== 'undefined',
|
|
112
|
+
filter: typeof args.options.filter !== 'undefined'
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}, _AadGroupUserListCommand_initOptions = function _AadGroupUserListCommand_initOptions() {
|
|
116
|
+
this.options.unshift({
|
|
117
|
+
option: "-i, --groupId [groupId]"
|
|
118
|
+
}, {
|
|
119
|
+
option: "-n, --groupDisplayName [groupDisplayName]"
|
|
120
|
+
}, {
|
|
121
|
+
option: "-r, --role [role]",
|
|
122
|
+
autocomplete: ["Owner", "Member"]
|
|
123
|
+
}, {
|
|
124
|
+
option: "-p, --properties [properties]"
|
|
125
|
+
}, {
|
|
126
|
+
option: "-f, --filter [filter]"
|
|
127
|
+
});
|
|
128
|
+
}, _AadGroupUserListCommand_initOptionSets = function _AadGroupUserListCommand_initOptionSets() {
|
|
129
|
+
this.optionSets.push({
|
|
130
|
+
options: ['groupId', 'groupDisplayName']
|
|
131
|
+
});
|
|
132
|
+
}, _AadGroupUserListCommand_initValidators = function _AadGroupUserListCommand_initValidators() {
|
|
133
|
+
this.validators.push(async (args) => {
|
|
134
|
+
if (args.options.groupId && !validation.isValidGuid(args.options.groupId)) {
|
|
135
|
+
return `${args.options.groupId} is not a valid GUID`;
|
|
136
|
+
}
|
|
137
|
+
if (args.options.role) {
|
|
138
|
+
if (['Owner', 'Member'].indexOf(args.options.role) === -1) {
|
|
139
|
+
return `${args.options.role} is not a valid role value. Allowed values Owner|Member`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
export default new AadGroupUserListCommand();
|
|
146
|
+
//# sourceMappingURL=group-user-list.js.map
|
|
@@ -10,16 +10,21 @@ import { aadGroup } from '../../../../utils/aadGroup.js';
|
|
|
10
10
|
import { validation } from '../../../../utils/validation.js';
|
|
11
11
|
import GraphCommand from '../../../base/GraphCommand.js';
|
|
12
12
|
import commands from '../../commands.js';
|
|
13
|
+
import config from '../../../../config.js';
|
|
14
|
+
import { formatting } from '../../../../utils/formatting.js';
|
|
15
|
+
import { spo } from '../../../../utils/spo.js';
|
|
16
|
+
import { setTimeout } from 'timers/promises';
|
|
13
17
|
class AadM365GroupRemoveCommand extends GraphCommand {
|
|
14
18
|
get name() {
|
|
15
19
|
return commands.M365GROUP_REMOVE;
|
|
16
20
|
}
|
|
17
21
|
get description() {
|
|
18
|
-
return 'Removes
|
|
22
|
+
return 'Removes a Microsoft 365 Group';
|
|
19
23
|
}
|
|
20
24
|
constructor() {
|
|
21
25
|
super();
|
|
22
26
|
_AadM365GroupRemoveCommand_instances.add(this);
|
|
27
|
+
this.intervalInMs = 5000;
|
|
23
28
|
__classPrivateFieldGet(this, _AadM365GroupRemoveCommand_instances, "m", _AadM365GroupRemoveCommand_initTelemetry).call(this);
|
|
24
29
|
__classPrivateFieldGet(this, _AadM365GroupRemoveCommand_instances, "m", _AadM365GroupRemoveCommand_initOptions).call(this);
|
|
25
30
|
__classPrivateFieldGet(this, _AadM365GroupRemoveCommand_instances, "m", _AadM365GroupRemoveCommand_initValidators).call(this);
|
|
@@ -34,21 +39,13 @@ class AadM365GroupRemoveCommand extends GraphCommand {
|
|
|
34
39
|
if (!isUnifiedGroup) {
|
|
35
40
|
throw Error(`Specified group with id '${args.options.id}' is not a Microsoft 365 group.`);
|
|
36
41
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
await request.delete(requestOptions);
|
|
42
|
+
const siteUrl = await this.getM365GroupSiteUrl(logger, args.options.id);
|
|
43
|
+
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.debug);
|
|
44
|
+
// Delete the Microsoft 365 group site. This operation will also delete the group.
|
|
45
|
+
await this.deleteM365GroupSite(logger, siteUrl, spoAdminUrl);
|
|
44
46
|
if (args.options.skipRecycleBin) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
headers: {
|
|
48
|
-
'accept': 'application/json;odata.metadata=none'
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
await request.delete(requestOptions2);
|
|
47
|
+
await this.deleteM365GroupFromRecycleBin(logger, args.options.id);
|
|
48
|
+
await this.deleteSiteFromRecycleBin(logger, siteUrl, spoAdminUrl);
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
catch (err) {
|
|
@@ -70,6 +67,102 @@ class AadM365GroupRemoveCommand extends GraphCommand {
|
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
69
|
}
|
|
70
|
+
async getM365GroupSiteUrl(logger, id) {
|
|
71
|
+
if (this.verbose) {
|
|
72
|
+
await logger.logToStderr(`Getting the site URL of Microsoft 365 Group: ${id}...`);
|
|
73
|
+
}
|
|
74
|
+
const requestOptions = {
|
|
75
|
+
url: `${this.resource}/v1.0/groups/${id}/drive?$select=webUrl`,
|
|
76
|
+
headers: {
|
|
77
|
+
accept: 'application/json;odata.metadata=none'
|
|
78
|
+
},
|
|
79
|
+
responseType: 'json'
|
|
80
|
+
};
|
|
81
|
+
const res = await request.get(requestOptions);
|
|
82
|
+
// Extract the base URL by removing everything after the last '/' character in the URL.
|
|
83
|
+
const baseUrl = res.webUrl.substring(0, res.webUrl.lastIndexOf('/'));
|
|
84
|
+
return baseUrl;
|
|
85
|
+
}
|
|
86
|
+
async deleteM365GroupSite(logger, url, spoAdminUrl) {
|
|
87
|
+
if (this.verbose) {
|
|
88
|
+
await logger.logToStderr(`Deleting the group site: '${url}'...`);
|
|
89
|
+
}
|
|
90
|
+
const requestOptions = {
|
|
91
|
+
url: `${spoAdminUrl}/_api/GroupSiteManager/Delete?siteUrl='${url}'`,
|
|
92
|
+
headers: {
|
|
93
|
+
accept: 'application/json;odata=nometadata'
|
|
94
|
+
},
|
|
95
|
+
responseType: 'json'
|
|
96
|
+
};
|
|
97
|
+
await request.post(requestOptions);
|
|
98
|
+
}
|
|
99
|
+
async deleteM365GroupFromRecycleBin(logger, id) {
|
|
100
|
+
for (let retries = 0; retries < AadM365GroupRemoveCommand.maxRetries; retries++) {
|
|
101
|
+
if (await this.isM365GroupInDeletedItemsList(id)) {
|
|
102
|
+
await this.removeM365GroupPermanently(logger, id);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (this.verbose) {
|
|
107
|
+
await logger.logToStderr(`Group has not been deleted yet. Waiting for ${this.intervalInMs / 1000} seconds before the next attempt. ${AadM365GroupRemoveCommand.maxRetries - retries} attempts remaining...`);
|
|
108
|
+
}
|
|
109
|
+
await setTimeout(this.intervalInMs);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
await logger.logToStderr(`Group has been successfully deleted, but it couldn't be permanently removed from the recycle bin after all retries. It will still appear in the deleted groups list.`);
|
|
113
|
+
}
|
|
114
|
+
async removeM365GroupPermanently(logger, id) {
|
|
115
|
+
if (this.verbose) {
|
|
116
|
+
await logger.logToStderr(`Group has been deleted and is now available in the deleted groups list. Removing permanently...`);
|
|
117
|
+
}
|
|
118
|
+
const requestOptions = {
|
|
119
|
+
url: `${this.resource}/v1.0/directory/deletedItems/${id}`,
|
|
120
|
+
headers: {
|
|
121
|
+
accept: 'application/json;odata.metadata=none'
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
await request.delete(requestOptions);
|
|
125
|
+
}
|
|
126
|
+
async isM365GroupInDeletedItemsList(id) {
|
|
127
|
+
const requestOptions = {
|
|
128
|
+
url: `${this.resource}/v1.0/directory/deletedItems/${id}`,
|
|
129
|
+
headers: {
|
|
130
|
+
accept: 'application/json;odata.metadata=none'
|
|
131
|
+
},
|
|
132
|
+
responseType: 'json'
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
const response = await request.get(requestOptions);
|
|
136
|
+
return Boolean(response && response.id);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error.response && error.response.status === 404) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async deleteSiteFromRecycleBin(logger, url, spoAdminUrl) {
|
|
148
|
+
if (this.verbose) {
|
|
149
|
+
await logger.logToStderr(`Deleting the M365 group site '${url}' from the recycle bin...`);
|
|
150
|
+
}
|
|
151
|
+
const res = await spo.ensureFormDigest(spoAdminUrl, logger, undefined, this.debug);
|
|
152
|
+
const requestOptions = {
|
|
153
|
+
url: `${spoAdminUrl}/_vti_bin/client.svc/ProcessQuery`,
|
|
154
|
+
headers: {
|
|
155
|
+
'X-RequestDigest': res.FormDigestValue
|
|
156
|
+
},
|
|
157
|
+
data: `<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="${config.applicationName}" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="185" ObjectPathId="184" /><Query Id="186" ObjectPathId="184"><Query SelectAllProperties="false"><Properties><Property Name="IsComplete" ScalarProperty="true" /><Property Name="PollingInterval" ScalarProperty="true" /></Properties></Query></Query></Actions><ObjectPaths><Method Id="184" ParentId="175" Name="RemoveDeletedSite"><Parameters><Parameter Type="String">${formatting.escapeXml(url)}</Parameter></Parameters></Method><Constructor Id="175" TypeId="{268004ae-ef6b-4e9b-8425-127220d84719}" /></ObjectPaths></Request>`
|
|
158
|
+
};
|
|
159
|
+
const processQuery = await request.post(requestOptions);
|
|
160
|
+
const json = JSON.parse(processQuery);
|
|
161
|
+
const response = json[0];
|
|
162
|
+
if (response.ErrorInfo) {
|
|
163
|
+
throw response.ErrorInfo.ErrorMessage;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
73
166
|
}
|
|
74
167
|
_AadM365GroupRemoveCommand_instances = new WeakSet(), _AadM365GroupRemoveCommand_initTelemetry = function _AadM365GroupRemoveCommand_initTelemetry() {
|
|
75
168
|
this.telemetry.push((args) => {
|
|
@@ -94,5 +187,6 @@ _AadM365GroupRemoveCommand_instances = new WeakSet(), _AadM365GroupRemoveCommand
|
|
|
94
187
|
return true;
|
|
95
188
|
});
|
|
96
189
|
};
|
|
190
|
+
AadM365GroupRemoveCommand.maxRetries = 10;
|
|
97
191
|
export default new AadM365GroupRemoveCommand();
|
|
98
192
|
//# sourceMappingURL=m365group-remove.js.map
|
|
@@ -14,6 +14,7 @@ export default {
|
|
|
14
14
|
GROUP_GET: `${prefix} group get`,
|
|
15
15
|
GROUP_LIST: `${prefix} group list`,
|
|
16
16
|
GROUP_REMOVE: `${prefix} group remove`,
|
|
17
|
+
GROUP_USER_LIST: `${prefix} group user list`,
|
|
17
18
|
GROUPSETTING_ADD: `${prefix} groupsetting add`,
|
|
18
19
|
GROUPSETTING_GET: `${prefix} groupsetting get`,
|
|
19
20
|
GROUPSETTING_LIST: `${prefix} groupsetting list`,
|
|
@@ -14,11 +14,14 @@ class CliDoctorCommand extends Command {
|
|
|
14
14
|
}
|
|
15
15
|
async commandAction(logger) {
|
|
16
16
|
const roles = [];
|
|
17
|
-
const scopes =
|
|
17
|
+
const scopes = new Map();
|
|
18
18
|
Object.keys(auth.service.accessTokens).forEach(resource => {
|
|
19
19
|
const accessToken = auth.service.accessTokens[resource].accessToken;
|
|
20
20
|
this.getRolesFromAccessToken(accessToken).forEach(role => roles.push(role));
|
|
21
|
-
this.getScopesFromAccessToken(accessToken)
|
|
21
|
+
const [res, scp] = this.getScopesFromAccessToken(accessToken);
|
|
22
|
+
if (res !== "") {
|
|
23
|
+
scopes.set(res, scp);
|
|
24
|
+
}
|
|
22
25
|
});
|
|
23
26
|
const diagnosticInfo = {
|
|
24
27
|
os: {
|
|
@@ -34,7 +37,7 @@ class CliDoctorCommand extends Command {
|
|
|
34
37
|
cliEnvironment: process.env.CLIMICROSOFT365_ENV ? process.env.CLIMICROSOFT365_ENV : '',
|
|
35
38
|
cliConfig: Cli.getInstance().config.all,
|
|
36
39
|
roles: roles,
|
|
37
|
-
scopes: scopes
|
|
40
|
+
scopes: Object.fromEntries(scopes)
|
|
38
41
|
};
|
|
39
42
|
await logger.log(diagnosticInfo);
|
|
40
43
|
}
|
|
@@ -55,20 +58,22 @@ class CliDoctorCommand extends Command {
|
|
|
55
58
|
return roles;
|
|
56
59
|
}
|
|
57
60
|
getScopesFromAccessToken(accessToken) {
|
|
61
|
+
let resource = "";
|
|
58
62
|
let scopes = [];
|
|
59
63
|
if (!accessToken || accessToken.length === 0) {
|
|
60
|
-
return scopes;
|
|
64
|
+
return [resource, scopes];
|
|
61
65
|
}
|
|
62
66
|
const chunks = accessToken.split('.');
|
|
63
67
|
if (chunks.length !== 3) {
|
|
64
|
-
return scopes;
|
|
68
|
+
return [resource, scopes];
|
|
65
69
|
}
|
|
66
70
|
const tokenString = Buffer.from(chunks[1], 'base64').toString();
|
|
67
71
|
const token = JSON.parse(tokenString);
|
|
68
72
|
if (token.scp?.length > 0) {
|
|
73
|
+
resource = token.aud.replace(/(-my|-admin).sharepoint.com/, '.sharepoint.com');
|
|
69
74
|
scopes = token.scp.split(' ');
|
|
70
75
|
}
|
|
71
|
-
return scopes;
|
|
76
|
+
return [resource, scopes];
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
export default new CliDoctorCommand();
|
|
@@ -12,6 +12,7 @@ import { pid } from '../../utils/pid.js';
|
|
|
12
12
|
import AnonymousCommand from '../base/AnonymousCommand.js';
|
|
13
13
|
import commands from './commands.js';
|
|
14
14
|
import { interactivePreset, powerShellPreset, scriptingPreset } from './setupPresets.js';
|
|
15
|
+
import { prompt } from '../../utils/prompt.js';
|
|
15
16
|
class SetupCommand extends AnonymousCommand {
|
|
16
17
|
get name() {
|
|
17
18
|
return commands.SETUP;
|
|
@@ -44,11 +45,13 @@ class SetupCommand extends AnonymousCommand {
|
|
|
44
45
|
await this.configureSettings(settings, true, logger);
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
48
|
+
// stop the spinner. Fixes #5598
|
|
49
|
+
Cli.getInstance().spinner.stop();
|
|
47
50
|
await logger.logToStderr(`Welcome to the CLI for Microsoft 365 setup!`);
|
|
48
51
|
await logger.logToStderr(`This command will guide you through the process of configuring the CLI for your needs.`);
|
|
49
52
|
await logger.logToStderr(`Please, answer the following questions and we'll define a set of settings to best match how you intend to use the CLI.`);
|
|
50
53
|
await logger.logToStderr('');
|
|
51
|
-
const preferences = await
|
|
54
|
+
const preferences = await prompt.forInput([
|
|
52
55
|
{
|
|
53
56
|
type: 'list',
|
|
54
57
|
name: 'usageMode',
|
|
@@ -94,6 +97,8 @@ class SetupCommand extends AnonymousCommand {
|
|
|
94
97
|
await logger.logToStderr('');
|
|
95
98
|
await logger.logToStderr('Configuring settings...');
|
|
96
99
|
await logger.logToStderr('');
|
|
100
|
+
// start the spinner. Fixes #5598
|
|
101
|
+
Cli.getInstance().spinner.start();
|
|
97
102
|
await this.configureSettings(settings, false, logger);
|
|
98
103
|
if (!this.verbose) {
|
|
99
104
|
await logger.logToStderr('');
|
|
@@ -4,7 +4,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
6
|
var _SpoSiteDesignGetCommand_instances, _SpoSiteDesignGetCommand_initTelemetry, _SpoSiteDesignGetCommand_initOptions, _SpoSiteDesignGetCommand_initValidators, _SpoSiteDesignGetCommand_initOptionSets;
|
|
7
|
+
import { Cli } from '../../../../cli/Cli.js';
|
|
7
8
|
import request from '../../../../request.js';
|
|
9
|
+
import { formatting } from '../../../../utils/formatting.js';
|
|
8
10
|
import { spo } from '../../../../utils/spo.js';
|
|
9
11
|
import { validation } from '../../../../utils/validation.js';
|
|
10
12
|
import SpoCommand from '../../../base/SpoCommand.js';
|
|
@@ -24,9 +26,9 @@ class SpoSiteDesignGetCommand extends SpoCommand {
|
|
|
24
26
|
__classPrivateFieldGet(this, _SpoSiteDesignGetCommand_instances, "m", _SpoSiteDesignGetCommand_initValidators).call(this);
|
|
25
27
|
__classPrivateFieldGet(this, _SpoSiteDesignGetCommand_instances, "m", _SpoSiteDesignGetCommand_initOptionSets).call(this);
|
|
26
28
|
}
|
|
27
|
-
getSiteDesignId(args, spoUrl) {
|
|
29
|
+
async getSiteDesignId(args, spoUrl) {
|
|
28
30
|
if (args.options.id) {
|
|
29
|
-
return
|
|
31
|
+
return args.options.id;
|
|
30
32
|
}
|
|
31
33
|
const requestOptions = {
|
|
32
34
|
url: `${spoUrl}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.GetSiteDesigns`,
|
|
@@ -35,18 +37,17 @@ class SpoSiteDesignGetCommand extends SpoCommand {
|
|
|
35
37
|
},
|
|
36
38
|
responseType: 'json'
|
|
37
39
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
40
|
+
const response = await request.post(requestOptions);
|
|
41
|
+
const matchingSiteDesigns = response.value.filter(x => x.Title === args.options.title);
|
|
42
|
+
if (matchingSiteDesigns.length === 0) {
|
|
43
|
+
throw `The specified site design does not exist`;
|
|
44
|
+
}
|
|
45
|
+
if (matchingSiteDesigns.length > 1) {
|
|
46
|
+
const resultAsKeyValuePair = formatting.convertArrayToHashTable('Id', matchingSiteDesigns);
|
|
47
|
+
const result = await Cli.handleMultipleResultsFound(`Multiple site designs with title '${args.options.title}' found.`, resultAsKeyValuePair);
|
|
48
|
+
return result.Id;
|
|
49
|
+
}
|
|
50
|
+
return matchingSiteDesigns[0].Id;
|
|
50
51
|
}
|
|
51
52
|
async commandAction(logger, args) {
|
|
52
53
|
try {
|
|
@@ -25,28 +25,8 @@ class SpoSiteDesignRemoveCommand extends SpoCommand {
|
|
|
25
25
|
__classPrivateFieldGet(this, _SpoSiteDesignRemoveCommand_instances, "m", _SpoSiteDesignRemoveCommand_initValidators).call(this);
|
|
26
26
|
}
|
|
27
27
|
async commandAction(logger, args) {
|
|
28
|
-
const removeSiteDesign = async () => {
|
|
29
|
-
try {
|
|
30
|
-
const spoUrl = await spo.getSpoUrl(logger, this.debug);
|
|
31
|
-
const requestDigest = await spo.getRequestDigest(spoUrl);
|
|
32
|
-
const requestOptions = {
|
|
33
|
-
url: `${spoUrl}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.DeleteSiteDesign`,
|
|
34
|
-
headers: {
|
|
35
|
-
'X-RequestDigest': requestDigest.FormDigestValue,
|
|
36
|
-
'content-type': 'application/json;charset=utf-8',
|
|
37
|
-
accept: 'application/json;odata=nometadata'
|
|
38
|
-
},
|
|
39
|
-
data: { id: args.options.id },
|
|
40
|
-
responseType: 'json'
|
|
41
|
-
};
|
|
42
|
-
await request.post(requestOptions);
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
this.handleRejectedODataJsonPromise(err);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
28
|
if (args.options.force) {
|
|
49
|
-
await removeSiteDesign();
|
|
29
|
+
await this.removeSiteDesign(logger, args.options.id);
|
|
50
30
|
}
|
|
51
31
|
else {
|
|
52
32
|
const result = await Cli.prompt({
|
|
@@ -56,10 +36,30 @@ class SpoSiteDesignRemoveCommand extends SpoCommand {
|
|
|
56
36
|
message: `Are you sure you want to remove the site design ${args.options.id}?`
|
|
57
37
|
});
|
|
58
38
|
if (result.continue) {
|
|
59
|
-
await removeSiteDesign();
|
|
39
|
+
await this.removeSiteDesign(logger, args.options.id);
|
|
60
40
|
}
|
|
61
41
|
}
|
|
62
42
|
}
|
|
43
|
+
async removeSiteDesign(logger, id) {
|
|
44
|
+
try {
|
|
45
|
+
const spoUrl = await spo.getSpoUrl(logger, this.debug);
|
|
46
|
+
const requestDigest = await spo.getRequestDigest(spoUrl);
|
|
47
|
+
const requestOptions = {
|
|
48
|
+
url: `${spoUrl}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.DeleteSiteDesign`,
|
|
49
|
+
headers: {
|
|
50
|
+
'X-RequestDigest': requestDigest.FormDigestValue,
|
|
51
|
+
'content-type': 'application/json;charset=utf-8',
|
|
52
|
+
accept: 'application/json;odata=nometadata'
|
|
53
|
+
},
|
|
54
|
+
data: { id: id },
|
|
55
|
+
responseType: 'json'
|
|
56
|
+
};
|
|
57
|
+
await request.post(requestOptions);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
this.handleRejectedODataJsonPromise(err);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
63
|
}
|
|
64
64
|
_SpoSiteDesignRemoveCommand_instances = new WeakSet(), _SpoSiteDesignRemoveCommand_initTelemetry = function _SpoSiteDesignRemoveCommand_initTelemetry() {
|
|
65
65
|
this.telemetry.push((args) => {
|
|
@@ -26,9 +26,9 @@ class TodoTaskListCommand extends GraphCommand {
|
|
|
26
26
|
__classPrivateFieldGet(this, _TodoTaskListCommand_instances, "m", _TodoTaskListCommand_initOptions).call(this);
|
|
27
27
|
__classPrivateFieldGet(this, _TodoTaskListCommand_instances, "m", _TodoTaskListCommand_initOptionSets).call(this);
|
|
28
28
|
}
|
|
29
|
-
getTodoListId(args) {
|
|
29
|
+
async getTodoListId(args) {
|
|
30
30
|
if (args.options.listId) {
|
|
31
|
-
return
|
|
31
|
+
return args.options.listId;
|
|
32
32
|
}
|
|
33
33
|
const requestOptions = {
|
|
34
34
|
url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName)}'`,
|
|
@@ -37,14 +37,12 @@ class TodoTaskListCommand extends GraphCommand {
|
|
|
37
37
|
},
|
|
38
38
|
responseType: 'json'
|
|
39
39
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return Promise.resolve(taskList.id);
|
|
47
|
-
});
|
|
40
|
+
const response = await request.get(requestOptions);
|
|
41
|
+
const taskList = response.value[0];
|
|
42
|
+
if (!taskList) {
|
|
43
|
+
throw `The specified task list does not exist`;
|
|
44
|
+
}
|
|
45
|
+
return taskList.id;
|
|
48
46
|
}
|
|
49
47
|
async commandAction(logger, args) {
|
|
50
48
|
try {
|
|
@@ -23,42 +23,8 @@ class TodoTaskRemoveCommand extends GraphCommand {
|
|
|
23
23
|
__classPrivateFieldGet(this, _TodoTaskRemoveCommand_instances, "m", _TodoTaskRemoveCommand_initOptionSets).call(this);
|
|
24
24
|
}
|
|
25
25
|
async commandAction(logger, args) {
|
|
26
|
-
const getToDoListId = async () => {
|
|
27
|
-
if (args.options.listName) {
|
|
28
|
-
// Search list by its name
|
|
29
|
-
const requestOptions = {
|
|
30
|
-
url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(args.options.listName)}'`,
|
|
31
|
-
headers: {
|
|
32
|
-
accept: "application/json;odata.metadata=none"
|
|
33
|
-
},
|
|
34
|
-
responseType: 'json'
|
|
35
|
-
};
|
|
36
|
-
const response = await request.get(requestOptions);
|
|
37
|
-
return response.value && response.value.length === 1 ? response.value[0].id : undefined;
|
|
38
|
-
}
|
|
39
|
-
return Promise.resolve(args.options.listId);
|
|
40
|
-
};
|
|
41
|
-
const removeToDoTask = async () => {
|
|
42
|
-
try {
|
|
43
|
-
const toDoListId = await getToDoListId();
|
|
44
|
-
if (!toDoListId) {
|
|
45
|
-
throw `The list ${args.options.listName} cannot be found`;
|
|
46
|
-
}
|
|
47
|
-
const requestOptions = {
|
|
48
|
-
url: `${this.resource}/v1.0/me/todo/lists/${toDoListId}/tasks/${args.options.id}`,
|
|
49
|
-
headers: {
|
|
50
|
-
accept: "application/json;odata.metadata=none"
|
|
51
|
-
},
|
|
52
|
-
responseType: 'json'
|
|
53
|
-
};
|
|
54
|
-
await request.delete(requestOptions);
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
this.handleRejectedODataJsonPromise(err);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
26
|
if (args.options.force) {
|
|
61
|
-
await removeToDoTask();
|
|
27
|
+
await this.removeToDoTask(args.options);
|
|
62
28
|
}
|
|
63
29
|
else {
|
|
64
30
|
const result = await Cli.prompt({
|
|
@@ -68,10 +34,44 @@ class TodoTaskRemoveCommand extends GraphCommand {
|
|
|
68
34
|
message: `Are you sure you want to remove the task ${args.options.id} from list ${args.options.listId || args.options.listName}?`
|
|
69
35
|
});
|
|
70
36
|
if (result.continue) {
|
|
71
|
-
await removeToDoTask();
|
|
37
|
+
await this.removeToDoTask(args.options);
|
|
72
38
|
}
|
|
73
39
|
}
|
|
74
40
|
}
|
|
41
|
+
async getToDoListId(options) {
|
|
42
|
+
if (options.listName) {
|
|
43
|
+
// Search list by its name
|
|
44
|
+
const requestOptions = {
|
|
45
|
+
url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName)}'`,
|
|
46
|
+
headers: {
|
|
47
|
+
accept: "application/json;odata.metadata=none"
|
|
48
|
+
},
|
|
49
|
+
responseType: 'json'
|
|
50
|
+
};
|
|
51
|
+
const response = await request.get(requestOptions);
|
|
52
|
+
return response.value && response.value.length === 1 ? response.value[0].id : undefined;
|
|
53
|
+
}
|
|
54
|
+
return options.listId;
|
|
55
|
+
}
|
|
56
|
+
async removeToDoTask(options) {
|
|
57
|
+
try {
|
|
58
|
+
const toDoListId = await this.getToDoListId(options);
|
|
59
|
+
if (!toDoListId) {
|
|
60
|
+
throw `The list ${options.listName} cannot be found`;
|
|
61
|
+
}
|
|
62
|
+
const requestOptions = {
|
|
63
|
+
url: `${this.resource}/v1.0/me/todo/lists/${toDoListId}/tasks/${options.id}`,
|
|
64
|
+
headers: {
|
|
65
|
+
accept: "application/json;odata.metadata=none"
|
|
66
|
+
},
|
|
67
|
+
responseType: 'json'
|
|
68
|
+
};
|
|
69
|
+
await request.delete(requestOptions);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
this.handleRejectedODataJsonPromise(err);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
75
|
}
|
|
76
76
|
_TodoTaskRemoveCommand_instances = new WeakSet(), _TodoTaskRemoveCommand_initTelemetry = function _TodoTaskRemoveCommand_initTelemetry() {
|
|
77
77
|
this.telemetry.push((args) => {
|
|
@@ -28,7 +28,7 @@ class TodoTaskSetCommand extends GraphCommand {
|
|
|
28
28
|
const endpoint = `${this.resource}/v1.0`;
|
|
29
29
|
const data = this.mapRequestBody(args.options);
|
|
30
30
|
try {
|
|
31
|
-
const listId = await this.getTodoListId(args);
|
|
31
|
+
const listId = await this.getTodoListId(args.options);
|
|
32
32
|
const requestOptions = {
|
|
33
33
|
url: `${endpoint}/me/todo/lists/${listId}/tasks/${formatting.encodeQueryParameter(args.options.id)}`,
|
|
34
34
|
headers: {
|
|
@@ -45,25 +45,23 @@ class TodoTaskSetCommand extends GraphCommand {
|
|
|
45
45
|
this.handleRejectedODataJsonPromise(err);
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
getTodoListId(
|
|
49
|
-
if (
|
|
50
|
-
return
|
|
48
|
+
async getTodoListId(options) {
|
|
49
|
+
if (options.listId) {
|
|
50
|
+
return options.listId;
|
|
51
51
|
}
|
|
52
52
|
const requestOptions = {
|
|
53
|
-
url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(
|
|
53
|
+
url: `${this.resource}/v1.0/me/todo/lists?$filter=displayName eq '${escape(options.listName)}'`,
|
|
54
54
|
headers: {
|
|
55
55
|
accept: 'application/json;odata.metadata=none'
|
|
56
56
|
},
|
|
57
57
|
responseType: 'json'
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return Promise.resolve(taskList.id);
|
|
66
|
-
});
|
|
59
|
+
const response = await request.get(requestOptions);
|
|
60
|
+
const taskList = response.value[0];
|
|
61
|
+
if (!taskList) {
|
|
62
|
+
throw `The specified task list does not exist`;
|
|
63
|
+
}
|
|
64
|
+
return taskList.id;
|
|
67
65
|
}
|
|
68
66
|
getDateTimeTimeZone(dateTime) {
|
|
69
67
|
return {
|
|
@@ -23,29 +23,8 @@ class YammerGroupUserRemoveCommand extends YammerCommand {
|
|
|
23
23
|
__classPrivateFieldGet(this, _YammerGroupUserRemoveCommand_instances, "m", _YammerGroupUserRemoveCommand_initValidators).call(this);
|
|
24
24
|
}
|
|
25
25
|
async commandAction(logger, args) {
|
|
26
|
-
const executeRemoveAction = async () => {
|
|
27
|
-
const endpoint = `${this.resource}/v1/group_memberships.json`;
|
|
28
|
-
const requestOptions = {
|
|
29
|
-
url: endpoint,
|
|
30
|
-
headers: {
|
|
31
|
-
accept: 'application/json;odata.metadata=none',
|
|
32
|
-
'content-type': 'application/json;odata=nometadata'
|
|
33
|
-
},
|
|
34
|
-
responseType: 'json',
|
|
35
|
-
data: {
|
|
36
|
-
group_id: args.options.groupId,
|
|
37
|
-
user_id: args.options.id
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
try {
|
|
41
|
-
await request.delete(requestOptions);
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
this.handleRejectedODataJsonPromise(err);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
26
|
if (args.options.force) {
|
|
48
|
-
await executeRemoveAction();
|
|
27
|
+
await this.executeRemoveAction(args.options);
|
|
49
28
|
}
|
|
50
29
|
else {
|
|
51
30
|
let messagePrompt = `Are you sure you want to leave group ${args.options.groupId}?`;
|
|
@@ -59,8 +38,28 @@ class YammerGroupUserRemoveCommand extends YammerCommand {
|
|
|
59
38
|
message: messagePrompt
|
|
60
39
|
});
|
|
61
40
|
if (result.continue) {
|
|
62
|
-
await executeRemoveAction();
|
|
41
|
+
await this.executeRemoveAction(args.options);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async executeRemoveAction(options) {
|
|
46
|
+
const requestOptions = {
|
|
47
|
+
url: `${this.resource}/v1/group_memberships.json`,
|
|
48
|
+
headers: {
|
|
49
|
+
accept: 'application/json;odata.metadata=none',
|
|
50
|
+
'content-type': 'application/json;odata=nometadata'
|
|
51
|
+
},
|
|
52
|
+
responseType: 'json',
|
|
53
|
+
data: {
|
|
54
|
+
group_id: options.groupId,
|
|
55
|
+
user_id: options.id
|
|
63
56
|
}
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
await request.delete(requestOptions);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
this.handleRejectedODataJsonPromise(err);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
}
|
package/dist/utils/odata.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import request from "../request.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
items = items.concat(nextPageItems);
|
|
19
|
-
}
|
|
20
|
-
return items;
|
|
2
|
+
async function getAllItems(param1, metadata) {
|
|
3
|
+
let items = [];
|
|
4
|
+
const requestOptions = typeof param1 !== 'string' ? param1 : {
|
|
5
|
+
url: param1,
|
|
6
|
+
headers: {
|
|
7
|
+
accept: `application/json;odata.metadata=${metadata ?? 'none'}`,
|
|
8
|
+
'odata-version': '4.0'
|
|
9
|
+
},
|
|
10
|
+
responseType: 'json'
|
|
11
|
+
};
|
|
12
|
+
const res = await request.get(requestOptions);
|
|
13
|
+
items = res.value;
|
|
14
|
+
const nextLink = res['@odata.nextLink'] ?? res.nextLink;
|
|
15
|
+
if (nextLink) {
|
|
16
|
+
const nextPageItems = await odata.getAllItems({ ...requestOptions, url: nextLink });
|
|
17
|
+
items = items.concat(nextPageItems);
|
|
21
18
|
}
|
|
19
|
+
return items;
|
|
20
|
+
}
|
|
21
|
+
export const odata = {
|
|
22
|
+
getAllItems
|
|
22
23
|
};
|
|
23
24
|
//# sourceMappingURL=odata.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Cli } from '../cli/Cli.js';
|
|
2
|
+
import { settingsNames } from '../settingsNames.js';
|
|
3
|
+
let inquirer;
|
|
4
|
+
export const prompt = {
|
|
5
|
+
/* c8 ignore next 10 */
|
|
6
|
+
async forInput(config, answers) {
|
|
7
|
+
if (!inquirer) {
|
|
8
|
+
inquirer = await import('inquirer');
|
|
9
|
+
}
|
|
10
|
+
const cli = Cli.getInstance();
|
|
11
|
+
const errorOutput = cli.getSettingWithDefaultValue(settingsNames.errorOutput, 'stderr');
|
|
12
|
+
const prompt = inquirer.createPromptModule({ output: errorOutput === 'stderr' ? process.stderr : process.stdout });
|
|
13
|
+
return await prompt(config, answers);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import Global from '/docs/cmd/_global.mdx';
|
|
2
|
+
import Tabs from '@theme/Tabs';
|
|
3
|
+
import TabItem from '@theme/TabItem';
|
|
4
|
+
|
|
5
|
+
# aad group user list
|
|
6
|
+
|
|
7
|
+
Lists users of a specific Azure AD group
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
m365 aad group user list [options]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Options
|
|
16
|
+
|
|
17
|
+
```md definition-list
|
|
18
|
+
`-i, --groupId [groupId]`
|
|
19
|
+
: The ID of the Azure AD group. Specify `groupId` or `groupDisplayName` but not both.
|
|
20
|
+
|
|
21
|
+
`-n, --groupDisplayName [groupDisplayName]`
|
|
22
|
+
: The display name of the Azure AD group. Specify `groupId` or `groupDisplayName` but not both.
|
|
23
|
+
|
|
24
|
+
`-r, --role [role]`
|
|
25
|
+
: Filter the results to only users with the given role: `Owner`, `Member`.
|
|
26
|
+
|
|
27
|
+
`-p, --properties [properties]`
|
|
28
|
+
: Comma-separated list of properties to retrieve.
|
|
29
|
+
|
|
30
|
+
`-f, --filter [filter]`
|
|
31
|
+
: OData filter to use to query the list of users with.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
<Global />
|
|
35
|
+
|
|
36
|
+
## Remarks
|
|
37
|
+
|
|
38
|
+
When the `properties` option includes values with a `/`, for example: `manager/displayName`, an additional `$expand` query parameter will be included on `manager`.
|
|
39
|
+
|
|
40
|
+
## Examples
|
|
41
|
+
|
|
42
|
+
List all group users from a group specified by ID.
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
m365 aad group user list --groupId 03cba9da-3974-46c1-afaf-79caa2e45bbe
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
List all owners from a group specified by display name.
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
m365 aad group user list --groupDisplayName Developers --role Owner
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
List all group users from a group specified by name. For each one return the display name, e-mail address, and manager display name.
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
m365 aad group user list --groupDisplayName Developers --properties "displayName,mail,manager/displayName"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
List all group users from a group specified by name. For each one return the display name, e-mail address, and manager information.
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
m365 aad group user list --groupDisplayName Developers --properties "displayName,mail,manager/*"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
List all group members that are guest users.
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
m365 aad group user list --groupDisplayName Developers --filter "userType eq 'Guest'"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Response
|
|
73
|
+
|
|
74
|
+
<Tabs>
|
|
75
|
+
<TabItem value="JSON">
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
[
|
|
79
|
+
{
|
|
80
|
+
"id": "da52218e-4822-4ac6-b41d-255e2059655e",
|
|
81
|
+
"displayName": "Adele Vance",
|
|
82
|
+
"userPrincipalName": "AdeleV@contoso.OnMicrosoft.com",
|
|
83
|
+
"givenName": "Adele",
|
|
84
|
+
"surname": "Vance",
|
|
85
|
+
"roles": [
|
|
86
|
+
"Owner",
|
|
87
|
+
"Member"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
</TabItem>
|
|
94
|
+
<TabItem value="Text">
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
id displayName userPrincipalName roles
|
|
98
|
+
------------------------------------ -------------------- ------------------------------------ --------
|
|
99
|
+
da52218e-4822-4ac6-b41d-255e2059655e Adele Vance AdeleV@contoso.OnMicrosoft.com Owner,Member
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
</TabItem>
|
|
103
|
+
<TabItem value="CSV">
|
|
104
|
+
|
|
105
|
+
```csv
|
|
106
|
+
id,displayName,userPrincipalName,givenName,surname
|
|
107
|
+
da52218e-4822-4ac6-b41d-255e2059655e,Adele Vance,AdeleV@contoso.OnMicrosoft.com,Adele,Vance
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
</TabItem>
|
|
111
|
+
<TabItem value="Markdown">
|
|
112
|
+
|
|
113
|
+
```md
|
|
114
|
+
# aad group user list --groupId "1deaa428-8dde-4043-b028-5492226d6114"
|
|
115
|
+
|
|
116
|
+
Date: 2023-10-02
|
|
117
|
+
|
|
118
|
+
## Adele Vance (da52218e-4822-4ac6-b41d-255e2059655e)
|
|
119
|
+
|
|
120
|
+
Property | Value
|
|
121
|
+
---------|-------
|
|
122
|
+
id | da52218e-4822-4ac6-b41d-255e2059655e
|
|
123
|
+
displayName | Adele Vance
|
|
124
|
+
userPrincipalName | AdeleV@contoso.OnMicrosoft.com
|
|
125
|
+
givenName | Adele
|
|
126
|
+
surname | Vance
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
</TabItem>
|
|
130
|
+
</Tabs>
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## More information
|
|
134
|
+
|
|
135
|
+
- View the documentation to see what userproperties can be included: [https://pnp.github.io/cli-microsoft365/cmd/aad/user/user-get#more-information](https://pnp.github.io/cli-microsoft365/cmd/aad/user/user-get#more-information)
|
|
@@ -27,7 +27,17 @@ m365 aad m365group remove [options]
|
|
|
27
27
|
|
|
28
28
|
## Remarks
|
|
29
29
|
|
|
30
|
-
If the specified _id_ doesn't refer to an existing group, you will get a `Resource does not exist` error.
|
|
30
|
+
If the specified _id_ doesn't refer to an existing group, you will get a `Resource does not exist` error. Additionally, if you do not have access to the group or the associated group-connected site, you will get an `Access denied` error.
|
|
31
|
+
|
|
32
|
+
When you use `--skipRecycleBin` flag to remove a Microsoft 365 group permanently, the process involves deleting the associated group-connected site and the group, and then checking if the group has been moved to the deleted groups list. In some cases, it may take a few seconds for the group to appear in the deleted groups list.
|
|
33
|
+
|
|
34
|
+
To ensure a smooth deletion process, the command employs a retry mechanism with the following parameters:
|
|
35
|
+
|
|
36
|
+
- `maxRetries`: This parameter is set to 10 by default. It specifies the maximum number of times the command will check if the group has been moved to the deleted groups list.
|
|
37
|
+
|
|
38
|
+
- `intervalInMs`: This parameter is set to 5000 milliseconds (5 seconds) by default. It defines the time interval between each retry attempt.
|
|
39
|
+
|
|
40
|
+
If the group is successfully moved to the deleted groups list within the specified number of retries, the command will then proceed to permanently remove it from the tenant. If the group cannot be removed after all retries, it will remain in the deleted groups list.
|
|
31
41
|
|
|
32
42
|
## Examples
|
|
33
43
|
|
|
@@ -31,7 +31,7 @@ m365 spo field get [options]
|
|
|
31
31
|
: The ID of the field to retrieve. Specify `id` or `title` but not both.
|
|
32
32
|
|
|
33
33
|
`-t, --title [title]`
|
|
34
|
-
: The display name (case-sensitive) of the field to
|
|
34
|
+
: The display name (case-sensitive) of the field to retrieve. Specify `id` or `title` but not both.
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
<Global />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnp/cli-microsoft365",
|
|
3
|
-
"version": "7.1.0-beta.
|
|
3
|
+
"version": "7.1.0-beta.ba486fa",
|
|
4
4
|
"description": "Manage Microsoft 365 and SharePoint Framework projects on any platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/api.js",
|
|
@@ -31,21 +31,41 @@
|
|
|
31
31
|
"lint": "eslint --ext .ts src"
|
|
32
32
|
},
|
|
33
33
|
"keywords": [
|
|
34
|
-
"
|
|
34
|
+
"adaptive card",
|
|
35
|
+
"ai builder",
|
|
36
|
+
"azure active directory",
|
|
37
|
+
"azure ad",
|
|
38
|
+
"azure",
|
|
39
|
+
"bookings",
|
|
40
|
+
"dataverse",
|
|
41
|
+
"entra id",
|
|
42
|
+
"graph",
|
|
43
|
+
"m365",
|
|
35
44
|
"microsoft 365",
|
|
36
|
-
"
|
|
45
|
+
"microsoft",
|
|
37
46
|
"o365",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
47
|
+
"office 365",
|
|
48
|
+
"onedrive",
|
|
49
|
+
"onenote",
|
|
50
|
+
"outlook",
|
|
51
|
+
"planner",
|
|
52
|
+
"power apps",
|
|
53
|
+
"power automate",
|
|
54
|
+
"power platform",
|
|
55
|
+
"purview",
|
|
56
|
+
"search",
|
|
57
|
+
"sharepoint framework",
|
|
40
58
|
"sharepoint online",
|
|
41
59
|
"sharepoint",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
60
|
+
"skype",
|
|
61
|
+
"spfx",
|
|
62
|
+
"teams",
|
|
63
|
+
"tenant",
|
|
64
|
+
"todo",
|
|
65
|
+
"viva connections",
|
|
66
|
+
"viva engage",
|
|
67
|
+
"viva",
|
|
68
|
+
"yammer"
|
|
49
69
|
],
|
|
50
70
|
"author": "https://github.com/pnp/cli-microsoft365/graphs/contributors",
|
|
51
71
|
"maintainers": [
|
|
@@ -209,7 +229,7 @@
|
|
|
209
229
|
"Thangavel, Sekar <sekar.thangavel@gmail.com>",
|
|
210
230
|
"Thorild, Fredrik <fredrik.thorild@sogeti.com>",
|
|
211
231
|
"Trinder, Garry <garry.trinder@live.com>",
|
|
212
|
-
"Tripathi, Saurabh <
|
|
232
|
+
"Tripathi, Saurabh <saurabh7019@gmail.com>",
|
|
213
233
|
"Vaghasia, Siddharth <siddh.vaghasia@gmail.com>",
|
|
214
234
|
"Van de Voorde, Tim <tim.vdv1995@hotmail.com>",
|
|
215
235
|
"van Dijk, Mark <mark@ichicraft.com>",
|