@pnp/cli-microsoft365 7.2.0 → 7.3.0-beta.01256d2
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/appInsights.js +2 -1
- package/dist/cli/Cli.js +14 -13
- package/dist/index.js +1 -9
- package/dist/m365/aad/commands/group/group-list.js +69 -1
- package/dist/m365/external/commands/connection/connection-doctor.js +424 -0
- package/dist/m365/external/commands/item/item-add.js +144 -0
- package/dist/m365/external/commands.js +3 -1
- package/dist/m365/teams/commands/meeting/meeting-add.js +151 -0
- package/dist/m365/teams/commands/user/user-app-remove.js +33 -2
- package/dist/m365/teams/commands.js +2 -1
- package/dist/telemetry.js +0 -8
- package/dist/utils/validation.js +5 -2
- package/docs/docs/cmd/aad/group/group-list.mdx +11 -0
- package/docs/docs/cmd/external/connection/connection-doctor.mdx +179 -0
- package/docs/docs/cmd/external/item/item-add.mdx +145 -0
- package/docs/docs/cmd/onedrive/report/report-activityfilecounts.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-activityusercounts.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-activityuserdetail.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-usageaccountcounts.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-usageaccountdetail.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-usagefilecounts.mdx +0 -3
- package/docs/docs/cmd/onedrive/report/report-usagestorage.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailactivitycounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailactivityusercounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailactivityuserdetail.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailappusageappsusercounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailappusageusercounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailappusageuserdetail.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailappusageversionsusercounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailboxusagedetail.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailboxusagemailboxcount.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailboxusagequotastatusmailboxcounts.mdx +0 -3
- package/docs/docs/cmd/outlook/report/report-mailboxusagestorage.mdx +0 -3
- package/docs/docs/cmd/skype/report/report-activitycounts.mdx +0 -2
- package/docs/docs/cmd/skype/report/report-activityusercounts.mdx +0 -3
- package/docs/docs/cmd/skype/report/report-activityuserdetail.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-activityfilecounts.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-activitypages.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-activityusercounts.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-activityuserdetail.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-siteusagedetail.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-siteusagefilecounts.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-siteusagepages.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-siteusagesitecounts.mdx +0 -3
- package/docs/docs/cmd/spo/report/report-siteusagestorage.mdx +0 -3
- package/docs/docs/cmd/teams/meeting/meeting-add.mdx +283 -0
- package/docs/docs/cmd/teams/report/report-deviceusagedistributionusercounts.mdx +0 -3
- package/docs/docs/cmd/teams/report/report-deviceusageusercounts.mdx +0 -3
- package/docs/docs/cmd/teams/report/report-deviceusageuserdetail.mdx +0 -3
- package/docs/docs/cmd/teams/report/report-useractivitycounts.mdx +0 -3
- package/docs/docs/cmd/teams/report/report-useractivityusercounts.mdx +0 -3
- package/docs/docs/cmd/teams/report/report-useractivityuserdetail.mdx +0 -3
- package/docs/docs/cmd/teams/user/user-app-remove.mdx +13 -4
- package/docs/docs/cmd/tenant/report/report-activeusercounts.mdx +0 -3
- package/docs/docs/cmd/tenant/report/report-activeuserdetail.mdx +0 -3
- package/docs/docs/cmd/tenant/report/report-servicesusercounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-activitycounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-activityusercounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-activityuserdetail.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-deviceusagedistributionusercounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-deviceusageusercounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-deviceusageuserdetail.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-groupsactivitycounts.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-groupsactivitydetail.mdx +0 -3
- package/docs/docs/cmd/yammer/report/report-groupsactivitygroupcounts.mdx +0 -3
- package/npm-shrinkwrap.json +6 -6
- package/package.json +4 -3
package/dist/appInsights.js
CHANGED
|
@@ -25,6 +25,7 @@ appInsightsClient.commonProperties = {
|
|
|
25
25
|
ci: Boolean(process.env.CI).toString()
|
|
26
26
|
};
|
|
27
27
|
appInsightsClient.context.tags['ai.cloud.roleInstance'] = crypto.createHash('sha256').update(appInsightsClient.context.tags['ai.cloud.roleInstance']).digest('hex');
|
|
28
|
-
appInsightsClient.context.tags['ai.cloud.role'];
|
|
28
|
+
delete appInsightsClient.context.tags['ai.cloud.role'];
|
|
29
|
+
delete appInsightsClient.context.tags['ai.cloud.roleName'];
|
|
29
30
|
export default appInsightsClient;
|
|
30
31
|
//# sourceMappingURL=appInsights.js.map
|
package/dist/cli/Cli.js
CHANGED
|
@@ -100,43 +100,44 @@ export class Cli {
|
|
|
100
100
|
}
|
|
101
101
|
return Promise.resolve();
|
|
102
102
|
}
|
|
103
|
-
|
|
103
|
+
delete this.optionsFromArgs.options._;
|
|
104
|
+
delete this.optionsFromArgs.options['--'];
|
|
104
105
|
try {
|
|
105
106
|
// replace values staring with @ with file contents
|
|
106
|
-
Cli.loadOptionValuesFromFiles(
|
|
107
|
+
Cli.loadOptionValuesFromFiles(this.optionsFromArgs);
|
|
107
108
|
}
|
|
108
109
|
catch (e) {
|
|
109
|
-
return this.closeWithError(e,
|
|
110
|
+
return this.closeWithError(e, this.optionsFromArgs);
|
|
110
111
|
}
|
|
111
112
|
const startProcessing = process.hrtime.bigint();
|
|
112
113
|
try {
|
|
113
114
|
// process options before passing them on to validation stage
|
|
114
|
-
const contextCommandOptions = await this.loadOptionsFromContext(this.commandToExecute.options,
|
|
115
|
-
|
|
116
|
-
await this.commandToExecute.command.processOptions(
|
|
115
|
+
const contextCommandOptions = await this.loadOptionsFromContext(this.commandToExecute.options, this.optionsFromArgs.options.debug);
|
|
116
|
+
this.optionsFromArgs.options = { ...contextCommandOptions, ...this.optionsFromArgs.options };
|
|
117
|
+
await this.commandToExecute.command.processOptions(this.optionsFromArgs.options);
|
|
117
118
|
const endProcessing = process.hrtime.bigint();
|
|
118
119
|
timings.options.push(Number(endProcessing - startProcessing));
|
|
119
120
|
}
|
|
120
121
|
catch (e) {
|
|
121
122
|
const endProcessing = process.hrtime.bigint();
|
|
122
123
|
timings.options.push(Number(endProcessing - startProcessing));
|
|
123
|
-
return this.closeWithError(e.message,
|
|
124
|
+
return this.closeWithError(e.message, this.optionsFromArgs, false);
|
|
124
125
|
}
|
|
125
126
|
// if output not specified, set the configured output value (if any)
|
|
126
|
-
if (
|
|
127
|
-
|
|
127
|
+
if (this.optionsFromArgs.options.output === undefined) {
|
|
128
|
+
this.optionsFromArgs.options.output = this.getSettingWithDefaultValue(settingsNames.output, 'json');
|
|
128
129
|
}
|
|
129
130
|
const startValidation = process.hrtime.bigint();
|
|
130
|
-
const validationResult = await this.commandToExecute.command.validate(
|
|
131
|
+
const validationResult = await this.commandToExecute.command.validate(this.optionsFromArgs, this.commandToExecute);
|
|
131
132
|
const endValidation = process.hrtime.bigint();
|
|
132
133
|
timings.validation.push(Number(endValidation - startValidation));
|
|
133
134
|
if (validationResult !== true) {
|
|
134
|
-
return this.closeWithError(validationResult,
|
|
135
|
+
return this.closeWithError(validationResult, this.optionsFromArgs, true);
|
|
135
136
|
}
|
|
136
137
|
const end = process.hrtime.bigint();
|
|
137
138
|
timings.core.push(Number(end - start));
|
|
138
139
|
try {
|
|
139
|
-
await Cli.executeCommand(this.commandToExecute.command,
|
|
140
|
+
await Cli.executeCommand(this.commandToExecute.command, this.optionsFromArgs);
|
|
140
141
|
const endTotal = process.hrtime.bigint();
|
|
141
142
|
timings.total.push(Number(endTotal - start));
|
|
142
143
|
this.printTimings(rawArgs);
|
|
@@ -146,7 +147,7 @@ export class Cli {
|
|
|
146
147
|
const endTotal = process.hrtime.bigint();
|
|
147
148
|
timings.total.push(Number(endTotal - start));
|
|
148
149
|
this.printTimings(rawArgs);
|
|
149
|
-
await this.closeWithError(err,
|
|
150
|
+
await this.closeWithError(err, this.optionsFromArgs);
|
|
150
151
|
/* c8 ignore next */
|
|
151
152
|
}
|
|
152
153
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Cli } from './cli/Cli.js';
|
|
3
|
-
import { telemetry } from './telemetry.js';
|
|
4
3
|
import { app } from './utils/app.js';
|
|
5
4
|
// required to make console.log() in combination with piped output synchronous
|
|
6
5
|
// on Windows/in PowerShell so that the output is not trimmed by calling
|
|
@@ -14,12 +13,5 @@ if (!process.env.CLIMICROSOFT365_NOUPDATE) {
|
|
|
14
13
|
updateNotifier.default({ pkg: app.packageJson() }).notify({ defer: false });
|
|
15
14
|
});
|
|
16
15
|
}
|
|
17
|
-
|
|
18
|
-
const cli = Cli.getInstance();
|
|
19
|
-
cli.execute(process.argv.slice(2));
|
|
20
|
-
}
|
|
21
|
-
catch (e) {
|
|
22
|
-
telemetry.trackException(e);
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
16
|
+
Cli.getInstance().execute(process.argv.slice(2));
|
|
25
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,3 +1,9 @@
|
|
|
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 _AadGroupListCommand_instances, _a, _AadGroupListCommand_initTelemetry, _AadGroupListCommand_initOptions, _AadGroupListCommand_initValidators;
|
|
1
7
|
import { Cli } from '../../../../cli/Cli.js';
|
|
2
8
|
import { odata } from '../../../../utils/odata.js';
|
|
3
9
|
import GraphCommand from '../../../base/GraphCommand.js';
|
|
@@ -12,9 +18,51 @@ class AadGroupListCommand extends GraphCommand {
|
|
|
12
18
|
defaultProperties() {
|
|
13
19
|
return ['id', 'displayName', 'groupType'];
|
|
14
20
|
}
|
|
21
|
+
constructor() {
|
|
22
|
+
super();
|
|
23
|
+
_AadGroupListCommand_instances.add(this);
|
|
24
|
+
__classPrivateFieldGet(this, _AadGroupListCommand_instances, "m", _AadGroupListCommand_initTelemetry).call(this);
|
|
25
|
+
__classPrivateFieldGet(this, _AadGroupListCommand_instances, "m", _AadGroupListCommand_initOptions).call(this);
|
|
26
|
+
__classPrivateFieldGet(this, _AadGroupListCommand_instances, "m", _AadGroupListCommand_initValidators).call(this);
|
|
27
|
+
}
|
|
15
28
|
async commandAction(logger, args) {
|
|
16
29
|
try {
|
|
17
|
-
|
|
30
|
+
let requestUrl = `${this.resource}/v1.0/groups`;
|
|
31
|
+
let useConsistencyLevelHeader = false;
|
|
32
|
+
if (args.options.type) {
|
|
33
|
+
const groupType = _a.groupTypes.find(g => g.toLowerCase() === args.options.type?.toLowerCase());
|
|
34
|
+
switch (groupType) {
|
|
35
|
+
case 'microsoft365':
|
|
36
|
+
requestUrl += `?$filter=groupTypes/any(c:c+eq+'Unified')`;
|
|
37
|
+
break;
|
|
38
|
+
case 'security':
|
|
39
|
+
requestUrl += '?$filter=securityEnabled eq true and mailEnabled eq false';
|
|
40
|
+
break;
|
|
41
|
+
case 'distribution':
|
|
42
|
+
requestUrl += '?$filter=securityEnabled eq false and mailEnabled eq true';
|
|
43
|
+
break;
|
|
44
|
+
case 'mailEnabledSecurity':
|
|
45
|
+
useConsistencyLevelHeader = true;
|
|
46
|
+
requestUrl += `?$filter=securityEnabled eq true and mailEnabled eq true and not(groupTypes/any(t:t eq 'Unified'))&$count=true`;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
let groups = [];
|
|
51
|
+
if (useConsistencyLevelHeader) {
|
|
52
|
+
// While using not() function in the filter, we need to specify the ConsistencyLevel header.
|
|
53
|
+
const requestOptions = {
|
|
54
|
+
url: requestUrl,
|
|
55
|
+
headers: {
|
|
56
|
+
accept: 'application/json;odata.metadata=none',
|
|
57
|
+
ConsistencyLevel: 'eventual'
|
|
58
|
+
},
|
|
59
|
+
responseType: 'json'
|
|
60
|
+
};
|
|
61
|
+
groups = await odata.getAllItems(requestOptions);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
groups = await odata.getAllItems(requestUrl);
|
|
65
|
+
}
|
|
18
66
|
if (Cli.shouldTrimOutput(args.options.output)) {
|
|
19
67
|
groups.forEach((group) => {
|
|
20
68
|
if (group.groupTypes && group.groupTypes.length > 0 && group.groupTypes[0] === 'Unified') {
|
|
@@ -38,5 +86,25 @@ class AadGroupListCommand extends GraphCommand {
|
|
|
38
86
|
}
|
|
39
87
|
}
|
|
40
88
|
}
|
|
89
|
+
_a = AadGroupListCommand, _AadGroupListCommand_instances = new WeakSet(), _AadGroupListCommand_initTelemetry = function _AadGroupListCommand_initTelemetry() {
|
|
90
|
+
this.telemetry.push((args) => {
|
|
91
|
+
Object.assign(this.telemetryProperties, {
|
|
92
|
+
type: typeof args.options.type !== 'undefined'
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}, _AadGroupListCommand_initOptions = function _AadGroupListCommand_initOptions() {
|
|
96
|
+
this.options.unshift({
|
|
97
|
+
option: '--type [type]',
|
|
98
|
+
autocomplete: _a.groupTypes
|
|
99
|
+
});
|
|
100
|
+
}, _AadGroupListCommand_initValidators = function _AadGroupListCommand_initValidators() {
|
|
101
|
+
this.validators.push(async (args) => {
|
|
102
|
+
if (args.options.type && _a.groupTypes.every(g => g.toLowerCase() !== args.options.type?.toLowerCase())) {
|
|
103
|
+
return `${args.options.type} is not a valid type value. Allowed values microsoft365|security|distribution|mailEnabledSecurity.`;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
AadGroupListCommand.groupTypes = ['microsoft365', 'security', 'distribution', 'mailEnabledSecurity'];
|
|
41
109
|
export default new AadGroupListCommand();
|
|
42
110
|
//# sourceMappingURL=group-list.js.map
|
|
@@ -0,0 +1,424 @@
|
|
|
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 _ExternalConnectionDoctorCommand_instances, _a, _ExternalConnectionDoctorCommand_initOptions, _ExternalConnectionDoctorCommand_initValidators;
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import { Cli } from '../../../../cli/Cli.js';
|
|
9
|
+
import request from '../../../../request.js';
|
|
10
|
+
import { settingsNames } from '../../../../settingsNames.js';
|
|
11
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
12
|
+
import commands from '../../commands.js';
|
|
13
|
+
class ExternalConnectionDoctorCommand extends GraphCommand {
|
|
14
|
+
get name() {
|
|
15
|
+
return commands.CONNECTION_DOCTOR;
|
|
16
|
+
}
|
|
17
|
+
get description() {
|
|
18
|
+
return 'Checks if the external connection is correctly configured for use with the specified Microsoft 365 experience';
|
|
19
|
+
}
|
|
20
|
+
constructor() {
|
|
21
|
+
super();
|
|
22
|
+
_ExternalConnectionDoctorCommand_instances.add(this);
|
|
23
|
+
this.checksStatus = [];
|
|
24
|
+
__classPrivateFieldGet(this, _ExternalConnectionDoctorCommand_instances, "m", _ExternalConnectionDoctorCommand_initOptions).call(this);
|
|
25
|
+
__classPrivateFieldGet(this, _ExternalConnectionDoctorCommand_instances, "m", _ExternalConnectionDoctorCommand_initValidators).call(this);
|
|
26
|
+
}
|
|
27
|
+
async commandAction(logger, args) {
|
|
28
|
+
const ux = args.options.ux ?? 'all';
|
|
29
|
+
const output = args.options.output;
|
|
30
|
+
this.checksStatus = [];
|
|
31
|
+
const cli = Cli.getInstance();
|
|
32
|
+
const showSpinner = cli.getSettingWithDefaultValue(settingsNames.showSpinner, true) &&
|
|
33
|
+
output === 'text' &&
|
|
34
|
+
typeof global.it === 'undefined';
|
|
35
|
+
let checks = [
|
|
36
|
+
{
|
|
37
|
+
id: 'loadExternalConnection',
|
|
38
|
+
text: 'Load connection',
|
|
39
|
+
fn: this.loadConnection,
|
|
40
|
+
type: 'required'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'loadSchema',
|
|
44
|
+
text: 'Load schema',
|
|
45
|
+
fn: this.loadSchema,
|
|
46
|
+
type: 'required'
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
if (ux === 'copilot' || ux === 'all') {
|
|
50
|
+
checks.push({
|
|
51
|
+
id: 'copilotRequiredSemanticLabels',
|
|
52
|
+
text: 'Required semantic labels',
|
|
53
|
+
fn: this.checkCopilotRequiredSemanticLabels,
|
|
54
|
+
type: 'required'
|
|
55
|
+
}, {
|
|
56
|
+
id: 'searchableProperties',
|
|
57
|
+
text: 'Searchable properties',
|
|
58
|
+
fn: this.checkSearchableProperties,
|
|
59
|
+
type: 'required'
|
|
60
|
+
}, {
|
|
61
|
+
id: 'contentIngested',
|
|
62
|
+
text: 'Items have content ingested',
|
|
63
|
+
fn: this.checkContentIngested,
|
|
64
|
+
type: 'required'
|
|
65
|
+
}, {
|
|
66
|
+
id: 'enabledForInlineResults',
|
|
67
|
+
text: 'Connection configured for inline results',
|
|
68
|
+
type: 'required'
|
|
69
|
+
}, {
|
|
70
|
+
id: 'itemsHaveActivities',
|
|
71
|
+
text: 'Items have activities recorded',
|
|
72
|
+
type: 'recommended'
|
|
73
|
+
}, {
|
|
74
|
+
id: 'meaningfulNameAndDescription',
|
|
75
|
+
text: 'Meaningful connection name and description',
|
|
76
|
+
type: 'required'
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (ux === 'search' || ux === 'all') {
|
|
80
|
+
checks.push({
|
|
81
|
+
id: 'semanticLabels',
|
|
82
|
+
text: 'Semantic labels',
|
|
83
|
+
fn: this.checkSemanticLabels,
|
|
84
|
+
type: 'recommended'
|
|
85
|
+
}, {
|
|
86
|
+
id: 'searchableProperties',
|
|
87
|
+
text: 'Searchable properties',
|
|
88
|
+
fn: this.checkSearchableProperties,
|
|
89
|
+
type: 'recommended'
|
|
90
|
+
}, {
|
|
91
|
+
id: 'resultType',
|
|
92
|
+
text: 'Result type',
|
|
93
|
+
fn: this.checkResultType,
|
|
94
|
+
type: 'recommended'
|
|
95
|
+
}, {
|
|
96
|
+
id: 'contentIngested',
|
|
97
|
+
text: 'Items have content ingested',
|
|
98
|
+
fn: this.checkContentIngested,
|
|
99
|
+
type: 'recommended'
|
|
100
|
+
}, {
|
|
101
|
+
id: 'itemsHaveActivities',
|
|
102
|
+
text: 'Items have activities recorded',
|
|
103
|
+
type: 'recommended'
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
checks.push({
|
|
107
|
+
id: 'urlToItemResolver',
|
|
108
|
+
text: 'urlToItemResolver configured',
|
|
109
|
+
fn: this.checkUrlToItemResolverConfigured,
|
|
110
|
+
type: 'recommended'
|
|
111
|
+
});
|
|
112
|
+
// filter out duplicate checks based on their IDs
|
|
113
|
+
checks = checks.filter((check, index, self) => self.findIndex(c => c.id === check.id) === index);
|
|
114
|
+
for (const check of checks) {
|
|
115
|
+
if (this.debug) {
|
|
116
|
+
logger.logToStderr(`Running check ${check.id}...`);
|
|
117
|
+
}
|
|
118
|
+
// don't show spinner if running tests
|
|
119
|
+
/* c8 ignore next 3 */
|
|
120
|
+
if (showSpinner) {
|
|
121
|
+
cli.spinner.start(check.text);
|
|
122
|
+
}
|
|
123
|
+
// only automated checks have functions
|
|
124
|
+
if (!check.fn) {
|
|
125
|
+
// don't show spinner if running tests
|
|
126
|
+
/* c8 ignore next 3 */
|
|
127
|
+
if (showSpinner) {
|
|
128
|
+
cli.spinner.info(`${check.text} (manual)`);
|
|
129
|
+
}
|
|
130
|
+
this.checksStatus.push({
|
|
131
|
+
...check,
|
|
132
|
+
status: 'manual'
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const result = await check.fn.bind(this)(check.id, args);
|
|
137
|
+
this.checksStatus.push({ ...check, ...result });
|
|
138
|
+
if (result.status === 'passed') {
|
|
139
|
+
// don't show spinner if running tests
|
|
140
|
+
/* c8 ignore next 3 */
|
|
141
|
+
if (showSpinner) {
|
|
142
|
+
cli.spinner.succeed();
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (result.status === 'failed') {
|
|
147
|
+
// don't show spinner if running tests
|
|
148
|
+
/* c8 ignore next 9 */
|
|
149
|
+
if (showSpinner) {
|
|
150
|
+
const message = `${check.text}: ${result.errorMessage}`;
|
|
151
|
+
if (check.type === 'required') {
|
|
152
|
+
cli.spinner.fail(message);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
cli.spinner.warn(message);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (result.shouldStop) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (output === 'text' || output === 'none') {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.checksStatus.forEach(s => {
|
|
167
|
+
delete s.data;
|
|
168
|
+
delete s.fn;
|
|
169
|
+
delete s.shouldStop;
|
|
170
|
+
});
|
|
171
|
+
if (output === 'json' || output === 'md') {
|
|
172
|
+
await logger.log(this.checksStatus);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (output === 'csv') {
|
|
176
|
+
this.checksStatus.forEach(r => {
|
|
177
|
+
// we need to set errorMessage to empty string so that it's not
|
|
178
|
+
// removed from the CSV output
|
|
179
|
+
r.errorMessage = r.errorMessage ?? '';
|
|
180
|
+
});
|
|
181
|
+
await logger.log(this.checksStatus);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
getMdOutput(logStatement, command, options) {
|
|
185
|
+
const output = [
|
|
186
|
+
`# ${command.getCommandName()} ${Object.keys(options).filter(o => o !== 'output').map(k => `--${k} "${options[k]}"`).join(' ')}`, os.EOL,
|
|
187
|
+
os.EOL,
|
|
188
|
+
`Date: ${(new Date().toLocaleDateString())}`, os.EOL,
|
|
189
|
+
os.EOL
|
|
190
|
+
];
|
|
191
|
+
if (logStatement && logStatement.length > 0) {
|
|
192
|
+
const properties = ['text', 'type', 'status', 'errorMessage'];
|
|
193
|
+
output.push('Check|Type|Status|Error message', os.EOL);
|
|
194
|
+
output.push(':----|:--:|:----:|:------------', os.EOL);
|
|
195
|
+
logStatement.forEach(r => {
|
|
196
|
+
output.push(properties.map(p => r[p] ?? '').join('|'), os.EOL);
|
|
197
|
+
});
|
|
198
|
+
logStatement.push(os.EOL);
|
|
199
|
+
}
|
|
200
|
+
return output.join('').trimEnd();
|
|
201
|
+
}
|
|
202
|
+
async loadConnection(id, args) {
|
|
203
|
+
const requestOptions = {
|
|
204
|
+
url: `${this.resource}/v1.0/external/connections/${args.options.id}`,
|
|
205
|
+
headers: {
|
|
206
|
+
accept: 'application/json;odata.metadata=none'
|
|
207
|
+
},
|
|
208
|
+
responseType: 'json'
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
const externalConnection = await request.get(requestOptions);
|
|
212
|
+
return {
|
|
213
|
+
id,
|
|
214
|
+
data: externalConnection,
|
|
215
|
+
status: 'passed'
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
catch (ex) {
|
|
219
|
+
return {
|
|
220
|
+
id,
|
|
221
|
+
error: ex?.response?.data?.error?.innerError?.message,
|
|
222
|
+
errorMessage: 'Connection not found',
|
|
223
|
+
shouldStop: true,
|
|
224
|
+
status: 'failed'
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async loadSchema(id, args) {
|
|
229
|
+
const requestOptions = {
|
|
230
|
+
url: `${this.resource}/v1.0/external/connections/${args.options.id}/schema`,
|
|
231
|
+
headers: {
|
|
232
|
+
accept: 'application/json;odata.metadata=none'
|
|
233
|
+
},
|
|
234
|
+
responseType: 'json'
|
|
235
|
+
};
|
|
236
|
+
try {
|
|
237
|
+
const schema = await request.get(requestOptions);
|
|
238
|
+
return {
|
|
239
|
+
id,
|
|
240
|
+
data: schema,
|
|
241
|
+
status: 'passed'
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (ex) {
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
errorMessage: 'Schema not found',
|
|
248
|
+
error: ex?.response?.data?.error?.innerError?.message,
|
|
249
|
+
shouldStop: true,
|
|
250
|
+
status: 'failed'
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async checkCopilotRequiredSemanticLabels(id) {
|
|
255
|
+
const schema = this.checksStatus.find(r => r.id === 'loadSchema').data;
|
|
256
|
+
const requiredLabels = ['title', 'url', 'iconUrl'];
|
|
257
|
+
for (const label of requiredLabels) {
|
|
258
|
+
if (!schema.properties?.find(p => p.labels?.find(l => l.toString() === label))) {
|
|
259
|
+
return {
|
|
260
|
+
id,
|
|
261
|
+
errorMessage: `Missing label ${label}`,
|
|
262
|
+
status: 'failed'
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
id,
|
|
268
|
+
status: 'passed'
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
async checkSearchableProperties(id) {
|
|
272
|
+
const schema = this.checksStatus.find(r => r.id === 'loadSchema').data;
|
|
273
|
+
if (!schema.properties?.some(p => p.isSearchable)) {
|
|
274
|
+
return {
|
|
275
|
+
id,
|
|
276
|
+
errorMessage: 'Schema does not have any searchable properties',
|
|
277
|
+
status: 'failed'
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
id,
|
|
282
|
+
status: 'passed'
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
async checkContentIngested(id, args) {
|
|
286
|
+
try {
|
|
287
|
+
// find items that belong to the connection
|
|
288
|
+
const searchRequestOptions = {
|
|
289
|
+
url: `${this.resource}/v1.0/search/query`,
|
|
290
|
+
headers: {
|
|
291
|
+
accept: 'application/json;odata.metadata=none'
|
|
292
|
+
},
|
|
293
|
+
responseType: 'json',
|
|
294
|
+
data: {
|
|
295
|
+
requests: [
|
|
296
|
+
{
|
|
297
|
+
entityTypes: [
|
|
298
|
+
'externalItem'
|
|
299
|
+
],
|
|
300
|
+
contentSources: [
|
|
301
|
+
`/external/connections/${args.options.id}`
|
|
302
|
+
],
|
|
303
|
+
query: {
|
|
304
|
+
queryString: '*'
|
|
305
|
+
},
|
|
306
|
+
from: 0,
|
|
307
|
+
size: 1
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
const result = await request.post(searchRequestOptions);
|
|
313
|
+
const hit = result.value?.[0].hitsContainers?.[0]?.hits?.[0];
|
|
314
|
+
if (!hit) {
|
|
315
|
+
return {
|
|
316
|
+
id,
|
|
317
|
+
errorMessage: 'No items found that belong to the connection',
|
|
318
|
+
status: 'failed'
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
// something@tenant,itemId
|
|
322
|
+
const itemId = hit.resource?.properties?.substrateContentDomainId?.split(',')?.[1];
|
|
323
|
+
if (!itemId) {
|
|
324
|
+
return {
|
|
325
|
+
id,
|
|
326
|
+
errorMessage: 'Item does not have substrateContentDomainId property or the property is invalid',
|
|
327
|
+
status: 'failed'
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
const externalItemRequestOptions = {
|
|
331
|
+
url: `${this.resource}/v1.0/external/connections/${args.options.id}/items/${itemId}`,
|
|
332
|
+
headers: {
|
|
333
|
+
accept: 'application/json;odata.metadata=none'
|
|
334
|
+
},
|
|
335
|
+
responseType: 'json'
|
|
336
|
+
};
|
|
337
|
+
const externalItem = await request.get(externalItemRequestOptions);
|
|
338
|
+
if (!externalItem.content?.value) {
|
|
339
|
+
return {
|
|
340
|
+
id,
|
|
341
|
+
data: externalItem,
|
|
342
|
+
errorMessage: 'Item does not have content or content is empty',
|
|
343
|
+
status: 'failed'
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
id,
|
|
348
|
+
data: externalItem,
|
|
349
|
+
status: 'passed'
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
catch (ex) {
|
|
353
|
+
return {
|
|
354
|
+
id,
|
|
355
|
+
error: ex?.response?.data?.error?.innerError?.message,
|
|
356
|
+
errorMessage: 'Error while checking if content is ingested',
|
|
357
|
+
status: 'failed'
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async checkUrlToItemResolverConfigured(id) {
|
|
362
|
+
const externalConnection = this.checksStatus.find(r => r.id === 'loadExternalConnection').data;
|
|
363
|
+
if (!externalConnection.activitySettings?.urlToItemResolvers?.some(r => r)) {
|
|
364
|
+
return {
|
|
365
|
+
id,
|
|
366
|
+
errorMessage: 'urlToItemResolver is not configured',
|
|
367
|
+
status: 'failed'
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
id,
|
|
372
|
+
status: 'passed'
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
async checkSemanticLabels(id) {
|
|
376
|
+
const schema = this.checksStatus.find(r => r.id === 'loadSchema').data;
|
|
377
|
+
const hasLabels = schema.properties?.some(p => p.labels?.some(l => l));
|
|
378
|
+
if (!hasLabels) {
|
|
379
|
+
return {
|
|
380
|
+
id,
|
|
381
|
+
errorMessage: `Schema does not have semantic labels`,
|
|
382
|
+
status: 'failed'
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
id,
|
|
387
|
+
status: 'passed'
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
async checkResultType(id) {
|
|
391
|
+
const externalConnection = this.checksStatus.find(r => r.id === 'loadExternalConnection').data;
|
|
392
|
+
if (!externalConnection.searchSettings?.searchResultTemplates?.some(t => t)) {
|
|
393
|
+
return {
|
|
394
|
+
id,
|
|
395
|
+
errorMessage: `Connection has no result types`,
|
|
396
|
+
status: 'failed'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
id,
|
|
401
|
+
status: 'passed'
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
_a = ExternalConnectionDoctorCommand, _ExternalConnectionDoctorCommand_instances = new WeakSet(), _ExternalConnectionDoctorCommand_initOptions = function _ExternalConnectionDoctorCommand_initOptions() {
|
|
406
|
+
this.options.unshift({
|
|
407
|
+
option: '-i, --id <id>'
|
|
408
|
+
}, {
|
|
409
|
+
option: '--ux [ux]',
|
|
410
|
+
autocomplete: _a.supportedUx
|
|
411
|
+
});
|
|
412
|
+
}, _ExternalConnectionDoctorCommand_initValidators = function _ExternalConnectionDoctorCommand_initValidators() {
|
|
413
|
+
this.validators.push(async (args) => {
|
|
414
|
+
if (args.options.ux) {
|
|
415
|
+
if (!_a.supportedUx.find(u => u === args.options.ux)) {
|
|
416
|
+
return `${args.options.ux} is not a valid UX. Allowed values are ${_a.supportedUx.join(', ')}`;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return true;
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
ExternalConnectionDoctorCommand.supportedUx = ['copilot', 'search', 'all'];
|
|
423
|
+
export default new ExternalConnectionDoctorCommand();
|
|
424
|
+
//# sourceMappingURL=connection-doctor.js.map
|