@pnp/cli-microsoft365 10.5.0-beta.395d550 → 10.5.0-beta.63404d1
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/.eslintrc.cjs +1 -0
- package/README.md +1 -1
- package/allCommands.json +1 -1
- package/allCommandsFull.json +1 -1
- package/dist/Auth.js +61 -3
- package/dist/appInsights.js +10 -4
- package/dist/m365/commands/login.js +3 -0
- package/dist/m365/graph/commands/directoryextension/directoryextension-add.js +85 -0
- package/dist/m365/graph/commands/directoryextension/directoryextension-get.js +82 -0
- package/dist/m365/graph/commands/directoryextension/directoryextension-remove.js +93 -0
- package/dist/m365/graph/commands.js +3 -0
- package/dist/m365/spo/commands/homesite/homesite-add.js +66 -0
- package/dist/m365/spo/commands/page/page-header-set.js +47 -10
- package/dist/m365/spo/commands/page/page-section-remove.js +81 -0
- package/dist/m365/spo/commands.js +2 -0
- package/dist/m365/viva/commands/engage/engage-community-user-add.js +113 -0
- package/dist/m365/viva/commands/engage/engage-community-user-remove.js +107 -0
- package/dist/m365/viva/commands.js +2 -0
- package/dist/request.js +5 -0
- package/dist/utils/directoryExtension.js +25 -0
- package/dist/utils/entraApp.js +28 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-add.mdx +123 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-get.mdx +118 -0
- package/docs/docs/cmd/graph/directoryextension/directoryextension-remove.mdx +57 -0
- package/docs/docs/cmd/login.mdx +9 -1
- package/docs/docs/cmd/spo/homesite/homesite-add.mdx +124 -0
- package/docs/docs/cmd/spo/page/page-section-remove.mdx +47 -0
- package/docs/docs/cmd/viva/engage/engage-community-user-add.mdx +67 -0
- package/docs/docs/cmd/viva/engage/engage-community-user-remove.mdx +55 -0
- package/npm-shrinkwrap.json +97 -84
- package/package.json +14 -14
package/dist/Auth.js
CHANGED
|
@@ -53,6 +53,7 @@ export var AuthType;
|
|
|
53
53
|
AuthType["Password"] = "password";
|
|
54
54
|
AuthType["Certificate"] = "certificate";
|
|
55
55
|
AuthType["Identity"] = "identity";
|
|
56
|
+
AuthType["FederatedIdentity"] = "federatedIdentity";
|
|
56
57
|
AuthType["Browser"] = "browser";
|
|
57
58
|
AuthType["Secret"] = "secret";
|
|
58
59
|
})(AuthType || (AuthType = {}));
|
|
@@ -153,7 +154,8 @@ export class Auth {
|
|
|
153
154
|
// wasn't specified
|
|
154
155
|
if (this.connection.authType !== AuthType.Certificate &&
|
|
155
156
|
this.connection.authType !== AuthType.Secret &&
|
|
156
|
-
this.connection.authType !== AuthType.Identity
|
|
157
|
+
this.connection.authType !== AuthType.Identity &&
|
|
158
|
+
this.connection.authType !== AuthType.FederatedIdentity) {
|
|
157
159
|
this.clientApplication = await this.getPublicClient(logger, debug);
|
|
158
160
|
if (this.clientApplication) {
|
|
159
161
|
const accounts = await this.clientApplication.getTokenCache().getAllAccounts();
|
|
@@ -177,6 +179,9 @@ export class Auth {
|
|
|
177
179
|
case AuthType.Identity:
|
|
178
180
|
getTokenPromise = this.ensureAccessTokenWithIdentity.bind(this);
|
|
179
181
|
break;
|
|
182
|
+
case AuthType.FederatedIdentity:
|
|
183
|
+
getTokenPromise = this.ensureAccessTokenWithFederatedIdentity.bind(this);
|
|
184
|
+
break;
|
|
180
185
|
case AuthType.Browser:
|
|
181
186
|
getTokenPromise = this.ensureAccessTokenWithBrowser.bind(this);
|
|
182
187
|
break;
|
|
@@ -552,6 +557,56 @@ export class Auth {
|
|
|
552
557
|
}
|
|
553
558
|
}
|
|
554
559
|
}
|
|
560
|
+
async ensureAccessTokenWithFederatedIdentity(resource, logger, debug) {
|
|
561
|
+
if (debug) {
|
|
562
|
+
await logger.logToStderr('Trying to retrieve access token using federated identity...');
|
|
563
|
+
}
|
|
564
|
+
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL || !process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) {
|
|
565
|
+
throw new CommandError('Federated identity is currently only supported in GitHub Actions.');
|
|
566
|
+
}
|
|
567
|
+
if (debug) {
|
|
568
|
+
await logger.logToStderr('ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN env variables found. The context is GitHub Actions...');
|
|
569
|
+
}
|
|
570
|
+
const federationToken = await this.getFederationTokenFromGithub(logger, debug);
|
|
571
|
+
const queryParams = [
|
|
572
|
+
'grant_type=client_credentials',
|
|
573
|
+
`scope=${encodeURIComponent(`${resource}/.default`)}`,
|
|
574
|
+
`client_id=${this.connection.appId}`,
|
|
575
|
+
`client_assertion_type=${encodeURIComponent('urn:ietf:params:oauth:client-assertion-type:jwt-bearer')}`,
|
|
576
|
+
`client_assertion=${federationToken}`
|
|
577
|
+
];
|
|
578
|
+
const requestOptions = {
|
|
579
|
+
url: `https://login.microsoftonline.com/${this.connection.tenant}/oauth2/v2.0/token`,
|
|
580
|
+
headers: {
|
|
581
|
+
accept: 'application/json',
|
|
582
|
+
'x-anonymous': true,
|
|
583
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
584
|
+
},
|
|
585
|
+
data: queryParams.join('&'),
|
|
586
|
+
responseType: 'json'
|
|
587
|
+
};
|
|
588
|
+
const accessTokenResponse = await request.post(requestOptions);
|
|
589
|
+
return {
|
|
590
|
+
accessToken: accessTokenResponse.access_token,
|
|
591
|
+
expiresOn: new Date(parseInt(accessTokenResponse.expires_on) * 1000)
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
async getFederationTokenFromGithub(logger, debug) {
|
|
595
|
+
if (debug) {
|
|
596
|
+
await logger.logToStderr('Retrieving GitHub federation token...');
|
|
597
|
+
}
|
|
598
|
+
const requestOptions = {
|
|
599
|
+
url: `${process.env.ACTIONS_ID_TOKEN_REQUEST_URL}&audience=${encodeURIComponent('api://AzureADTokenExchange')}`,
|
|
600
|
+
headers: {
|
|
601
|
+
Authorization: `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`,
|
|
602
|
+
accept: 'application/json',
|
|
603
|
+
'x-anonymous': true
|
|
604
|
+
},
|
|
605
|
+
responseType: 'json'
|
|
606
|
+
};
|
|
607
|
+
const accessTokenResponse = await request.get(requestOptions);
|
|
608
|
+
return accessTokenResponse.value;
|
|
609
|
+
}
|
|
555
610
|
async ensureAccessTokenWithSecret(resource, logger, debug, fetchNew) {
|
|
556
611
|
this.clientApplication = await this.getConfidentialClient(logger, debug, undefined, undefined, this.connection.secret);
|
|
557
612
|
return this.clientApplication.acquireTokenByClientCredential({
|
|
@@ -597,7 +652,9 @@ export class Auth {
|
|
|
597
652
|
await connectionStorage.set(JSON.stringify(this.connection));
|
|
598
653
|
let allConnections = await this.getAllConnections();
|
|
599
654
|
if (this.connection.active) {
|
|
600
|
-
allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId
|
|
655
|
+
allConnections = allConnections.filter(c => c.identityId !== this.connection.identityId ||
|
|
656
|
+
c.appId !== this.connection.appId ||
|
|
657
|
+
c.tenant !== this.connection.tenant);
|
|
601
658
|
allConnections.forEach(c => c.active = false);
|
|
602
659
|
allConnections = [{ ...this.connection }, ...allConnections];
|
|
603
660
|
}
|
|
@@ -624,7 +681,8 @@ export class Auth {
|
|
|
624
681
|
// When using an application identity, there is no account in the MSAL TokenCache
|
|
625
682
|
if (this.connection.authType !== AuthType.Certificate &&
|
|
626
683
|
this.connection.authType !== AuthType.Secret &&
|
|
627
|
-
this.connection.authType !== AuthType.Identity
|
|
684
|
+
this.connection.authType !== AuthType.Identity &&
|
|
685
|
+
this.connection.authType !== AuthType.FederatedIdentity) {
|
|
628
686
|
this.clientApplication = await this.getPublicClient(logger, debug);
|
|
629
687
|
if (this.clientApplication) {
|
|
630
688
|
const tokenCache = this.clientApplication.getTokenCache();
|
package/dist/appInsights.js
CHANGED
|
@@ -5,7 +5,7 @@ process.env.APPLICATION_INSIGHTS_NO_DIAGNOSTIC_CHANNEL = 'none';
|
|
|
5
5
|
process.env.APPLICATION_INSIGHTS_NO_STATSBEAT = 'true';
|
|
6
6
|
// suppress all logging
|
|
7
7
|
process.env.APPLICATIONINSIGHTS_INSTRUMENTATION_LOGGING_LEVEL = 'NONE';
|
|
8
|
-
import
|
|
8
|
+
import appInsights from 'applicationinsights';
|
|
9
9
|
import crypto from 'crypto';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import os from 'os';
|
|
@@ -15,21 +15,27 @@ import { app } from './utils/app.js';
|
|
|
15
15
|
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
|
|
16
16
|
appInsights
|
|
17
17
|
.setup('InstrumentationKey=6b908c80-d09f-4cf6-8274-e54349a0149a;IngestionEndpoint=https://westeurope-3.in.applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;ApplicationId=d42020c6-da32-44f7-bbb7-666f0b3a664d')
|
|
18
|
+
.setAutoCollectRequests(false)
|
|
19
|
+
.setAutoCollectPerformance(false, false)
|
|
20
|
+
.setAutoCollectExceptions(false)
|
|
21
|
+
.setAutoCollectDependencies(false)
|
|
22
|
+
.setAutoCollectConsole(false, false)
|
|
23
|
+
.setAutoCollectPreAggregatedMetrics(false)
|
|
24
|
+
.setSendLiveMetrics(false)
|
|
25
|
+
.enableWebInstrumentation(false)
|
|
18
26
|
.setInternalLogging(false, false);
|
|
19
27
|
// append -dev to the version number when ran locally
|
|
20
28
|
// to distinguish production and dev version of the CLI
|
|
21
29
|
// in the telemetry
|
|
22
30
|
const version = `${app.packageJson().version}${fs.existsSync(path.join(__dirname, `..${path.sep}src`)) ? '-dev' : ''}`;
|
|
23
31
|
const env = process.env.CLIMICROSOFT365_ENV !== undefined ? process.env.CLIMICROSOFT365_ENV : '';
|
|
24
|
-
const appInsightsClient = appInsights.
|
|
32
|
+
const appInsightsClient = appInsights.defaultClient;
|
|
25
33
|
appInsightsClient.commonProperties = {
|
|
26
34
|
version: version,
|
|
27
35
|
node: process.version,
|
|
28
36
|
env: env,
|
|
29
37
|
ci: Boolean(process.env.CI).toString()
|
|
30
38
|
};
|
|
31
|
-
appInsightsClient.config.proxyHttpUrl = process.env.HTTP_PROXY ?? '';
|
|
32
|
-
appInsightsClient.config.proxyHttpsUrl = process.env.HTTPS_PROXY ?? '';
|
|
33
39
|
appInsightsClient.context.tags[appInsightsClient.context.keys.cloudRoleInstance] = crypto.createHash('sha256').update(os.hostname()).digest('hex');
|
|
34
40
|
delete appInsightsClient.context.tags[appInsightsClient.context.keys.cloudRole];
|
|
35
41
|
delete appInsightsClient.context.tags['ai.cloud.roleName'];
|
|
@@ -184,6 +184,9 @@ class LoginCommand extends Command {
|
|
|
184
184
|
auth.connection.authType = AuthType.Identity;
|
|
185
185
|
auth.connection.userName = args.options.userName;
|
|
186
186
|
break;
|
|
187
|
+
case 'federatedIdentity':
|
|
188
|
+
auth.connection.authType = AuthType.FederatedIdentity;
|
|
189
|
+
break;
|
|
187
190
|
case 'browser':
|
|
188
191
|
auth.connection.authType = AuthType.Browser;
|
|
189
192
|
break;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import request from '../../../../request.js';
|
|
4
|
+
import { zod } from '../../../../utils/zod.js';
|
|
5
|
+
import commands from '../../commands.js';
|
|
6
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
7
|
+
import { validation } from '../../../../utils/validation.js';
|
|
8
|
+
import { entraApp } from '../../../../utils/entraApp.js';
|
|
9
|
+
const options = globalOptionsZod
|
|
10
|
+
.extend({
|
|
11
|
+
name: zod.alias('n', z.string()),
|
|
12
|
+
appId: z.string().optional(),
|
|
13
|
+
appObjectId: z.string().optional(),
|
|
14
|
+
appName: z.string().optional(),
|
|
15
|
+
dataType: z.enum(['Binary', 'Boolean', 'DateTime', 'Integer', 'LargeInteger', 'String']),
|
|
16
|
+
targetObjects: z.string().transform((value) => value.split(',').map(String))
|
|
17
|
+
.pipe(z.enum(['User', 'Group', 'Application', 'AdministrativeUnit', 'Device', 'Organization']).array()),
|
|
18
|
+
isMultiValued: z.boolean().optional()
|
|
19
|
+
})
|
|
20
|
+
.strict();
|
|
21
|
+
class GraphDirectoryExtensionAddCommand extends GraphCommand {
|
|
22
|
+
get name() {
|
|
23
|
+
return commands.DIRECTORYEXTENSION_ADD;
|
|
24
|
+
}
|
|
25
|
+
get description() {
|
|
26
|
+
return 'Creates a new directory extension';
|
|
27
|
+
}
|
|
28
|
+
get schema() {
|
|
29
|
+
return options;
|
|
30
|
+
}
|
|
31
|
+
getRefinedSchema(schema) {
|
|
32
|
+
return schema
|
|
33
|
+
.refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, {
|
|
34
|
+
message: 'Specify either appId, appObjectId or appName, but not multiple'
|
|
35
|
+
})
|
|
36
|
+
.refine(options => (!options.appId && !options.appObjectId && !options.appName) || options.appObjectId || options.appName ||
|
|
37
|
+
(options.appId && validation.isValidGuid(options.appId)), options => ({
|
|
38
|
+
message: `The '${options.appId}' must be a valid GUID`,
|
|
39
|
+
path: ['appId']
|
|
40
|
+
}))
|
|
41
|
+
.refine(options => (!options.appId && !options.appObjectId && !options.appName) || options.appId || options.appName ||
|
|
42
|
+
(options.appObjectId && validation.isValidGuid(options.appObjectId)), options => ({
|
|
43
|
+
message: `The '${options.appObjectId}' must be a valid GUID`,
|
|
44
|
+
path: ['appObjectId']
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
async commandAction(logger, args) {
|
|
48
|
+
try {
|
|
49
|
+
const appObjectId = await this.getAppObjectId(args.options);
|
|
50
|
+
if (args.options.verbose) {
|
|
51
|
+
await logger.logToStderr(`Adding directory extension to the app with id '${appObjectId}'...`);
|
|
52
|
+
}
|
|
53
|
+
const requestOptions = {
|
|
54
|
+
url: `${this.resource}/v1.0/applications/${appObjectId}/extensionProperties`,
|
|
55
|
+
headers: {
|
|
56
|
+
accept: 'application/json;odata.metadata=none',
|
|
57
|
+
'content-type': 'application/json'
|
|
58
|
+
},
|
|
59
|
+
data: {
|
|
60
|
+
name: args.options.name,
|
|
61
|
+
dataType: args.options.dataType,
|
|
62
|
+
targetObjects: args.options.targetObjects,
|
|
63
|
+
isMultiValued: args.options.isMultiValued
|
|
64
|
+
},
|
|
65
|
+
responseType: 'json'
|
|
66
|
+
};
|
|
67
|
+
const res = await request.post(requestOptions);
|
|
68
|
+
await logger.log(res);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
this.handleRejectedODataJsonPromise(err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async getAppObjectId(options) {
|
|
75
|
+
if (options.appObjectId) {
|
|
76
|
+
return options.appObjectId;
|
|
77
|
+
}
|
|
78
|
+
if (options.appId) {
|
|
79
|
+
return (await entraApp.getAppRegistrationByAppId(options.appId, ["id"])).id;
|
|
80
|
+
}
|
|
81
|
+
return (await entraApp.getAppRegistrationByAppName(options.appName, ["id"])).id;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export default new GraphDirectoryExtensionAddCommand();
|
|
85
|
+
//# sourceMappingURL=directoryextension-add.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import request from '../../../../request.js';
|
|
4
|
+
import { zod } from '../../../../utils/zod.js';
|
|
5
|
+
import commands from '../../commands.js';
|
|
6
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
7
|
+
import { validation } from '../../../../utils/validation.js';
|
|
8
|
+
import { entraApp } from '../../../../utils/entraApp.js';
|
|
9
|
+
import { directoryExtension } from '../../../../utils/directoryExtension.js';
|
|
10
|
+
const options = globalOptionsZod
|
|
11
|
+
.extend({
|
|
12
|
+
id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
13
|
+
message: `'${id}' is not a valid GUID.`
|
|
14
|
+
})).optional()),
|
|
15
|
+
name: zod.alias('n', z.string().optional()),
|
|
16
|
+
appId: z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
17
|
+
message: `'${id}' is not a valid GUID.`
|
|
18
|
+
})).optional(),
|
|
19
|
+
appObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
20
|
+
message: `'${id}' is not a valid GUID.`
|
|
21
|
+
})).optional(),
|
|
22
|
+
appName: z.string().optional()
|
|
23
|
+
})
|
|
24
|
+
.strict();
|
|
25
|
+
class GraphDirectoryExtensionGetCommand extends GraphCommand {
|
|
26
|
+
get name() {
|
|
27
|
+
return commands.DIRECTORYEXTENSION_GET;
|
|
28
|
+
}
|
|
29
|
+
get description() {
|
|
30
|
+
return 'Retrieves the definition of a directory extension';
|
|
31
|
+
}
|
|
32
|
+
get schema() {
|
|
33
|
+
return options;
|
|
34
|
+
}
|
|
35
|
+
getRefinedSchema(schema) {
|
|
36
|
+
return schema
|
|
37
|
+
.refine(options => !options.id !== !options.name, {
|
|
38
|
+
message: 'Specify either id or name, but not both'
|
|
39
|
+
})
|
|
40
|
+
.refine(options => options.id || options.name, {
|
|
41
|
+
message: 'Specify either id or name'
|
|
42
|
+
})
|
|
43
|
+
.refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, {
|
|
44
|
+
message: 'Specify either appId, appObjectId or appName, but not multiple'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async commandAction(logger, args) {
|
|
48
|
+
try {
|
|
49
|
+
const appObjectId = await this.getAppObjectId(args.options);
|
|
50
|
+
let schemeExtensionId = args.options.id;
|
|
51
|
+
if (args.options.name) {
|
|
52
|
+
schemeExtensionId = (await directoryExtension.getDirectoryExtensionByName(args.options.name, appObjectId, ['id'])).id;
|
|
53
|
+
}
|
|
54
|
+
if (args.options.verbose) {
|
|
55
|
+
await logger.logToStderr(`Retrieving schema extension with ID ${schemeExtensionId} from application with ID ${appObjectId}...`);
|
|
56
|
+
}
|
|
57
|
+
const requestOptions = {
|
|
58
|
+
url: `${this.resource}/v1.0/applications/${appObjectId}/extensionProperties/${schemeExtensionId}`,
|
|
59
|
+
headers: {
|
|
60
|
+
accept: 'application/json;odata.metadata=none'
|
|
61
|
+
},
|
|
62
|
+
responseType: 'json'
|
|
63
|
+
};
|
|
64
|
+
const res = await request.get(requestOptions);
|
|
65
|
+
await logger.log(res);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
this.handleRejectedODataJsonPromise(err);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async getAppObjectId(options) {
|
|
72
|
+
if (options.appObjectId) {
|
|
73
|
+
return options.appObjectId;
|
|
74
|
+
}
|
|
75
|
+
if (options.appId) {
|
|
76
|
+
return (await entraApp.getAppRegistrationByAppId(options.appId, ["id"])).id;
|
|
77
|
+
}
|
|
78
|
+
return (await entraApp.getAppRegistrationByAppName(options.appName, ["id"])).id;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export default new GraphDirectoryExtensionGetCommand();
|
|
82
|
+
//# sourceMappingURL=directoryextension-get.js.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
3
|
+
import request from '../../../../request.js';
|
|
4
|
+
import { zod } from '../../../../utils/zod.js';
|
|
5
|
+
import commands from '../../commands.js';
|
|
6
|
+
import GraphCommand from '../../../base/GraphCommand.js';
|
|
7
|
+
import { validation } from '../../../../utils/validation.js';
|
|
8
|
+
import { entraApp } from '../../../../utils/entraApp.js';
|
|
9
|
+
import { cli } from '../../../../cli/cli.js';
|
|
10
|
+
import { directoryExtension } from '../../../../utils/directoryExtension.js';
|
|
11
|
+
const options = globalOptionsZod
|
|
12
|
+
.extend({
|
|
13
|
+
id: zod.alias('i', z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
14
|
+
message: `'${id}' is not a valid GUID.`
|
|
15
|
+
})).optional()),
|
|
16
|
+
name: zod.alias('n', z.string().optional()),
|
|
17
|
+
appId: z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
18
|
+
message: `'${id}' is not a valid GUID.`
|
|
19
|
+
})).optional(),
|
|
20
|
+
appObjectId: z.string().refine(id => validation.isValidGuid(id), id => ({
|
|
21
|
+
message: `'${id}' is not a valid GUID.`
|
|
22
|
+
})).optional(),
|
|
23
|
+
appName: z.string().optional(),
|
|
24
|
+
force: zod.alias('f', z.boolean().optional())
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
class GraphDirectoryExtensionRemoveCommand extends GraphCommand {
|
|
28
|
+
get name() {
|
|
29
|
+
return commands.DIRECTORYEXTENSION_REMOVE;
|
|
30
|
+
}
|
|
31
|
+
get description() {
|
|
32
|
+
return 'Removes a directory extension';
|
|
33
|
+
}
|
|
34
|
+
get schema() {
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
37
|
+
getRefinedSchema(schema) {
|
|
38
|
+
return schema
|
|
39
|
+
.refine(options => !options.id !== !options.name, {
|
|
40
|
+
message: 'Specify either id or name, but not both'
|
|
41
|
+
})
|
|
42
|
+
.refine(options => options.id || options.name, {
|
|
43
|
+
message: 'Specify either id or name'
|
|
44
|
+
})
|
|
45
|
+
.refine(options => Object.values([options.appId, options.appObjectId, options.appName]).filter(v => typeof v !== 'undefined').length === 1, {
|
|
46
|
+
message: 'Specify either appId, appObjectId or appName, but not multiple'
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async commandAction(logger, args) {
|
|
50
|
+
const removeDirectoryExtension = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const appObjectId = await this.getAppObjectId(args.options);
|
|
53
|
+
let schemeExtensionId = args.options.id;
|
|
54
|
+
if (args.options.name) {
|
|
55
|
+
schemeExtensionId = (await directoryExtension.getDirectoryExtensionByName(args.options.name, appObjectId, ['id'])).id;
|
|
56
|
+
}
|
|
57
|
+
if (args.options.verbose) {
|
|
58
|
+
await logger.logToStderr(`Removing schema extension with ID ${schemeExtensionId} from application with ID ${appObjectId}...`);
|
|
59
|
+
}
|
|
60
|
+
const requestOptions = {
|
|
61
|
+
url: `${this.resource}/v1.0/applications/${appObjectId}/extensionProperties/${schemeExtensionId}`,
|
|
62
|
+
headers: {
|
|
63
|
+
accept: 'application/json;odata.metadata=none'
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
await request.delete(requestOptions);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
this.handleRejectedODataJsonPromise(err);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (args.options.force) {
|
|
73
|
+
await removeDirectoryExtension();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove directory extension '${args.options.id || args.options.name}' from application '${args.options.appObjectId || args.options.appId || args.options.appName}'?` });
|
|
77
|
+
if (result) {
|
|
78
|
+
await removeDirectoryExtension();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async getAppObjectId(options) {
|
|
83
|
+
if (options.appObjectId) {
|
|
84
|
+
return options.appObjectId;
|
|
85
|
+
}
|
|
86
|
+
if (options.appId) {
|
|
87
|
+
return (await entraApp.getAppRegistrationByAppId(options.appId, ["id"])).id;
|
|
88
|
+
}
|
|
89
|
+
return (await entraApp.getAppRegistrationByAppName(options.appName, ["id"])).id;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export default new GraphDirectoryExtensionRemoveCommand();
|
|
93
|
+
//# sourceMappingURL=directoryextension-remove.js.map
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const prefix = 'graph';
|
|
2
2
|
export default {
|
|
3
3
|
CHANGELOG_LIST: `${prefix} changelog list`,
|
|
4
|
+
DIRECTORYEXTENSION_ADD: `${prefix} directoryextension add`,
|
|
5
|
+
DIRECTORYEXTENSION_GET: `${prefix} directoryextension get`,
|
|
6
|
+
DIRECTORYEXTENSION_REMOVE: `${prefix} directoryextension remove`,
|
|
4
7
|
SCHEMAEXTENSION_ADD: `${prefix} schemaextension add`,
|
|
5
8
|
SCHEMAEXTENSION_GET: `${prefix} schemaextension get`,
|
|
6
9
|
SCHEMAEXTENSION_LIST: `${prefix} schemaextension list`,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { zod } from '../../../../utils/zod.js';
|
|
3
|
+
import { globalOptionsZod } from '../../../../Command.js';
|
|
4
|
+
import { spo } from '../../../../utils/spo.js';
|
|
5
|
+
import { validation } from '../../../../utils/validation.js';
|
|
6
|
+
import SpoCommand from '../../../base/SpoCommand.js';
|
|
7
|
+
import commands from '../../commands.js';
|
|
8
|
+
import request from '../../../../request.js';
|
|
9
|
+
const options = globalOptionsZod
|
|
10
|
+
.extend({
|
|
11
|
+
url: zod.alias('u', z.string()
|
|
12
|
+
.refine((url) => validation.isValidSharePointUrl(url) === true, url => ({
|
|
13
|
+
message: `'${url}' is not a valid SharePoint Online site URL.`
|
|
14
|
+
}))),
|
|
15
|
+
audiences: z.string()
|
|
16
|
+
.refine(audiences => validation.isValidGuidArray(audiences) === true, audiences => ({
|
|
17
|
+
message: `The following GUIDs are invalid: ${validation.isValidGuidArray(audiences)}.`
|
|
18
|
+
})).optional(),
|
|
19
|
+
vivaConnectionsDefaultStart: z.boolean().optional(),
|
|
20
|
+
isInDraftMode: z.boolean().optional(),
|
|
21
|
+
order: z.number()
|
|
22
|
+
.refine(order => validation.isValidPositiveInteger(order) === true, order => ({
|
|
23
|
+
message: `'${order}' is not a positive integer.`
|
|
24
|
+
})).optional()
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
class SpoHomeSiteAddCommand extends SpoCommand {
|
|
28
|
+
get name() {
|
|
29
|
+
return commands.HOMESITE_ADD;
|
|
30
|
+
}
|
|
31
|
+
get description() {
|
|
32
|
+
return 'Adds a home site';
|
|
33
|
+
}
|
|
34
|
+
get schema() {
|
|
35
|
+
return options;
|
|
36
|
+
}
|
|
37
|
+
async commandAction(logger, args) {
|
|
38
|
+
try {
|
|
39
|
+
const spoAdminUrl = await spo.getSpoAdminUrl(logger, this.verbose);
|
|
40
|
+
const requestOptions = {
|
|
41
|
+
url: `${spoAdminUrl}/_api/SPHSite/AddHomeSite`,
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json;odata=nometadata'
|
|
44
|
+
},
|
|
45
|
+
responseType: 'json',
|
|
46
|
+
data: {
|
|
47
|
+
siteUrl: args.options.url,
|
|
48
|
+
audiences: args.options.audiences?.split(','),
|
|
49
|
+
vivaConnectionsDefaultStart: args.options.vivaConnectionsDefaultStart ?? true,
|
|
50
|
+
isInDraftMode: args.options.isInDraftMode ?? false,
|
|
51
|
+
order: args.options.order
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
if (this.verbose) {
|
|
55
|
+
await logger.logToStderr(`Adding home site with URL: ${args.options.url}...`);
|
|
56
|
+
}
|
|
57
|
+
const res = await request.post(requestOptions);
|
|
58
|
+
await logger.log(res);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
this.handleRejectedODataJsonPromise(err);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export default new SpoHomeSiteAddCommand();
|
|
66
|
+
//# sourceMappingURL=homesite-add.js.map
|
|
@@ -9,6 +9,7 @@ import { formatting } from '../../../../utils/formatting.js';
|
|
|
9
9
|
import { validation } from '../../../../utils/validation.js';
|
|
10
10
|
import SpoCommand from '../../../base/SpoCommand.js';
|
|
11
11
|
import commands from '../../commands.js';
|
|
12
|
+
const BannerWebPartId = 'cbe7b0a9-3504-44dd-a3a3-0e5cacd07788';
|
|
12
13
|
class SpoPageHeaderSetCommand extends SpoCommand {
|
|
13
14
|
get name() {
|
|
14
15
|
return commands.PAGE_HEADER_SET;
|
|
@@ -28,8 +29,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
28
29
|
}
|
|
29
30
|
async commandAction(logger, args) {
|
|
30
31
|
const noPageHeader = {
|
|
31
|
-
"id":
|
|
32
|
-
"instanceId":
|
|
32
|
+
"id": BannerWebPartId,
|
|
33
|
+
"instanceId": BannerWebPartId,
|
|
33
34
|
"title": "Title Region",
|
|
34
35
|
"description": "Title Region Description",
|
|
35
36
|
"serverProcessedContent": {
|
|
@@ -50,8 +51,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
50
51
|
}
|
|
51
52
|
};
|
|
52
53
|
const defaultPageHeader = {
|
|
53
|
-
"id":
|
|
54
|
-
"instanceId":
|
|
54
|
+
"id": BannerWebPartId,
|
|
55
|
+
"instanceId": BannerWebPartId,
|
|
55
56
|
"title": "Title Region",
|
|
56
57
|
"description": "Title Region Description",
|
|
57
58
|
"serverProcessedContent": {
|
|
@@ -72,8 +73,8 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
72
73
|
}
|
|
73
74
|
};
|
|
74
75
|
const customPageHeader = {
|
|
75
|
-
"id":
|
|
76
|
-
"instanceId":
|
|
76
|
+
"id": BannerWebPartId,
|
|
77
|
+
"instanceId": BannerWebPartId,
|
|
77
78
|
"title": "Title Region",
|
|
78
79
|
"description": "Title Region Description",
|
|
79
80
|
"serverProcessedContent": {
|
|
@@ -177,6 +178,14 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
177
178
|
title = pageData.Title;
|
|
178
179
|
topicHeader = topicHeader || pageData.TopicHeader || "";
|
|
179
180
|
}
|
|
181
|
+
const pageControls = JSON.parse(pageData.CanvasContent1);
|
|
182
|
+
//In the new design page header is is a configurable Banner webpart in the first full-width section
|
|
183
|
+
const headerControl = pageControls.find(control => control?.position?.zoneIndex === 1 && control?.position?.sectionFactor === 0 && control?.webPartId === BannerWebPartId);
|
|
184
|
+
const isStandardPageHeader = pageData.LayoutWebpartsContent !== '[]';
|
|
185
|
+
//LayoutWebpartsContent represents standard page header
|
|
186
|
+
if (!isStandardPageHeader) {
|
|
187
|
+
header = headerControl?.webPartData || header;
|
|
188
|
+
}
|
|
180
189
|
header.properties.title = title;
|
|
181
190
|
header.properties.textAlignment = args.options.textAlignment || 'Left';
|
|
182
191
|
header.properties.showTopicHeader = args.options.showTopicHeader || false;
|
|
@@ -232,8 +241,39 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
232
241
|
}
|
|
233
242
|
}
|
|
234
243
|
const requestBody = {
|
|
235
|
-
LayoutWebpartsContent: JSON.stringify([header])
|
|
244
|
+
LayoutWebpartsContent: JSON.stringify([header]),
|
|
245
|
+
CanvasContent1: canvasContent
|
|
236
246
|
};
|
|
247
|
+
if (!isStandardPageHeader) {
|
|
248
|
+
requestBody.LayoutWebpartsContent = '[]';
|
|
249
|
+
header.properties.title = topicHeader;
|
|
250
|
+
if (headerControl) {
|
|
251
|
+
headerControl.webPartData = header;
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
for (const pageControl of pageControls) {
|
|
255
|
+
if (pageControl?.position?.sectionIndex) {
|
|
256
|
+
pageControl.position.sectionIndex += pageControl.position.sectionIndex;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
pageControls.push({
|
|
260
|
+
id: BannerWebPartId,
|
|
261
|
+
controlType: 3,
|
|
262
|
+
displayMode: 2,
|
|
263
|
+
emphasis: {},
|
|
264
|
+
position: {
|
|
265
|
+
zoneIndex: 1,
|
|
266
|
+
sectionFactor: 0,
|
|
267
|
+
layoutIndex: 1,
|
|
268
|
+
controlIndex: 1,
|
|
269
|
+
sectionIndex: 1
|
|
270
|
+
},
|
|
271
|
+
webPartId: BannerWebPartId,
|
|
272
|
+
webPartData: header
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
requestBody.CanvasContent1 = JSON.stringify(pageControls);
|
|
276
|
+
}
|
|
237
277
|
if (title) {
|
|
238
278
|
requestBody.Title = title;
|
|
239
279
|
}
|
|
@@ -249,9 +289,6 @@ class SpoPageHeaderSetCommand extends SpoCommand {
|
|
|
249
289
|
if (bannerImageUrl) {
|
|
250
290
|
requestBody.BannerImageUrl = bannerImageUrl;
|
|
251
291
|
}
|
|
252
|
-
if (canvasContent) {
|
|
253
|
-
requestBody.CanvasContent1 = canvasContent;
|
|
254
|
-
}
|
|
255
292
|
requestOptions = {
|
|
256
293
|
url: `${args.options.webUrl}/_api/sitepages/pages/GetByUrl('sitepages/${formatting.encodeQueryParameter(pageFullName)}')/SavePageAsDraft`,
|
|
257
294
|
headers: {
|