@pnp/cli-microsoft365 5.1.0-beta.c27e2b5 → 5.1.0-beta.c518929

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.js CHANGED
@@ -59,6 +59,7 @@ const dictionary = [
59
59
  'site',
60
60
  'status',
61
61
  'storage',
62
+ 'teams',
62
63
  'token',
63
64
  'type',
64
65
  'user',
package/Dockerfile CHANGED
@@ -21,7 +21,9 @@ RUN apk add --no-cache \
21
21
  shadow \
22
22
  bash-completion \
23
23
  nodejs \
24
- npm
24
+ npm \
25
+ python3 \
26
+ py3-pip
25
27
 
26
28
  RUN adduser --system cli-microsoft365
27
29
  USER cli-microsoft365
@@ -31,11 +33,10 @@ WORKDIR /home/cli-microsoft365
31
33
  ENV 0="/bin/bash" \
32
34
  SHELL="bash" \
33
35
  NPM_CONFIG_PREFIX=/home/cli-microsoft365/.npm-global \
34
- PATH=$PATH:/home/cli-microsoft365/.npm-global/bin \
36
+ PATH=$PATH:/home/cli-microsoft365/.npm-global/bin:/home/cli-microsoft365/.local/bin \
35
37
  CLIMICROSOFT365_ENV="docker"
36
38
 
37
- RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin" >> ~/.bash_profile' \
38
- && bash -c 'echo "export NPM_CONFIG_PREFIX=/home/cli-microsoft365/.npm-global" >> ~/.bash_profile' \
39
+ RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin:/home/.local/bin" >> ~/.bash_profile' \
39
40
  && bash -c 'echo "export CLIMICROSOFT365_ENV=\"docker\"" >> ~/.bash_profile' \
40
41
  && bash -c 'npm i -g @pnp/cli-microsoft365@${CLI_VERSION} --production --quiet --no-progress' \
41
42
  && bash -c 'echo "source /etc/profile.d/bash_completion.sh" >> ~/.bash_profile' \
@@ -43,4 +44,6 @@ RUN bash -c 'echo "export PATH=$PATH:/home/cli-microsoft365/.npm-global/bin" >>
43
44
  && bash -c 'm365 cli completion sh setup' \
44
45
  && pwsh -c 'm365 cli completion pwsh setup --profile $profile'
45
46
 
47
+ RUN pip install jmespath-terminal
48
+
46
49
  CMD [ "bash", "-l" ]
package/dist/Command.js CHANGED
@@ -123,6 +123,9 @@ class Command {
123
123
  }
124
124
  ];
125
125
  }
