@pnp/cli-microsoft365 10.4.0 → 10.5.0-beta.31351d1

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.
Files changed (89) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/README.md +1 -1
  3. package/allCommands.json +1 -1
  4. package/allCommandsFull.json +1 -1
  5. package/dist/Auth.js +61 -3
  6. package/dist/appInsights.js +10 -4
  7. package/dist/m365/booking/commands/business/business-get.js +0 -3
  8. package/dist/m365/commands/login.js +3 -0
  9. package/dist/m365/entra/commands/user/user-guest-add.js +0 -3
  10. package/dist/m365/flow/commands/environment/environment-get.js +0 -3
  11. package/dist/m365/flow/commands/flow-get.js +0 -3
  12. package/dist/m365/flow/commands/run/run-get.js +0 -3
  13. package/dist/m365/graph/commands/directoryextension/directoryextension-add.js +85 -0
  14. package/dist/m365/graph/commands/directoryextension/directoryextension-get.js +82 -0
  15. package/dist/m365/graph/commands/directoryextension/directoryextension-remove.js +93 -0
  16. package/dist/m365/graph/commands.js +3 -0
  17. package/dist/m365/pa/commands/app/app-get.js +0 -3
  18. package/dist/m365/pa/commands/environment/environment-get.js +0 -3
  19. package/dist/m365/planner/commands/bucket/bucket-add.js +0 -3
  20. package/dist/m365/planner/commands/plan/plan-add.js +0 -3
  21. package/dist/m365/planner/commands/plan/plan-get.js +0 -3
  22. package/dist/m365/planner/commands/plan/plan-set.js +0 -3
  23. package/dist/m365/planner/commands/task/task-checklistitem-add.js +0 -3
  24. package/dist/m365/pp/commands/aibuildermodel/aibuildermodel-get.js +0 -3
  25. package/dist/m365/pp/commands/card/card-get.js +0 -3
  26. package/dist/m365/pp/commands/copilot/copilot-get.js +0 -3
  27. package/dist/m365/pp/commands/dataverse/dataverse-table-get.js +0 -3
  28. package/dist/m365/pp/commands/environment/environment-get.js +0 -3
  29. package/dist/m365/pp/commands/solution/solution-get.js +0 -3
  30. package/dist/m365/pp/commands/solution/solution-publisher-get.js +0 -3
  31. package/dist/m365/pp/commands/website/website-get.js +0 -3
  32. package/dist/m365/spo/commands/group/group-member-add.js +0 -3
  33. package/dist/m365/spo/commands/homesite/homesite-add.js +66 -0
  34. package/dist/m365/spo/commands/page/page-copy.js +0 -3
  35. package/dist/m365/spo/commands/page/page-get.js +0 -3
  36. package/dist/m365/spo/commands/page/page-header-set.js +47 -10
  37. package/dist/m365/spo/commands/page/page-section-remove.js +81 -0
  38. package/dist/m365/spo/commands/sitedesign/sitedesign-run-status-get.js +0 -3
  39. package/dist/m365/spo/commands.js +2 -0
  40. package/dist/m365/tenant/commands/serviceannouncement/serviceannouncement-health-get.js +0 -3
  41. package/dist/m365/todo/commands/list/list-get.js +0 -3
  42. package/dist/m365/todo/commands/task/task-get.js +0 -3
  43. package/dist/m365/viva/commands/engage/engage-community-user-add.js +113 -0
  44. package/dist/m365/viva/commands/engage/engage-community-user-remove.js +107 -0
  45. package/dist/m365/viva/commands/engage/engage-message-add.js +0 -3
  46. package/dist/m365/viva/commands/engage/engage-message-get.js +0 -3
  47. package/dist/m365/viva/commands/engage/engage-user-get.js +0 -3
  48. package/dist/m365/viva/commands.js +2 -0
  49. package/dist/request.js +5 -0
  50. package/dist/utils/directoryExtension.js +25 -0
  51. package/dist/utils/entraApp.js +28 -0
  52. package/docs/docs/cmd/booking/business/business-get.mdx +13 -0
  53. package/docs/docs/cmd/entra/user/user-guest-add.mdx +11 -0
  54. package/docs/docs/cmd/flow/environment/environment-get.mdx +2 -0
  55. package/docs/docs/cmd/flow/flow-get.mdx +2 -0
  56. package/docs/docs/cmd/flow/run/run-get.mdx +17 -13
  57. package/docs/docs/cmd/graph/directoryextension/directoryextension-add.mdx +123 -0
  58. package/docs/docs/cmd/graph/directoryextension/directoryextension-get.mdx +118 -0
  59. package/docs/docs/cmd/graph/directoryextension/directoryextension-remove.mdx +57 -0
  60. package/docs/docs/cmd/login.mdx +9 -1
  61. package/docs/docs/cmd/pa/app/app-get.mdx +8 -2
  62. package/docs/docs/cmd/pa/environment/environment-get.mdx +5 -3
  63. package/docs/docs/cmd/planner/plan/plan-add.mdx +2 -0
  64. package/docs/docs/cmd/planner/plan/plan-get.mdx +8 -4
  65. package/docs/docs/cmd/planner/plan/plan-set.mdx +2 -0
  66. package/docs/docs/cmd/planner/task/task-checklistitem-add.mdx +6 -3
  67. package/docs/docs/cmd/pp/aibuildermodel/aibuildermodel-get.mdx +12 -3
  68. package/docs/docs/cmd/pp/card/card-get.mdx +36 -5
  69. package/docs/docs/cmd/pp/copilot/copilot-get.mdx +41 -5
  70. package/docs/docs/cmd/pp/dataverse/dataverse-table-get.mdx +19 -4
  71. package/docs/docs/cmd/pp/environment/environment-get.mdx +5 -2
  72. package/docs/docs/cmd/pp/solution/solution-get.mdx +12 -7
  73. package/docs/docs/cmd/pp/solution/solution-publisher-get.mdx +12 -7
  74. package/docs/docs/cmd/pp/website/website-get.mdx +22 -4
  75. package/docs/docs/cmd/spo/group/group-member-add.mdx +12 -3
  76. package/docs/docs/cmd/spo/homesite/homesite-add.mdx +124 -0
  77. package/docs/docs/cmd/spo/page/page-copy.mdx +30 -4
  78. package/docs/docs/cmd/spo/page/page-get.mdx +44 -5
  79. package/docs/docs/cmd/spo/page/page-section-remove.mdx +47 -0
  80. package/docs/docs/cmd/spo/sitedesign/sitedesign-run-status-get.mdx +7 -0
  81. package/docs/docs/cmd/todo/list/list-get.mdx +7 -4
  82. package/docs/docs/cmd/todo/task/task-get.mdx +7 -2
  83. package/docs/docs/cmd/viva/engage/engage-community-user-add.mdx +67 -0
  84. package/docs/docs/cmd/viva/engage/engage-community-user-remove.mdx +55 -0
  85. package/docs/docs/cmd/viva/engage/engage-message-add.mdx +37 -12
  86. package/docs/docs/cmd/viva/engage/engage-message-get.mdx +27 -13
  87. package/docs/docs/cmd/viva/engage/engage-user-get.mdx +50 -8
  88. package/npm-shrinkwrap.json +99 -86
  89. package/package.json +15 -15
