@pnp/cli-microsoft365 6.7.0-beta.60f2469 → 6.7.0-beta.872b0f5

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.
@@ -26,7 +26,7 @@ RUN apt-get update && apt-get install -y \
26
26
  vim \
27
27
  python3.8 \
28
28
  python3-pip \
29
- && curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash - \
29
+ && curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - \
30
30
  && apt-get install nodejs -y \
31
31
  && rm -rf /var/lib/apt/lists/*
32
32
 
package/README.md CHANGED
@@ -184,7 +184,7 @@ m365 spo site get --url https://contoso.sharepoint.com --output text
184
184
 
185
185
  ## Build
186
186
 
187
- To build and run this CLI locally, you will need [`node`](https://nodejs.org) `>= 16.0.0` installed.
187
+ To build and run this CLI locally, you will need [`node`](https://nodejs.org) `>= 18.0.0` installed.
188
188
 
189
189
  ```sh
190
190
  # Clone this repository
package/dist/Auth.js CHANGED
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.Auth = exports.CertificateType = exports.AuthType = exports.Service = void 0;
12
+ exports.Auth = exports.CertificateType = exports.AuthType = exports.Service = exports.CloudType = void 0;
13
+ const msal_common_1 = require("@azure/msal-common");
13
14
  const FileTokenStorage_1 = require("./auth/FileTokenStorage");
14
15
  const msalCachePlugin_1 = require("./auth/msalCachePlugin");
15
16
  const Cli_1 = require("./cli/Cli");
@@ -17,14 +18,24 @@ const Command_1 = require("./Command");
17
18
  const config_1 = require("./config");
18
19
  const request_1 = require("./request");
19
20
  const settingsNames_1 = require("./settingsNames");
21
+ var CloudType;
22
+ (function (CloudType) {
23
+ CloudType[CloudType["Public"] = 0] = "Public";
24
+ CloudType[CloudType["USGov"] = 1] = "USGov";
25
+ CloudType[CloudType["USGovHigh"] = 2] = "USGovHigh";
26
+ CloudType[CloudType["USGovDoD"] = 3] = "USGovDoD";
27
+ CloudType[CloudType["China"] = 4] = "China";
28
+ })(CloudType = exports.CloudType || (exports.CloudType = {}));
20
29
  class Service {
21
30
  constructor() {
22
31
  this.connected = false;
23
32
  this.authType = AuthType.DeviceCode;
24
33
  this.certificateType = CertificateType.Unknown;
34
+ this.cloudType = CloudType.Public;
25
35
  this.accessTokens = {};
26
36
  this.appId = config_1.default.cliAadAppId;
27
37
  this.tenant = config_1.default.tenant;
38
+ this.cloudType = CloudType.Public;
28
39
  }
29
40
  logout() {
30
41
  this.connected = false;
@@ -62,11 +73,41 @@ class Auth {
62
73
  return this._service;
63
74
  }
64
75
  get defaultResource() {
65
- return 'https://graph.microsoft.com';
76
+ return Auth.getEndpointForResource('https://graph.microsoft.com', this._service.cloudType);
66
77
  }
67
78
  constructor() {
68
79
  this._service = new Service();
69
80
  }
81
+ // we need to init cloud endpoints here, because we're using CloudType enum
82
+ // as indexers, which we can't do in the static initializer
83
+ // it also needs to be a separate method that we call here, because in tests
84
+ // we're mocking auth and calling its constructor
85
+ static initialize() {
86
+ this.cloudEndpoints[CloudType.USGov] = {
87
+ 'https://graph.microsoft.com': 'https://graph.microsoft.com',
88
+ 'https://graph.windows.net': 'https://graph.windows.net',
89
+ 'https://management.azure.com/': 'https://management.usgovcloudapi.net/',
90
+ 'https://login.microsoftonline.com': 'https://login.microsoftonline.com'
91
+ };
92
+ this.cloudEndpoints[CloudType.USGovHigh] = {
93
+ 'https://graph.microsoft.com': 'https://graph.microsoft.us',
94
+ 'https://graph.windows.net': 'https://graph.windows.net',
95
+ 'https://management.azure.com/': 'https://management.usgovcloudapi.net/',
96
+ 'https://login.microsoftonline.com': 'https://login.microsoftonline.us'
97
+ };
98
+ this.cloudEndpoints[CloudType.USGovDoD] = {
99
+ 'https://graph.microsoft.com': 'https://dod-graph.microsoft.us',
100
+ 'https://graph.windows.net': 'https://graph.windows.net',
101
+ 'https://management.azure.com/': 'https://management.usgovcloudapi.net/',
102
+ 'https://login.microsoftonline.com': 'https://login.microsoftonline.us'
103
+ };
104
+ this.cloudEndpoints[CloudType.China] = {
105
+ 'https://graph.microsoft.com': 'https://microsoftgraph.chinacloudapi.cn',
106
+ 'https://graph.windows.net': 'https://graph.chinacloudapi.cn',
107
+ 'https://management.azure.com/': 'https://management.chinacloudapi.cn',
108
+ 'https://login.microsoftonline.com': 'https://login.chinacloudapi.cn'
109
+ };
110
+ }
70
111
  restoreAuth() {
71
112
  return __awaiter(this, void 0, void 0, function* () {
72
113
  // check if auth has been restored previously
@@ -196,9 +237,27 @@ class Auth {
196
237
  thumbprint: certificateThumbprint,
197
238
  privateKey: certificatePrivateKey
198
239
  };
240
+ let azureCloudInstance = 0;
241
+ switch (this.service.cloudType) {
242
+ case CloudType.Public:
243
+ azureCloudInstance = msal_common_1.AzureCloudInstance.AzurePublic;
244
+ break;
245
+ case CloudType.China:
246
+ azureCloudInstance = msal_common_1.AzureCloudInstance.AzureChina;
247
+ break;
248
+ case CloudType.USGov:
249
+ case CloudType.USGovHigh:
250
+ case CloudType.USGovDoD:
251
+ azureCloudInstance = msal_common_1.AzureCloudInstance.AzureUsGovernment;
252
+ break;
253
+ }
199
254
  const config = {
200
255
  clientId: this.service.appId,
201
- authority: `https://login.microsoftonline.com/${this.service.tenant}`
256
+ authority: `${Auth.getEndpointForResource('https://login.microsoftonline.com', this.service.cloudType)}/${this.service.tenant}`,
257
+ azureCloudOptions: {
258
+ azureCloudInstance,
259
+ tenant: this.service.tenant
260
+ }
202
261
  };
203
262
  const authConfig = cert
204
263
  ? Object.assign(Object.assign({}, config), { clientCertificate: cert }) : Object.assign(Object.assign({}, config), { clientSecret });
@@ -534,7 +593,8 @@ class Auth {
534
593
  if (pos > -1) {
535
594
  resource = resource.substr(0, pos);
536
595
  }
537
- if (resource === 'https://api.bap.microsoft.com' || resource === 'https://api.powerapps.com') {
596
+ if (resource === 'https://api.bap.microsoft.com' ||
597
+ resource === 'https://api.powerapps.com') {
538
598
  resource = 'https://service.powerapps.com/';
539
599
  }
540
600
  if (resource === 'https://api.powerbi.com') {
@@ -571,7 +631,18 @@ class Auth {
571
631
  getMsalCacheStorage() {
572
632
  return new FileTokenStorage_1.FileTokenStorage(FileTokenStorage_1.FileTokenStorage.msalCacheFilePath());
573
633
  }
634
+ static getEndpointForResource(resource, cloudType) {
635
+ if (Auth.cloudEndpoints[cloudType] &&
636
+ Auth.cloudEndpoints[cloudType][resource]) {
637
+ return Auth.cloudEndpoints[cloudType][resource];
638
+ }
639
+ else {
640
+ return resource;
641
+ }
642
+ }
574
643
  }
575
644
  exports.Auth = Auth;
645
+ Auth.cloudEndpoints = [];
646
+ Auth.initialize();
576
647
  exports.default = new Auth();
577
648
  //# sourceMappingURL=Auth.js.map
@@ -4,6 +4,7 @@ exports.AuthServer = void 0;
4
4
  const http = require("http");
5
5
  const open = require("open");
6
6
  const url = require("url");
7
+ const Auth_1 = require("./Auth");
7
8
  class AuthServer {
8
9
  constructor() {
9
10
  this.open = open;
@@ -23,7 +24,7 @@ class AuthServer {
23
24
  const requestState = Math.random().toString(16).substr(2, 20);
24
25
  const address = this.httpServer.address();
25
26
  this.generatedServerUrl = `http://localhost:${address.port}`;
26
- const url = `https://login.microsoftonline.com/${this.service.tenant}/oauth2/authorize?response_type=code&client_id=${this.service.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`;
27
+ const url = `${Auth_1.Auth.getEndpointForResource('https://login.microsoftonline.com', this.service.cloudType)}/${this.service.tenant}/oauth2/authorize?response_type=code&client_id=${this.service.appId}&redirect_uri=${this.generatedServerUrl}&state=${requestState}&resource=${this.resource}&prompt=select_account`;
27
28
  if (this.debug) {
28
29
  this.logger.logToStderr('Redirect URL:');
29
30
  this.logger.logToStderr(url);
package/dist/Command.js CHANGED
@@ -127,21 +127,70 @@ class Command {
127
127
  if (!optionsSets || optionsSets.length === 0) {
128
128
  return true;
129
129
  }
130
+ let inquirer;
131
+ const shouldPrompt = Cli_1.Cli.getInstance().getSettingWithDefaultValue(settingsNames_1.settingsNames.prompt, false);
130
132
  const argsOptions = Object.keys(args.options);
131
- for (const optionSet of optionsSets) {
132
- if (optionSet.runsWhen === undefined || optionSet.runsWhen(args)) {
133
- const commonOptions = argsOptions.filter(opt => optionSet.options.includes(opt));
134
- if (commonOptions.length === 0) {
135
- return `Specify one of the following options: ${optionSet.options.map(opt => opt).join(', ')}.`;
133
+ for (const optionSet of optionsSets.sort(opt => opt.runsWhen ? 0 : 1)) {
134
+ if (optionSet.runsWhen && !optionSet.runsWhen(args)) {
135
+ continue;
136
+ }
137
+ const commonOptions = argsOptions.filter(opt => optionSet.options.includes(opt));
138
+ if (commonOptions.length === 0) {
139
+ if (!shouldPrompt) {
140
+ return `Specify one of the following options: ${optionSet.options.join(', ')}.`;
136
141
  }
137
- if (commonOptions.length > 1) {
138
- return `Specify one of the following options: ${optionSet.options.map(opt => opt).join(', ')}, but not multiple.`;
142
+ yield this.promptForOptionSetNameAndValue(args, optionSet, inquirer);
143
+ }
144
+ if (commonOptions.length > 1) {
145
+ if (!shouldPrompt) {
146
+ return `Specify one of the following options: ${optionSet.options.join(', ')}, but not multiple.`;
139
147
  }
148
+ yield this.promptForSpecificOption(args, commonOptions, inquirer);
140
149
  }
141
150
  }
142
151
  return true;
143
152
  });
144
153
  }
154
+ promptForOptionSetNameAndValue(args, optionSet, inquirer) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ if (!inquirer) {
157
+ inquirer = require('inquirer');
158
+ }
159
+ Cli_1.Cli.log(`Please specify one of the following options:`);
160
+ const resultOptionName = yield inquirer
161
+ .prompt({
162
+ type: 'list',
163
+ name: 'missingRequiredOptionName',
164
+ message: `Option to use:`,
165
+ choices: optionSet.options
166
+ });
167
+ const missingRequiredOptionName = resultOptionName.missingRequiredOptionName;
168
+ const resultOptionValue = yield inquirer
169
+ .prompt({
170
+ name: 'missingRequiredOptionValue',
171
+ message: `Value for '${missingRequiredOptionName}':`
172
+ });
173
+ args.options[missingRequiredOptionName] = resultOptionValue.missingRequiredOptionValue;
174
+ Cli_1.Cli.log();
175
+ });
176
+ }
177
+ promptForSpecificOption(args, commonOptions, inquirer) {
178
+ return __awaiter(this, void 0, void 0, function* () {
179
+ if (!inquirer) {
180
+ inquirer = require('inquirer');
181
+ }
182
+ Cli_1.Cli.log(`Multiple options for an option set specified. Please specify the correct option that you wish to use.`);
183
+ const requiredOptionNameResult = yield inquirer
184
+ .prompt({
185
+ type: 'list',
186
+ name: 'missingRequiredOptionName',
187
+ message: `Option to use:`,
188
+ choices: commonOptions
189
+ });
190
+ commonOptions.filter(y => y !== requiredOptionNameResult.missingRequiredOptionName).map(optionName => args.options[optionName] = undefined);
191
+ Cli_1.Cli.log();
192
+ });
193
+ }
145
194
  validateOutput(args) {
146
195
  return __awaiter(this, void 0, void 0, function* () {
147
196
  if (args.options.output &&
@@ -1,10 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Auth_1 = require("../../Auth");
3
4
  const Command_1 = require("../../Command");
4
5
  class AzmgmtCommand extends Command_1.default {
5
6
  get resource() {
6
7
  return 'https://management.azure.com/';
7
8
  }
9
+ initAction(args, logger) {
10
+ super.initAction(args, logger);
11
+ if (!Auth_1.default.service.connected) {
12
+ // we fail no login in the base command command class
13
+ return;
14
+ }
15
+ if (Auth_1.default.service.cloudType !== Auth_1.CloudType.Public) {
16
+ throw new Command_1.CommandError(`Power Automate commands only support the public cloud at the moment. We'll add support for other clouds in the future. Sorry for the inconvenience.`);
17
+ }
18
+ }
8
19
  }
9
20
  exports.default = AzmgmtCommand;
10
21
  //# sourceMappingURL=AzmgmtCommand.js.map
@@ -1,10 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Auth_1 = require("../../Auth");
3
4
  const Command_1 = require("../../Command");
4
5
  class PowerAppsCommand extends Command_1.default {
5
6
  get resource() {
6
7
  return 'https://api.powerapps.com';
7
8
  }
9
+ initAction(args, logger) {
10
+ super.initAction(args, logger);
11
+ if (!Auth_1.default.service.connected) {
12
+ // we fail no login in the base command command class
13
+ return;
14
+ }
15
+ if (Auth_1.default.service.cloudType !== Auth_1.CloudType.Public) {
16
+ throw new Command_1.CommandError(`Power Apps commands only support the public cloud at the moment. We'll add support for other clouds in the future. Sorry for the inconvenience.`);
17
+ }
18
+ }
8
19
  }
9
20
  exports.default = PowerAppsCommand;
10
21
  //# sourceMappingURL=PowerAppsCommand.js.map
@@ -1,10 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Auth_1 = require("../../Auth");
3
4
  const Command_1 = require("../../Command");
4
5
  class PowerPlatformCommand extends Command_1.default {
5
6
  get resource() {
6
7
  return 'https://api.bap.microsoft.com';
7
8
  }
9
+ initAction(args, logger) {
10
+ super.initAction(args, logger);
11
+ if (!Auth_1.default.service.connected) {
12
+ // we fail no login in the base command command class
13
+ return;
14
+ }
15
+ if (Auth_1.default.service.cloudType !== Auth_1.CloudType.Public) {
16
+ throw new Command_1.CommandError(`Power Platform commands only support the public cloud at the moment. We'll add support for other clouds in the future. Sorry for the inconvenience.`);
17
+ }
18
+ }
8
19
  }
9
20
  exports.default = PowerPlatformCommand;
10
21
  //# sourceMappingURL=PowerPlatformCommand.js.map
@@ -19,6 +19,7 @@ const fs = require("fs");
19
19
  const Auth_1 = require("../../Auth");
20
20
  const Command_1 = require("../../Command");
21
21
  const config_1 = require("../../config");
22
+ const misc_1 = require("../../utils/misc");
22
23
  const commands_1 = require("./commands");
23
24
  class LoginCommand extends Command_1.default {
24
25
  get name() {
@@ -71,6 +72,12 @@ class LoginCommand extends Command_1.default {
71
72
  Auth_1.default.service.secret = args.options.secret;
72
73
  break;
73
74
  }
75
+ if (args.options.cloud) {
76
+ Auth_1.default.service.cloudType = Auth_1.CloudType[args.options.cloud];
77
+ }
78
+ else {
79
+ Auth_1.default.service.cloudType = Auth_1.CloudType.Public;
80
+ }
74
81
  try {
75
82
  yield Auth_1.default.ensureAccessToken(Auth_1.default.defaultResource, logger, this.debug);
76
83
  Auth_1.default.service.connected = true;
@@ -113,8 +120,10 @@ class LoginCommand extends Command_1.default {
113
120
  }
114
121
  _LoginCommand_instances = new WeakSet(), _LoginCommand_initTelemetry = function _LoginCommand_initTelemetry() {
115
122
  this.telemetry.push((args) => {
123
+ var _a;
116
124
  Object.assign(this.telemetryProperties, {
117
- authType: args.options.authType || 'deviceCode'
125
+ authType: args.options.authType || 'deviceCode',
126
+ cloud: (_a = args.options.cloud) !== null && _a !== void 0 ? _a : Auth_1.CloudType.Public
118
127
  });
119
128
  });
120
129
  }, _LoginCommand_initOptions = function _LoginCommand_initOptions() {
@@ -137,6 +146,9 @@ _LoginCommand_instances = new WeakSet(), _LoginCommand_initTelemetry = function
137
146
  option: '--tenant [tenant]'
138
147
  }, {
139
148
  option: '--secret [secret]'
149
+ }, {
150
+ option: '--cloud [cloud]',
151
+ autocomplete: misc_1.misc.getEnums(Auth_1.CloudType)
140
152
  });
141
153
  }, _LoginCommand_initValidators = function _LoginCommand_initValidators() {
142
154
  this.validators.push((args) => __awaiter(this, void 0, void 0, function* () {
@@ -170,6 +182,10 @@ _LoginCommand_instances = new WeakSet(), _LoginCommand_initTelemetry = function
170
182
  return 'Required option secret missing';
171
183
  }
172
184
  }
185
+ if (args.options.cloud &&
186
+ typeof Auth_1.CloudType[args.options.cloud] === 'undefined') {
187
+ return `${args.options.cloud} is not a valid value for cloud. Valid options are ${misc_1.misc.getEnums(Auth_1.CloudType).join(', ')}`;
188
+ }
173
189
  return true;
174
190
  }));
175
191
  };
@@ -39,7 +39,8 @@ class StatusCommand extends Command_1.default {
39
39
  authType: Auth_1.AuthType[Auth_1.default.service.authType],
40
40
  appId: Auth_1.default.service.appId,
41
41
  appTenant: Auth_1.default.service.tenant,
42
- accessTokens: JSON.stringify(Auth_1.default.service.accessTokens, null, 2)
42
+ accessTokens: JSON.stringify(Auth_1.default.service.accessTokens, null, 2),
43
+ cloudType: Auth_1.CloudType[Auth_1.default.service.cloudType]
43
44
  });
44
45
  }
45
46
  else {
@@ -47,7 +48,8 @@ class StatusCommand extends Command_1.default {
47
48
  connectedAs: accessToken_1.accessToken.getUserNameFromAccessToken(Auth_1.default.service.accessTokens[Auth_1.default.defaultResource].accessToken),
48
49
  authType: Auth_1.AuthType[Auth_1.default.service.authType],
49
50
  appId: Auth_1.default.service.appId,
50
- appTenant: Auth_1.default.service.tenant
51
+ appTenant: Auth_1.default.service.tenant,
52
+ cloudType: Auth_1.CloudType[Auth_1.default.service.cloudType]
51
53
  });
52
54
  }
53
55
  }
@@ -13,10 +13,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  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");
14
14
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
15
  };
16
- var _FlowRunGetCommand_instances, _FlowRunGetCommand_initOptions;
16
+ var _FlowRunGetCommand_instances, _FlowRunGetCommand_initOptions, _FlowRunGetCommand_initValidators, _FlowRunGetCommand_initTelemetry;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  const request_1 = require("../../../../request");
19
19
  const formatting_1 = require("../../../../utils/formatting");
20
+ const validation_1 = require("../../../../utils/validation");
20
21
  const AzmgmtCommand_1 = require("../../../base/AzmgmtCommand");
21
22
  const commands_1 = require("../../commands");
22
23
  class FlowRunGetCommand extends AzmgmtCommand_1.default {
@@ -32,7 +33,9 @@ class FlowRunGetCommand extends AzmgmtCommand_1.default {
32
33
  constructor() {
33
34
  super();
34
35
  _FlowRunGetCommand_instances.add(this);
36
+ __classPrivateFieldGet(this, _FlowRunGetCommand_instances, "m", _FlowRunGetCommand_initTelemetry).call(this);
35
37
  __classPrivateFieldGet(this, _FlowRunGetCommand_instances, "m", _FlowRunGetCommand_initOptions).call(this);
38
+ __classPrivateFieldGet(this, _FlowRunGetCommand_instances, "m", _FlowRunGetCommand_initValidators).call(this);
36
39
  }
37
40
  commandAction(logger, args) {
38
41
  return __awaiter(this, void 0, void 0, function* () {
@@ -52,6 +55,18 @@ class FlowRunGetCommand extends AzmgmtCommand_1.default {
52
55
  res.endTime = res.properties.endTime || '';
53
56
  res.status = res.properties.status;
54
57
  res.triggerName = res.properties.trigger.name;
58
+ if (args.options.includeTriggerInformation && res.properties.trigger.outputsLink) {
59
+ const triggerInformationOptions = {
60
+ url: res.properties.trigger.outputsLink.uri,
61
+ headers: {
62
+ accept: 'application/json',
63
+ 'x-anonymous': true
64
+ },
65
+ responseType: 'json'
66
+ };
67
+ const triggerInformationResponse = yield request_1.default.get(triggerInformationOptions);
68
+ res.triggerInformation = triggerInformationResponse.body;
69
+ }
55
70
  logger.log(res);
56
71
  }
57
72
  catch (err) {
@@ -67,6 +82,21 @@ _FlowRunGetCommand_instances = new WeakSet(), _FlowRunGetCommand_initOptions = f
67
82
  option: '-f, --flowName <flowName>'
68
83
  }, {
69
84
  option: '-e, --environmentName <environmentName>'
85
+ }, {
86
+ option: '--includeTriggerInformation'
87
+ });
88
+ }, _FlowRunGetCommand_initValidators = function _FlowRunGetCommand_initValidators() {
89
+ this.validators.push((args) => __awaiter(this, void 0, void 0, function* () {
90
+ if (!validation_1.validation.isValidGuid(args.options.flowName)) {
91
+ return `${args.options.flowName} is not a valid GUID`;
92
+ }
93
+ return true;
94
+ }));
95
+ }, _FlowRunGetCommand_initTelemetry = function _FlowRunGetCommand_initTelemetry() {
96
+ this.telemetry.push((args) => {
97
+ Object.assign(this.telemetryProperties, {
98
+ includeTriggerInformation: !!args.options.includeTriggerInformation
99
+ });
70
100
  });
71
101
  };
72
102
  module.exports = new FlowRunGetCommand();
@@ -35,55 +35,123 @@ class SpoFileListCommand extends SpoCommand_1.default {
35
35
  __classPrivateFieldGet(this, _SpoFileListCommand_instances, "m", _SpoFileListCommand_initValidators).call(this);
36
36
  }
37
37
  commandAction(logger, args) {
38
+ var _a, _b;
38
39
  return __awaiter(this, void 0, void 0, function* () {
39
40
  if (this.verbose) {
40
41
  logger.logToStderr(`Retrieving all files in folder ${args.options.folder} at site ${args.options.webUrl}...`);
41
42
  }
42
43
  try {
43
- const files = yield this.getFiles(args.options.folder, args);
44
- logger.log(files.value);
44
+ const allFiles = [];
45
+ const allFolders = args.options.recursive
46
+ ? [...yield this.getFolders(args.options.folder, args, logger), args.options.folder]
47
+ : [args.options.folder];
48
+ for (const folder of allFolders) {
49
+ const files = yield this.getFiles(folder, args, logger);
50
+ files.forEach((file) => allFiles.push(file));
51
+ }
52
+ // Clean ListItemAllFields.ID property from the output if included
53
+ // Reason: It causes a casing conflict with 'Id' when parsing JSON in PowerShell
54
+ if (((_b = (_a = allFiles[0]) === null || _a === void 0 ? void 0 : _a.ListItemAllFields) === null || _b === void 0 ? void 0 : _b.ID) !== undefined) {
55
+ allFiles.filter(file => { var _a; return ((_a = file.ListItemAllFields) === null || _a === void 0 ? void 0 : _a.ID) !== undefined; }).forEach(file => delete file.ListItemAllFields['ID']);
56
+ }
57
+ logger.log(allFiles);
45
58
  }
46
59
  catch (err) {
47
60
  this.handleRejectedODataJsonPromise(err);
48
61
  }
49
62
  });
50
63
  }
51
- // Gets files from a folder recursively.
52
- getFiles(folderUrl, args, files = { value: [] }) {
53
- // If --recursive option is specified, retrieve both Files and Folder details, otherwise only Files.
54
- const expandParameters = args.options.recursive ? 'Files,Folders' : 'Files';
55
- let requestUrl = `${args.options.webUrl}/_api/web/GetFolderByServerRelativeUrl('${formatting_1.formatting.encodeQueryParameter(folderUrl)}')?$expand=${expandParameters}`;
56
- if (args.options.output !== 'json') {
57
- requestUrl += '&$select=Files/UniqueId,Files/Name,Files/ServerRelativeUrl';
58
- }
59
- const requestOptions = {
60
- url: requestUrl,
61
- method: 'GET',
62
- headers: {
63
- 'accept': 'application/json;odata=nometadata'
64
- },
65
- responseType: 'json'
66
- };
67
- return request_1.default
68
- .get(requestOptions)
69
- .then((filesAndFoldersResult) => {
70
- filesAndFoldersResult.Files.forEach((file) => files.value.push(file));
71
- // If the request is --recursive, call this method for other folders.
72
- if (args.options.recursive &&
73
- filesAndFoldersResult.Folders !== undefined &&
74
- filesAndFoldersResult.Folders.length !== 0) {
75
- return Promise.all(filesAndFoldersResult.Folders.map((folder) => this.getFiles(folder.ServerRelativeUrl, args, files)));
64
+ getFiles(folderUrl, args, logger, skip = 0) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ if (this.verbose) {
67
+ const page = Math.ceil(skip / SpoFileListCommand.pageSize) + 1;
68
+ logger.logToStderr(`Retrieving files in folder ${folderUrl}${page > 1 ? ', page ' + page : ''}...`);
69
+ }
70
+ const allFiles = [];
71
+ const requestUrl = `${args.options.webUrl}/_api/web/GetFolderByServerRelativeUrl('${formatting_1.formatting.encodeQueryParameter(folderUrl)}')/Files`;
72
+ const fieldsProperties = this.formatSelectProperties(args.options.fields, args.options.output);
73
+ const queryParams = [`$skip=${skip}`, `$top=${SpoFileListCommand.pageSize}`];
74
+ if (fieldsProperties.expandProperties.length > 0) {
75
+ queryParams.push(`$expand=${fieldsProperties.expandProperties.join(',')}`);
76
+ }
77
+ if (fieldsProperties.selectProperties.length > 0) {
78
+ queryParams.push(`$select=${fieldsProperties.selectProperties.join(',')}`);
76
79
  }
77
- else {
78
- return;
80
+ if (args.options.filter) {
81
+ queryParams.push(`$filter=${args.options.filter}`);
79
82
  }
80
- }).then(() => files);
83
+ const requestOptions = {
84
+ url: `${requestUrl}?${queryParams.join('&')}`,
85
+ method: 'GET',
86
+ headers: {
87
+ 'accept': 'application/json;odata=nometadata'
88
+ },
89
+ responseType: 'json'
90
+ };
91
+ const response = yield request_1.default.get(requestOptions);
92
+ response.value.forEach(file => allFiles.push(file));
93
+ if (response.value.length === SpoFileListCommand.pageSize) {
94
+ const files = yield this.getFiles(folderUrl, args, logger, skip + SpoFileListCommand.pageSize);
95
+ files.forEach(file => allFiles.push(file));
96
+ }
97
+ return allFiles;
98
+ });
99
+ }
100
+ getFolders(folderUrl, args, logger, skip = 0) {
101
+ return __awaiter(this, void 0, void 0, function* () {
102
+ if (this.verbose) {
103
+ const page = Math.ceil(skip / SpoFileListCommand.pageSize) + 1;
104
+ logger.logToStderr(`Retrieving sub folders in folder ${folderUrl}${page > 1 ? ', page ' + page : ''}...`);
105
+ }
106
+ const allFolders = [];
107
+ const requestOptions = {
108
+ url: `${args.options.webUrl}/_api/web/GetFolderByServerRelativeUrl('${formatting_1.formatting.encodeQueryParameter(folderUrl)}')/Folders?$skip=${skip}&$top=${SpoFileListCommand.pageSize}&$select=ServerRelativeUrl`,
109
+ method: 'GET',
110
+ headers: {
111
+ 'accept': 'application/json;odata=nometadata'
112
+ },
113
+ responseType: 'json'
114
+ };
115
+ const response = yield request_1.default.get(requestOptions);
116
+ for (const folder of response.value) {
117
+ allFolders.push(folder.ServerRelativeUrl);
118
+ const subfolders = yield this.getFolders(folder.ServerRelativeUrl, args, logger);
119
+ subfolders.forEach(folder => allFolders.push(folder));
120
+ }
121
+ if (response.value.length === SpoFileListCommand.pageSize) {
122
+ const folders = yield this.getFolders(folderUrl, args, logger, skip + SpoFileListCommand.pageSize);
123
+ folders.forEach(folder => allFolders.push(folder));
124
+ }
125
+ return allFolders;
126
+ });
127
+ }
128
+ formatSelectProperties(fields, output) {
129
+ let selectProperties = [];
130
+ const expandProperties = [];
131
+ if (output === 'text' && !fields) {
132
+ selectProperties = ['UniqueId', 'Name', 'ServerRelativeUrl'];
133
+ }
134
+ if (fields) {
135
+ fields.split(',').forEach((field) => {
136
+ const subparts = field.trim().split('/');
137
+ if (subparts.length > 1) {
138
+ expandProperties.push(subparts[0]);
139
+ }
140
+ selectProperties.push(field.trim());
141
+ });
142
+ }
143
+ return {
144
+ selectProperties: [...new Set(selectProperties)],
145
+ expandProperties: [...new Set(expandProperties)]
146
+ };
81
147
  }
82
148
  }
83
149
  _SpoFileListCommand_instances = new WeakSet(), _SpoFileListCommand_initTelemetry = function _SpoFileListCommand_initTelemetry() {
84
150
  this.telemetry.push((args) => {
85
151
  Object.assign(this.telemetryProperties, {
86
- recursive: args.options.recursive
152
+ recursive: args.options.recursive,
153
+ fields: typeof args.options.fields !== 'undefined',
154
+ filter: typeof args.options.filter !== 'undefined'
87
155
  });
88
156
  });
89
157
  }, _SpoFileListCommand_initOptions = function _SpoFileListCommand_initOptions() {
@@ -91,11 +159,16 @@ _SpoFileListCommand_instances = new WeakSet(), _SpoFileListCommand_initTelemetry
91
159
  option: '-u, --webUrl <webUrl>'
92
160
  }, {
93
161
  option: '-f, --folder <folder>'
162
+ }, {
163
+ option: '--fields [fields]'
164
+ }, {
165
+ option: '--filter [filter]'
94
166
  }, {
95
167
  option: '-r, --recursive'
96
168
  });
97
169
  }, _SpoFileListCommand_initValidators = function _SpoFileListCommand_initValidators() {
98
170
  this.validators.push((args) => __awaiter(this, void 0, void 0, function* () { return validation_1.validation.isValidSharePointUrl(args.options.webUrl); }));
99
171
  };
172
+ SpoFileListCommand.pageSize = 5000;
100
173
  module.exports = new SpoFileListCommand();
101
174
  //# sourceMappingURL=file-list.js.map