126
+ optionSets() {
127
+ return;
128
+ }
126
129
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
127
130
  validate(args) {
128
131
  return true;
package/dist/cli/Cli.js CHANGED
@@ -163,6 +163,19 @@ class Cli {
163
163
  if (optionsWithoutShorts.options.output === undefined) {
164
164
  optionsWithoutShorts.options.output = this.getSettingWithDefaultValue(settingsNames_1.settingsNames.output, undefined);
165
165
  }
166
+ // validate options sets defined by the command
167
+ const optionsSets = this.commandToExecute.command.optionSets();
168
+ const argsOptions = Object.keys(this.optionsFromArgs.options);
169
+ const cmdArgs = this.optionsFromArgs;
170
+ optionsSets === null || optionsSets === void 0 ? void 0 : optionsSets.forEach((optionSet) => {
171
+ const commonOptions = argsOptions.filter(opt => optionSet.includes(opt));
172
+ if (commonOptions.length === 0) {
173
+ return this.closeWithError(`Specify one of the following options: ${optionSet.map(opt => opt).join(', ')}.`, cmdArgs, true);
174
+ }
175
+ if (commonOptions.length > 1) {
176
+ return this.closeWithError(`Specify one of the following options: ${optionSet.map(opt => opt).join(', ')}, but not multiple.`, cmdArgs, true);
177
+ }
178
+ });
166
179
  const validationResult = this.commandToExecute.command.validate(optionsWithoutShorts);
167
180
  if (typeof validationResult === 'string') {
168
181
  return this.closeWithError(validationResult, optionsWithoutShorts, true);
@@ -481,6 +481,9 @@ class SpfxDoctorCommand extends AnonymousCommand_1.default {
481
481
  return 'Verifies environment configuration for using the specific version of the SharePoint Framework';
482
482
  }
483
483
  commandAction(logger, args, cb) {
484
+ if (!args.options.output) {
485
+ args.options.output = 'text';
486
+ }
484
487
  logger.log(' ');
485
488
  logger.log('CLI for Microsoft 365 SharePoint Framework doctor');
486
489
  logger.log('Verifying configuration of your system for working with the SharePoint Framework');
@@ -758,6 +761,9 @@ class SpfxDoctorCommand extends AnonymousCommand_1.default {
758
761
  return `${args.options.env} is not a valid SharePoint version. Valid versions are sp2016, sp2019 or spo`;
759
762
  }
760
763
  }
764
+ if (args.options.output && args.options.output !== 'text') {
765
+ return `The output option only accepts the type 'text'`;
766
+ }
761
767
  return true;
762
768
  }
763
769
  }
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const request_1 = require("../../../../request");
6
+ const urlUtil_1 = require("../../../../utils/urlUtil");
7
+ const spo_1 = require("../../../../utils/spo");
8
+ const validation_1 = require("../../../../utils/validation");
9
+ const commands_1 = require("../../commands");
10
+ const SpoAppBaseCommand_1 = require("./SpoAppBaseCommand");
11
+ class SpoAppTeamsPackageDownloadCommand extends SpoAppBaseCommand_1.SpoAppBaseCommand {
12
+ get name() {
13
+ return commands_1.default.APP_TEAMSPACKAGE_DOWNLOAD;
14
+ }
15
+ get description() {
16
+ return 'Downloads Teams app package for an SPFx solution deployed to tenant app catalog';
17
+ }
18
+ getTelemetryProperties(args) {
19
+ const telemetryProps = super.getTelemetryProperties(args);
20
+ telemetryProps.appCatalogUrl = typeof args.options.appCatalogUrl !== 'undefined';
21
+ telemetryProps.appItemUniqueId = typeof args.options.appItemUniqueId !== 'undefined';
22
+ telemetryProps.appItemId = typeof args.options.appItemId !== 'undefined';
23
+ telemetryProps.appName = typeof args.options.appName !== 'undefined';
24
+ telemetryProps.fileName = typeof args.options.fileName !== 'undefined';
25
+ return telemetryProps;
26
+ }
27
+ commandAction(logger, args, cb) {
28
+ var _a, _b;
29
+ this.appCatalogUrl = args.options.appCatalogUrl;
30
+ const appInfo = {
31
+ id: (_a = args.options.appItemId) !== null && _a !== void 0 ? _a : undefined,
32
+ packageFileName: (_b = args.options.fileName) !== null && _b !== void 0 ? _b : undefined
33
+ };
34
+ if (this.debug) {
35
+ logger.logToStderr(`appInfo: ${JSON.stringify(appInfo)}`);
36
+ }
37
+ this
38
+ .ensureAppInfo(logger, args, appInfo)
39
+ .then(_ => {
40
+ if (this.debug) {
41
+ logger.logToStderr(`ensureAppInfo: ${JSON.stringify(appInfo)}`);
42
+ }
43
+ return this.loadAppCatalogUrl(logger, args);
44
+ })
45
+ .then(_ => {
46
+ const requestOptions = {
47
+ url: `${this.appCatalogUrl}/_api/web/tenantappcatalog/downloadteamssolution(${appInfo.id})/$value`,
48
+ headers: {
49
+ accept: 'application/json;odata=nometadata'
50
+ },
51
+ responseType: 'stream'
52
+ };
53
+ return request_1.default.get(requestOptions);
54
+ })
55
+ .then((file) => {
56
+ return new Promise((resolve, reject) => {
57
+ const writer = fs.createWriteStream(appInfo.packageFileName);
58
+ file.data.pipe(writer);
59
+ writer.on('error', err => {
60
+ reject(err);
61
+ });
62
+ writer.on('close', () => {
63
+ resolve(appInfo.packageFileName);
64
+ });
65
+ });
66
+ })
67
+ .then((file) => {
68
+ if (this.verbose) {
69
+ logger.logToStderr(`Package saved to ${file}`);
70
+ }
71
+ cb();
72
+ }, (err) => this.handleRejectedODataJsonPromise(err, logger, cb));
73
+ }
74
+ ensureAppInfo(logger, args, appInfo) {
75
+ if (appInfo.id && appInfo.packageFileName) {
76
+ return Promise.resolve();
77
+ }
78
+ if (args.options.appName && !appInfo.packageFileName) {
79
+ appInfo.packageFileName = this.getPackageNameFromFileName(args.options.appName);
80
+ }
81
+ return this
82
+ .loadAppCatalogUrl(logger, args)
83
+ .then(_ => {
84
+ const appCatalogListName = 'AppCatalog';
85
+ const serverRelativeAppCatalogListUrl = `${urlUtil_1.urlUtil.getServerRelativeSiteUrl(this.appCatalogUrl)}/${appCatalogListName}`;
86
+ let url = `${this.appCatalogUrl}/_api/web/`;
87
+ if (args.options.appItemUniqueId) {
88
+ url += `GetList('${serverRelativeAppCatalogListUrl}')/GetItemByUniqueId('${args.options.appItemUniqueId}')?$expand=File&$select=Id,File/Name`;
89
+ }
90
+ else if (args.options.appItemId) {
91
+ url += `GetList('${serverRelativeAppCatalogListUrl}')/GetItemById(${args.options.appItemId})?$expand=File&$select=File/Name`;
92
+ }
93
+ else if (args.options.appName) {
94
+ url += `getfolderbyserverrelativeurl('${appCatalogListName}')/files('${encodeURIComponent(args.options.appName)}')/ListItemAllFields?$select=Id`;
95
+ }
96
+ const requestOptions = {
97
+ url,
98
+ headers: {
99
+ accept: 'application/json;odata=nometadata'
100
+ },
101
+ responseType: 'json'
102
+ };
103
+ return request_1.default.get(requestOptions);
104
+ })
105
+ .then(res => {
106
+ if (args.options.appItemUniqueId) {
107
+ appInfo.id = parseInt(res.Id);
108
+ if (!appInfo.packageFileName) {
109
+ appInfo.packageFileName = this.getPackageNameFromFileName(res.File.Name);
110
+ }
111
+ return Promise.resolve();
112
+ }
113
+ if (args.options.appItemId) {
114
+ if (!appInfo.packageFileName) {
115
+ appInfo.packageFileName = this.getPackageNameFromFileName(res.File.Name);
116
+ }
117
+ return Promise.resolve();
118
+ }
119
+ // if (args.options.appName)
120
+ // skipped 'if' clause to provide a default code branch
121
+ appInfo.id = parseInt(res.Id);
122
+ return Promise.resolve();
123
+ });
124
+ }
125
+ getPackageNameFromFileName(fileName) {
126
+ return `${path.basename(fileName, path.extname(fileName))}.zip`;
127
+ }
128
+ loadAppCatalogUrl(logger, args) {
129
+ if (this.appCatalogUrl) {
130
+ return Promise.resolve();
131
+ }
132
+ return spo_1.spo
133
+ .getSpoUrl(logger, this.debug)
134
+ .then(spoUrl => this.getAppCatalogSiteUrl(logger, spoUrl, args))
135
+ .then(appCatalogUrl => {
136
+ this.appCatalogUrl = appCatalogUrl;
137
+ });
138
+ }
139
+ options() {
140
+ const options = [
141
+ { option: '--appItemId [appItemId]' },
142
+ { option: '--appItemUniqueId [appItemUniqueId]' },
143
+ { option: '--appName [appName]' },
144
+ { option: '--fileName [fileName]' },
145
+ { option: '-u, --appCatalogUrl [appCatalogUrl]' }
146
+ ];
147
+ const parentOptions = super.options();
148
+ return options.concat(parentOptions);
149
+ }
150
+ validate(args) {
151
+ if (!args.options.appItemUniqueId &&
152
+ !args.options.appItemId &&
153
+ !args.options.appName) {
154
+ return `Specify appItemUniqueId, appItemId or appName`;
155
+ }
156
+ if ((args.options.appItemUniqueId && args.options.appItemId) ||
157
+ (args.options.appItemUniqueId && args.options.appName) ||
158
+ (args.options.appItemId && args.options.appName)) {
159
+ return `Specify appItemUniqueId, appItemId or appName but not multiple`;
160
+ }
161
+ if (args.options.appItemUniqueId &&
162
+ !validation_1.validation.isValidGuid(args.options.appItemUniqueId)) {
163
+ return `${args.options.appItemUniqueId} is not a valid GUID`;
164
+ }
165
+ if (args.options.appItemId &&
166
+ isNaN(args.options.appItemId)) {
167
+ return `${args.options.appItemId} is not a number`;
168
+ }
169
+ if (args.options.fileName &&
170
+ fs.existsSync(args.options.fileName)) {
171
+ return `File ${args.options.fileName} already exists`;
172
+ }
173
+ if (args.options.appCatalogUrl) {
174
+ return validation_1.validation.isValidSharePointUrl(args.options.appCatalogUrl);
175
+ }
176
+ return true;
177
+ }
178
+ }
179
+ module.exports = new SpoAppTeamsPackageDownloadCommand();
180
+ //# sourceMappingURL=app-teamspackage-download.js.map
@@ -10,6 +10,7 @@ exports.default = {
10
10
  APP_LIST: `${prefix} app list`,
11
11
  APP_REMOVE: `${prefix} app remove`,
12
12
  APP_RETRACT: `${prefix} app retract`,
13
+ APP_TEAMSPACKAGE_DOWNLOAD: `${prefix} app teamspackage download`,
13
14
  APP_UNINSTALL: `${prefix} app uninstall`,
14
15
  APP_UPGRADE: `${prefix} app upgrade`,
15
16
  APPPAGE_ADD: `${prefix} apppage add`,
@@ -20,7 +20,7 @@ m365 spfx doctor [options]
20
20
  : JMESPath query string. See [http://jmespath.org/](http://jmespath.org/) for more information and examples
21
21
 
22
22
  `-o, --output [output]`
23
- : Output type. `json,text,csv,md`. Default `json`
23
+ : Output type. `text`. Default `text`
24
24
 
25
25
  `--verbose`
26
26
  : Runs command with verbose logging
@@ -43,6 +43,9 @@ If you miss any required tools or use a version that doesn't meet the SharePoint
43
43
 
44
44
  Next to verifying the readiness of your environment to use a particular version of the SharePoint Framework, you can also check if the version of the SharePoint Framework that you use is compatible with the specific version of SharePoint. Supported versions are `sp2016`, `sp2019` and `spo`.
45
45
 
46
+ !!! important
47
+ This command supports only text output.
48
+
46
49
  ## Examples
47
50
 
48
51
  Verify if your environment meets the requirements to work with the SharePoint Framework
@@ -0,0 +1,56 @@
1
+ # spo app teamspackage download
2
+
3
+ Downloads Teams app package for an SPFx solution deployed to tenant app catalog
4
+
5
+ ## Usage
6
+
7
+ ```sh
8
+ m365 spo app teamspackage download [options]
9
+ ```
10
+
11
+ ## Options
12
+
13
+ `--appItemUniqueId [appItemUniqueId]`
14
+ : The unique ID of the SPFx app to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
15
+
16
+ `--appItemId [appItemId]`
17
+ : The ID of the list item behind the SPFx app to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
18
+
19
+ `--appName [appName]`
20
+ : The name of the sppkg file to download the Teams package for. Specify `appItemUniqueId`, `appItemId` or `appName`
21
+
22
+ `--fileName [fileName]`
23
+ : Name of the file to save the package to. If not specified will use the name of the sppkg file with a `.zip` extension
24
+
25
+ `-u, --appCatalogUrl [appCatalogUrl]`
26
+ : URL of the tenant app catalog. If not specified, the command will try to autodiscover it
27
+
28
+ --8<-- "docs/cmd/_global.md"
29
+
30
+ ## Remarks
31
+
32
+ Download the Teams app package for an SPFx solution works only for solutions deployed to the tenant app catalog.
33
+
34
+ If you try to download Teams app package for an SPFx solution that doesn't support deployment to Teams, you'll get the _Request failed with status code 404_ error.
35
+
36
+ For maximum performance, specify the URL of the tenant app catalog, the item ID (`appItemId`) of the SPFx package for which you want to download the Teams app package and the name of the file where you want to save the downloaded package to (`fileName`).
37
+
38
+ ## Examples
39
+
40
+ Downloads the Teams app package for the SPFx solution deployed to the tenant app catalog with the ID `1` to a file with .zip extension named after the .sppkg file:
41
+
42
+ ```sh
43
+ m365 spo app teamspackage download --appItemId 1
44
+ ```
45
+
46
+ Downloads the Teams app package for the SPFx solution deployed to the tenant app catalog with the unique item ID `335a5612-3e85-462d-9d5b-c014b5abeac5` to the specified file:
47
+
48
+ ```sh
49
+ m365 spo app teamspackage download --appItemUniqueId 335a5612-3e85-462d-9d5b-c014b5abeac5 --fileName my-app.zip
50
+ ```
51
+
52
+ Downloads the Teams app package for the SPFx solution deployed to the specified tenant app catalog:
53
+
54
+ ```sh
55
+ m365 spo app teamspackage download --appName my-app.sppkg --appCatalogUrl https://contoso.sharepoint.com/sites/appcatalog
56
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnp/cli-microsoft365",
3
- "version": "5.1.0-beta.c27e2b5",
3
+ "version": "5.1.0-beta.c518929",
4
4
  "description": "Manage Microsoft 365 and SharePoint Framework projects on any platform",
5
5
  "license": "MIT",
6
6
  "main": "./dist/api.js",