@@ -17,9 +17,6 @@ class SpoGroupMemberAddCommand extends SpoCommand {
17
17
  get description() {
18
18
  return 'Add members to a SharePoint Group';
19
19
  }
20
- defaultProperties() {
21
- return ['Title', 'UserPrincipalName'];
22
- }
23
20
  constructor() {
24
21
  super();
25
22
  _SpoGroupMemberAddCommand_instances.add(this);
@@ -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
@@ -16,9 +16,6 @@ class SpoPageCopyCommand extends SpoCommand {
16
16
  get description() {
17
17
  return 'Creates a copy of a modern page or template';
18
18
  }
19
- defaultProperties() {
20
- return ['Id', 'PageLayoutType', 'Title', 'Url'];
21
- }
22
19
  constructor() {
23
20
  super();
24
21
  _SpoPageCopyCommand_instances.add(this);
@@ -25,9 +25,6 @@ class SpoPageGetCommand extends SpoCommand {
25
25
  get description() {
26
26
  return 'Gets information about the specific modern page';
27
27
  }
28
- defaultProperties() {
29
- return ['commentsDisabled', 'numSections', 'numControls', 'title', 'layoutType'];
30
- }
31
28
  get schema() {
32
29
  return options;
33
30
  }
@@ -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": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
32
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
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": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
54
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
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": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
76
- "instanceId": "cbe7b0a9-3504-44dd-a3a3-0e5cacd07788",
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: {
@@ -0,0 +1,81 @@
1
+ import { cli } from '../../../../cli/cli.js';
2
+ import request from '../../../../request.js';
3
+ import { spo } from '../../../../utils/spo.js';
4
+ import { urlUtil } from '../../../../utils/urlUtil.js';
5
+ import { validation } from '../../../../utils/validation.js';
6
+ import SpoCommand from '../../../base/SpoCommand.js';
7
+ import commands from '../../commands.js';
8
+ import { Page } from './Page.js';
9
+ import { z } from 'zod';
10
+ import { globalOptionsZod } from '../../../../Command.js';
11
+ import { zod } from '../../../../utils/zod.js';
12
+ const options = globalOptionsZod
13
+ .extend({
14
+ webUrl: zod.alias('u', z.string()
15
+ .refine(url => validation.isValidSharePointUrl(url) === true, url => ({
16
+ message: `'${url}' is not a valid SharePoint Online site URL.`
17
+ }))),
18
+ pageName: zod.alias('n', z.string()),
19
+ section: zod.alias('s', z.number()),
20
+ force: zod.alias('f', z.boolean().optional())
21
+ })
22
+ .strict();
23
+ class SpoPageSectionRemoveCommand extends SpoCommand {
24
+ get name() {
25
+ return commands.PAGE_SECTION_REMOVE;
26
+ }
27
+ get description() {
28
+ return 'Removes the specified section from the modern page';
29
+ }
30
+ get schema() {
31
+ return options;
32
+ }
33
+ async commandAction(logger, args) {
34
+ if (args.options.force) {
35
+ await this.removeSection(logger, args);
36
+ }
37
+ else {
38
+ const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove section ${args.options.section} from '${args.options.pageName}'?` });
39
+ if (result) {
40
+ await this.removeSection(logger, args);
41
+ }
42
+ }
43
+ }
44
+ async removeSection(logger, args) {
45
+ try {
46
+ if (this.verbose) {
47
+ await logger.logToStderr(`Removing modern page section ${args.options.pageName} - ${args.options.section}...`);
48
+ }
49
+ const reqDigest = await spo.getRequestDigest(args.options.webUrl);
50
+ const clientSidePage = await Page.getPage(args.options.pageName, args.options.webUrl, logger, this.debug, this.verbose);
51
+ const sectionToDelete = clientSidePage.sections
52
+ .findIndex(section => section.order === args.options.section);
53
+ if (sectionToDelete === -1) {
54
+ throw new Error(`Section ${args.options.section} not found`);
55
+ }
56
+ clientSidePage.sections.splice(sectionToDelete, 1);
57
+ const updatedContent = clientSidePage.toHtml();
58
+ const requestOptions = {
59
+ url: `${args.options
60
+ .webUrl}/_api/web/GetFileByServerRelativePath(DecodedUrl='${urlUtil.getServerRelativeSiteUrl(args.options.webUrl)}/sitepages/${args.options.pageName}')/ListItemAllFields`,
61
+ headers: {
62
+ 'X-RequestDigest': reqDigest.FormDigestValue,
63
+ 'content-type': 'application/json;odata=nometadata',
64
+ 'X-HTTP-Method': 'MERGE',
65
+ 'IF-MATCH': '*',
66
+ accept: 'application/json;odata=nometadata'
67
+ },
68
+ data: {
69
+ CanvasContent1: updatedContent
70
+ },
71
+ responseType: 'json'
72
+ };
73
+ return request.post(requestOptions);
74
+ }
75
+ catch (err) {
76
+ this.handleRejectedODataJsonPromise(err);
77
+ }
78
+ }
79
+ }
80
+ export default new SpoPageSectionRemoveCommand();
81
+ //# sourceMappingURL=page-section-remove.js.map
@@ -15,9 +15,6 @@ class SpoSiteDesignRunStatusGetCommand extends SpoCommand {
15
15
  get description() {
16
16
  return 'Gets information about the site scripts executed for the specified site design';
17
17
  }
18
- defaultProperties() {
19
- return ['ActionTitle', 'SiteScriptTitle', 'OutcomeText'];
20
- }
21
18
  constructor() {
22
19
  super();
23
20
  _SpoSiteDesignRunStatusGetCommand_instances.add(this);
@@ -116,6 +116,7 @@ export default {
116
116
  GROUP_MEMBER_REMOVE: `${prefix} group member remove`,
117
117
  HIDEDEFAULTTHEMES_GET: `${prefix} hidedefaultthemes get`,
118
118
  HIDEDEFAULTTHEMES_SET: `${prefix} hidedefaultthemes set`,
119
+ HOMESITE_ADD: `${prefix} homesite add`,
119
120
  HOMESITE_GET: `${prefix} homesite get`,
120
121
  HOMESITE_LIST: `${prefix} homesite list`,
121
122
  HOMESITE_REMOVE: `${prefix} homesite remove`,
@@ -221,6 +222,7 @@ export default {
221
222
  PAGE_SECTION_ADD: `${prefix} page section add`,
222
223
  PAGE_SECTION_GET: `${prefix} page section get`,
223
224
  PAGE_SECTION_LIST: `${prefix} page section list`,
225
+ PAGE_SECTION_REMOVE: `${prefix} page section remove`,
224
226
  PAGE_TEMPLATE_LIST: `${prefix} page template list`,
225
227
  PAGE_TEMPLATE_REMOVE: `${prefix} page template remove`,
226
228
  PAGE_TEXT_ADD: `${prefix} page text add`,
@@ -14,9 +14,6 @@ class TenantServiceAnnouncementHealthGetCommand extends GraphCommand {
14
14
  get description() {
15
15
  return 'This operation provides the health information of a specified service for a tenant';
16
16
  }
17
- defaultProperties() {
18
- return ['id', 'status', 'service'];
19
- }
20
17
  constructor() {
21
18
  super();
22
19
  _TenantServiceAnnouncementHealthGetCommand_instances.add(this);
@@ -15,9 +15,6 @@ class TodoListGetCommand extends DelegatedGraphCommand {
15
15
  get description() {
16
16
  return 'Gets a specific list of Microsoft To Do task lists';
17
17
  }
18
- defaultProperties() {
19
- return ['displayName', 'id'];
20
- }
21
18
  constructor() {
22
19
  super();
23
20
  _TodoListGetCommand_instances.add(this);
@@ -16,9 +16,6 @@ class TodoTaskGetCommand extends DelegatedGraphCommand {
16
16
  get description() {
17
17
  return 'Get a specific task from a Microsoft To Do task list';
18
18
  }
19
- defaultProperties() {
20
- return ['id', 'title', 'status', 'createdDateTime', 'lastModifiedDateTime'];
21
- }
22
19
  constructor() {
23
20
  super();
24
21
  _TodoTaskGetCommand_instances.add(this);
@@ -0,0 +1,113 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import { zod } from '../../../../utils/zod.js';
4
+ import GraphCommand from '../../../base/GraphCommand.js';
5
+ import commands from '../../commands.js';
6
+ import { validation } from '../../../../utils/validation.js';
7
+ import { vivaEngage } from '../../../../utils/vivaEngage.js';
8
+ import request from '../../../../request.js';
9
+ import { entraUser } from '../../../../utils/entraUser.js';
10
+ import { formatting } from '../../../../utils/formatting.js';
11
+ const options = globalOptionsZod
12
+ .extend({
13
+ communityId: z.string().optional(),
14
+ communityDisplayName: zod.alias('n', z.string().optional()),
15
+ entraGroupId: z.string()
16
+ .refine(id => validation.isValidGuid(id), id => ({
17
+ message: `'${id}' is not a valid GUID.`
18
+ })).optional(),
19
+ ids: z.string()
20
+ .refine(ids => validation.isValidGuidArray(ids) === true, invalidIds => ({
21
+ message: `The following GUIDs are invalid: ${invalidIds}.`
22
+ })).optional(),
23
+ userNames: z.string()
24
+ .refine(userNames => validation.isValidUserPrincipalNameArray(userNames) === true, invalidUserNames => ({
25
+ message: `The following user principal names are invalid: ${invalidUserNames}.`
26
+ })).optional(),
27
+ role: zod.alias('r', z.enum(['Admin', 'Member']))
28
+ })
29
+ .strict();
30
+ class VivaEngageCommunityUserAddCommand extends GraphCommand {
31
+ get name() {
32
+ return commands.ENGAGE_COMMUNITY_USER_ADD;
33
+ }
34
+ get description() {
35
+ return 'Adds a user to a specific Microsoft 365 Viva Engage community';
36
+ }
37
+ get schema() {
38
+ return options;
39
+ }
40
+ getRefinedSchema(schema) {
41
+ return schema
42
+ .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, {
43
+ message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.'
44
+ })
45
+ .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, {
46
+ message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.'
47
+ })
48
+ .refine(options => options.ids || options.userNames, {
49
+ message: 'Specify either of ids or userNames.'
50
+ })
51
+ .refine(options => typeof options.userNames !== undefined && typeof options.ids !== undefined, {
52
+ message: 'Specify either ids or userNames, but not both.'
53
+ });
54
+ }
55
+ async commandAction(logger, args) {
56
+ try {
57
+ if (this.verbose) {
58
+ await logger.logToStderr('Adding users to community...');
59
+ }
60
+ let entraGroupId = args.options.entraGroupId;
61
+ if (args.options.communityDisplayName) {
62
+ const community = await vivaEngage.getCommunityByDisplayName(args.options.communityDisplayName, ['groupId']);
63
+ entraGroupId = community.groupId;
64
+ }
65
+ if (args.options.communityId) {
66
+ const community = await vivaEngage.getCommunityById(args.options.communityId, ['groupId']);
67
+ entraGroupId = community.groupId;
68
+ }
69
+ const userIds = args.options.ids ? formatting.splitAndTrim(args.options.ids) : await entraUser.getUserIdsByUpns(formatting.splitAndTrim(args.options.userNames));
70
+ const role = args.options.role === 'Member' ? 'members' : 'owners';
71
+ for (let i = 0; i < userIds.length; i += 400) {
72
+ const userIdsBatch = userIds.slice(i, i + 400);
73
+ const requestOptions = {
74
+ url: `${this.resource}/v1.0/$batch`,
75
+ headers: {
76
+ 'content-type': 'application/json;odata.metadata=none'
77
+ },
78
+ responseType: 'json',
79
+ data: {
80
+ requests: []
81
+ }
82
+ };
83
+ // only 20 requests per one batch are allowed
84
+ for (let j = 0; j < userIdsBatch.length; j += 20) {
85
+ // only 20 users can be added in one request
86
+ const userIdsChunk = userIdsBatch.slice(j, j + 20);
87
+ requestOptions.data.requests.push({
88
+ id: j + 1,
89
+ method: 'PATCH',
90
+ url: `/groups/${entraGroupId}`,
91
+ headers: {
92
+ 'content-type': 'application/json;odata.metadata=none'
93
+ },
94
+ body: {
95
+ [`${role}@odata.bind`]: userIdsChunk.map((u) => `${this.resource}/v1.0/directoryObjects/${u}`)
96
+ }
97
+ });
98
+ }
99
+ const res = await request.post(requestOptions);
100
+ for (const response of res.responses) {
101
+ if (response.status !== 204) {
102
+ throw response.body;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ catch (err) {
108
+ this.handleRejectedODataJsonPromise(err);
109
+ }
110
+ }
111
+ }
112
+ export default new VivaEngageCommunityUserAddCommand();
113
+ //# sourceMappingURL=engage-community-user-add.js.map
@@ -0,0 +1,107 @@
1
+ import { z } from 'zod';
2
+ import { globalOptionsZod } from '../../../../Command.js';
3
+ import { zod } from '../../../../utils/zod.js';
4
+ import GraphCommand from '../../../base/GraphCommand.js';
5
+ import commands from '../../commands.js';
6
+ import { validation } from '../../../../utils/validation.js';
7
+ import { vivaEngage } from '../../../../utils/vivaEngage.js';
8
+ import request from '../../../../request.js';
9
+ import { entraUser } from '../../../../utils/entraUser.js';
10
+ import { cli } from '../../../../cli/cli.js';
11
+ const options = globalOptionsZod
12
+ .extend({
13
+ communityId: z.string().optional(),
14
+ communityDisplayName: zod.alias('n', z.string().optional()),
15
+ entraGroupId: z.string()
16
+ .refine(id => validation.isValidGuid(id), id => ({
17
+ message: `'${id}' is not a valid GUID.`
18
+ })).optional(),
19
+ id: z.string()
20
+ .refine(id => validation.isValidGuid(id), id => ({
21
+ message: `'${id}' is not a valid GUID.`
22
+ })).optional(),
23
+ userName: z.string()
24
+ .refine(userName => validation.isValidUserPrincipalName(userName), userName => ({
25
+ message: `'${userName}' is not a valid user principal name.`
26
+ })).optional(),
27
+ force: z.boolean().optional()
28
+ })
29
+ .strict();
30
+ class VivaEngageCommunityUserRemoveCommand extends GraphCommand {
31
+ get name() {
32
+ return commands.ENGAGE_COMMUNITY_USER_REMOVE;
33
+ }
34
+ get description() {
35
+ return 'Removes a specified user from a Microsoft 365 Viva Engage community';
36
+ }
37
+ get schema() {
38
+ return options;
39
+ }
40
+ getRefinedSchema(schema) {
41
+ return schema
42
+ .refine(options => [options.communityId, options.communityDisplayName, options.entraGroupId].filter(x => x !== undefined).length === 1, {
43
+ message: 'Specify either communityId, communityDisplayName, or entraGroupId, but not multiple.'
44
+ })
45
+ .refine(options => options.communityId || options.communityDisplayName || options.entraGroupId, {
46
+ message: 'Specify at least one of communityId, communityDisplayName, or entraGroupId.'
47
+ })
48
+ .refine(options => options.id || options.userName, {
49
+ message: 'Specify either of id or userName.'
50
+ })
51
+ .refine(options => typeof options.userName !== undefined && typeof options.id !== undefined, {
52
+ message: 'Specify either id or userName, but not both.'
53
+ });
54
+ }
55
+ async commandAction(logger, args) {
56
+ try {
57
+ if (args.options.force) {
58
+ await this.deleteUserFromCommunity(args.options, logger);
59
+ }
60
+ else {
61
+ const result = await cli.promptForConfirmation({ message: `Are you sure you want to remove the user ${args.options.id || args.options.userName} from the community ${args.options.communityDisplayName || args.options.communityId || args.options.entraGroupId}?` });
62
+ if (result) {
63
+ await this.deleteUserFromCommunity(args.options, logger);
64
+ }
65
+ }
66
+ }
67
+ catch (err) {
68
+ this.handleRejectedODataJsonPromise(err);
69
+ }
70
+ }
71
+ async deleteUserFromCommunity(options, logger) {
72
+ if (this.verbose) {
73
+ await logger.logToStderr('Removing user from community...');
74
+ }
75
+ let entraGroupId = options.entraGroupId;
76
+ if (options.communityDisplayName) {
77
+ const community = await vivaEngage.getCommunityByDisplayName(options.communityDisplayName, ['groupId']);
78
+ entraGroupId = community.groupId;
79
+ }
80
+ else if (options.communityId) {
81
+ const community = await vivaEngage.getCommunityById(options.communityId, ['groupId']);
82
+ entraGroupId = community.groupId;
83
+ }
84
+ const userId = options.id || await entraUser.getUserIdByUpn(options.userName);
85
+ await this.deleteUser(entraGroupId, userId, 'owners');
86
+ await this.deleteUser(entraGroupId, userId, 'members');
87
+ }
88
+ async deleteUser(entraGroupId, userId, role) {
89
+ try {
90
+ const requestOptions = {
91
+ url: `${this.resource}/v1.0/groups/${entraGroupId}/${role}/${userId}/$ref`,
92
+ headers: {
93
+ accept: 'application/json;odata.metadata=none'
94
+ },
95
+ responseType: 'json'
96
+ };
97
+ await request.delete(requestOptions);
98
+ }
99
+ catch (err) {
100
+ if (err.response.status !== 404) {
101
+ throw err.response.data;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ export default new VivaEngageCommunityUserRemoveCommand();
107
+ //# sourceMappingURL=engage-community-user-remove.js.map
@@ -14,9 +14,6 @@ class VivaEngageMessageAddCommand extends VivaEngageCommand {
14
14
  get description() {
15
15
  return 'Posts a Viva Engage network message on behalf of the current user';
16
16
  }
17
- defaultProperties() {
18
- return ['id'];
19
- }
20
17
  constructor() {
21
18
  super();
22
19
  _VivaEngageMessageAddCommand_instances.add(this);
@@ -14,9 +14,6 @@ class VivaEngageMessageGetCommand extends VivaEngageCommand {
14
14
  get description() {
15
15
  return 'Returns a Viva Engage message';
16
16
  }
17
- defaultProperties() {
18
- return ['id', 'sender_id', 'replied_to_id', 'thread_id', 'group_id', 'created_at', 'direct_message', 'system_message', 'privacy', 'message_type', 'content_excerpt'];
19
- }
20
17
  constructor() {
21
18
  super();
22
19
  _VivaEngageMessageGetCommand_instances.add(this);
@@ -15,9 +15,6 @@ class VivaEngageUserGetCommand extends VivaEngageCommand {
15
15
  get description() {
16
16
  return 'Retrieves the current user or searches for a user by ID or e-mail';
17
17
  }
18
- defaultProperties() {
19
- return ['id', 'full_name', 'email', 'job_title', 'state', 'url'];
20
- }
21
18
  constructor() {
22
19
  super();
23
20
  _VivaEngageUserGetCommand_instances.add(this);
@@ -5,7 +5,9 @@ export default {
5
5
  ENGAGE_COMMUNITY_GET: `${prefix} engage community get`,
6
6
  ENGAGE_COMMUNITY_LIST: `${prefix} engage community list`,
7
7
  ENGAGE_COMMUNITY_SET: `${prefix} engage community set`,
8
+ ENGAGE_COMMUNITY_USER_ADD: `${prefix} engage community user add`,
8
9
  ENGAGE_COMMUNITY_USER_LIST: `${prefix} engage community user list`,
10
+ ENGAGE_COMMUNITY_USER_REMOVE: `${prefix} engage community user remove`,
9
11
  ENGAGE_GROUP_LIST: `${prefix} engage group list`,
10
12
  ENGAGE_GROUP_USER_ADD: `${prefix} engage group user add`,
11
13
  ENGAGE_GROUP_USER_REMOVE: `${prefix} engage group user remove`,
package/dist/request.js CHANGED
@@ -132,6 +132,7 @@ class Request {
132
132
  throw 'Logger not set on the request object';
133
133
  }
134
134
  this.updateRequestForCloudType(options, auth.connection.cloudType);
135
+ this.removeDoubleSlashes(options);
135
136
  try {
136
137
  let accessToken = '';
137
138
  if (options.headers && options.headers['x-anonymous']) {
@@ -187,6 +188,10 @@ class Request {
187
188
  const cloudUrl = Auth.getEndpointForResource(hostname, cloudType);
188
189
  options.url = options.url.replace(hostname, cloudUrl);
189
190
  }
191
+ removeDoubleSlashes(options) {
192
+ options.url = options.url.substring(0, 8) +
193
+ options.url.substring(8).replace('//', '/');
194
+ }
190
195
  createProxyConfigFromUrl(url) {
191
196
  const parsedUrl = new URL(url);
192
197
  const port = parsedUrl.port || (url.toLowerCase().startsWith('https') ? 443 : 80);
@@ -0,0 +1,25 @@
1
+ import { formatting } from './formatting.js';
2
+ import { odata } from './odata.js';
3
+ export const directoryExtension = {
4
+ /**
5
+ * Get a directory extension by its name registered for an application.
6
+ * @param name Role definition display name.
7
+ * @param appObjectId Application object id.
8
+ * @param properties List of properties to include in the response.
9
+ * @returns The directory extensions.
10
+ * @throws Error when directory extension was not found.
11
+ */
12
+ async getDirectoryExtensionByName(name, appObjectId, properties) {
13
+ let url = `https://graph.microsoft.com/v1.0/applications/${appObjectId}/extensionProperties?$filter=name eq '${formatting.encodeQueryParameter(name)}'`;
14
+ if (properties) {
15
+ url += `&$select=${properties.join(',')}`;
16
+ }
17
+ const extensionProperties = await odata.getAllItems(url);
18
+ if (extensionProperties.length === 0) {
19
+ throw `The specified directory extension '${name}' does not exist.`;
20
+ }
21
+ // there can be only one directory extension with a given name
22
+ return extensionProperties[0];
23
+ }
24
+ };
25
+ //# sourceMappingURL=directoryExtension.js.map