Skip to content

Commit f08b74a

Browse files
committed
Adds 'pa environment get' script, solving #2109
1 parent 30366a8 commit f08b74a

6 files changed

Lines changed: 272 additions & 1 deletion

File tree

docs/docs/about/comparison-powershell.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ Get-PowerAppConnection|Microsoft.PowerApps.PowerShell|
715715
Get-PowerAppConnectionRoleAssignment|Microsoft.PowerApps.PowerShell|
716716
Get-PowerAppConnector|Microsoft.PowerApps.PowerShell|
717717
Get-PowerAppConnectorRoleAssignment|Microsoft.PowerApps.PowerShell|
718-
Get-PowerAppEnvironment|Microsoft.PowerApps.PowerShell|[pa environment list](../cmd/pa/environment/environment-list.md)
718+
Get-PowerAppEnvironment|Microsoft.PowerApps.PowerShell|[pa environment list](../cmd/pa/environment/environment-list.md), [pa environment get](../cmd/pa/environment/environment-get.md)
719719
Get-PowerAppRoleAssignment|Microsoft.PowerApps.PowerShell|
720720
Get-PowerAppsNotification|Microsoft.PowerApps.PowerShell|
721721
Get-PowerAppVersion|Microsoft.PowerApps.PowerShell|
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# pa environment ga
2+
3+
Gets information about the specified Microsoft Power Apps environment
4+
5+
## Usage
6+
7+
```sh
8+
m365 pa environment get [options]
9+
```
10+
11+
## Options
12+
13+
--8<-- "docs/cmd/_global.md"
14+
15+
## Remarks
16+
17+
!!! attention
18+
This command is based on an API that is currently in preview and is subject to change once the API reached general availability.
19+
20+
If the environment with the name you specified doesn't exist, you will get the `Access to the environment 'xyz' is denied.` error.
21+
22+
## Examples
23+
24+
Get information about the Microsoft Apps environment named _Default-d87a7535-dd31-4437-bfe1-95340acd55c5_
25+
26+
```sh
27+
m365 pa environment get --name Default-d87a7535-dd31-4437-bfe1-95340acd55c5
28+
```

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ nav:
141141
- connector export: 'cmd/pa/connector/connector-export.md'
142142
- connector list: 'cmd/pa/connector/connector-list.md'
143143
- environment:
144+
- environment get: 'cmd/pa/environment/environment-get.md'
144145
- environment list: 'cmd/pa/environment/environment-list.md'
145146
- pcf:
146147
- pcf init: 'cmd/pa/pcf/pcf-init.md'

src/m365/pa/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default {
44
APP_LIST: `${prefix} app list`,
55
CONNECTOR_EXPORT: `${prefix} connector export`,
66
CONNECTOR_LIST: `${prefix} connector list`,
7+
ENVIRONMENT_GET: `${prefix} environment get`,
78
ENVIRONMENT_LIST: `${prefix} environment list`,
89
PCF_INIT: `${prefix} pcf init`,
910
SOLUTION_INIT: `${prefix} solution init`,
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import * as assert from 'assert';
2+
import * as sinon from 'sinon';
3+
import appInsights from '../../../../appInsights';
4+
import auth from '../../../../Auth';
5+
import { Logger } from '../../../../cli';
6+
import Command, { CommandError } from '../../../../Command';
7+
import request from '../../../../request';
8+
import Utils from '../../../../Utils';
9+
import commands from '../../commands';
10+
const command: Command = require('./environment-get');
11+
12+
describe(commands.ENVIRONMENT_GET, () => {
13+
let log: string[];
14+
let logger: Logger;
15+
let loggerLogSpy: sinon.SinonSpy;
16+
17+
before(() => {
18+
sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.resolve());
19+
sinon.stub(appInsights, 'trackEvent').callsFake(() => {});
20+
auth.service.connected = true;
21+
});
22+
23+
beforeEach(() => {
24+
log = [];
25+
logger = {
26+
log: (msg: string) => {
27+
log.push(msg);
28+
},
29+
logRaw: (msg: string) => {
30+
log.push(msg);
31+
},
32+
logToStderr: (msg: string) => {
33+
log.push(msg);
34+
}
35+
};
36+
loggerLogSpy = sinon.spy(logger, 'log');
37+
});
38+
39+
afterEach(() => {
40+
Utils.restore([
41+
request.get
42+
]);
43+
});
44+
45+
after(() => {
46+
Utils.restore([
47+
auth.restoreAuth,
48+
appInsights.trackEvent
49+
]);
50+
auth.service.connected = false;
51+
});
52+
53+
it('has correct name', () => {
54+
assert.strictEqual(command.name.startsWith(commands.ENVIRONMENT_GET), true);
55+
});
56+
57+
it('has a description', () => {
58+
assert.notStrictEqual(command.description, null);
59+
});
60+
61+
it('retrieves information about the specified environment (debug)', (done) => {
62+
const env: any = {"name":"Default-d87a7535-dd31-4437-bfe1-95340acd55c5","location":"europe","type":"Microsoft.PowerApps/environments","id":"/providers/Microsoft.PowerApps/environments/Default-d87a7535-dd31-4437-bfe1-95340acd55c5","properties":{"displayName":"Contoso (default)","createdTime":"2018-03-22T20:20:46.08653Z","createdBy":{"id":"SYSTEM","displayName":"SYSTEM","type":"NotSpecified"},"provisioningState":"Succeeded","creationType":"DefaultTenant","environmentSku":"Default","environmentType":"Production","isDefault":true,"azureRegionHint":"westeurope","runtimeEndpoints":{"microsoft.BusinessAppPlatform":"https://europe.api.bap.microsoft.com","microsoft.CommonDataModel":"https://europe.api.cds.microsoft.com","microsoft.PowerApps":"https://europe.api.powerapps.com","microsoft.Flow":"https://europe.api.flow.microsoft.com"}}};
63+
64+
sinon.stub(request, 'get').callsFake((opts) => {
65+
if ((opts.url as string).indexOf(`providers/Microsoft.PowerApps/environments/Default-d87a7535-dd31-4437-bfe1-95340acd55c5?api-version=2016-11-01`) > -1) {
66+
if (opts.headers &&
67+
opts.headers.accept &&
68+
opts.headers.accept.indexOf('application/json') === 0) {
69+
return Promise.resolve(env);
70+
}
71+
}
72+
73+
return Promise.reject('Invalid request');
74+
});
75+
76+
command.action(logger, { options: { debug: true, name: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c5' } }, () => {
77+
try {
78+
assert(loggerLogSpy.calledWith(env));
79+
done();
80+
}
81+
catch (e) {
82+
done(e);
83+
}
84+
});
85+
});
86+
87+
it('retrieves information about the specified environment', (done) => {
88+
const env: any = {"name":"Default-d87a7535-dd31-4437-bfe1-95340acd55c5","location":"europe","type":"Microsoft.PowerApps/environments","id":"/providers/Microsoft.PowerApps/environments/Default-d87a7535-dd31-4437-bfe1-95340acd55c5","properties":{"displayName":"Contoso (default)","createdTime":"2018-03-22T20:20:46.08653Z","createdBy":{"id":"SYSTEM","displayName":"SYSTEM","type":"NotSpecified"},"provisioningState":"Succeeded","creationType":"DefaultTenant","environmentSku":"Default","environmentType":"Production","isDefault":true,"azureRegionHint":"westeurope","runtimeEndpoints":{"microsoft.BusinessAppPlatform":"https://europe.api.bap.microsoft.com","microsoft.CommonDataModel":"https://europe.api.cds.microsoft.com","microsoft.PowerApps":"https://europe.api.powerapps.com","microsoft.Flow":"https://europe.api.flow.microsoft.com"}}};
89+
90+
sinon.stub(request, 'get').callsFake((opts) => {
91+
if ((opts.url as string).indexOf(`providers/Microsoft.PowerApps/environments/Default-d87a7535-dd31-4437-bfe1-95340acd55c5?api-version=2016-11-01`) > -1) {
92+
if (opts.headers &&
93+
opts.headers.accept &&
94+
opts.headers.accept.indexOf('application/json') === 0) {
95+
return Promise.resolve(env);
96+
}
97+
}
98+
99+
return Promise.reject('Invalid request');
100+
});
101+
102+
command.action(logger, { options: { debug: false, name: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c5' } }, () => {
103+
try {
104+
assert(loggerLogSpy.calledWith(env));
105+
done();
106+
}
107+
catch (e) {
108+
done(e);
109+
}
110+
});
111+
});
112+
113+
it('correctly handles no environment found', (done) => {
114+
sinon.stub(request, 'get').callsFake((opts) => {
115+
return Promise.reject({
116+
"error": {
117+
"code": "EnvironmentAccessDenied",
118+
"message": "Access to the environment 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6' is denied."
119+
}
120+
});
121+
});
122+
123+
command.action(logger, { options: { debug: false, name: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6' } } as any, (err?: any) => {
124+
try {
125+
assert.strictEqual(JSON.stringify(err), JSON.stringify(new CommandError(`Access to the environment 'Default-d87a7535-dd31-4437-bfe1-95340acd55c6' is denied.`)));
126+
done();
127+
}
128+
catch (e) {
129+
done(e);
130+
}
131+
});
132+
});
133+
134+
it('correctly handles API OData error', (done) => {
135+
sinon.stub(request, 'get').callsFake((opts) => {
136+
return Promise.reject({
137+
error: {
138+
'odata.error': {
139+
code: '-1, InvalidOperationException',
140+
message: {
141+
value: 'An error has occurred'
142+
}
143+
}
144+
}
145+
});
146+
});
147+
148+
command.action(logger, { options: { debug: false, name: 'Default-d87a7535-dd31-4437-bfe1-95340acd55c5' } } as any, (err?: any) => {
149+
try {
150+
assert.strictEqual(JSON.stringify(err), JSON.stringify(new CommandError('An error has occurred')));
151+
done();
152+
}
153+
catch (e) {
154+
done(e);
155+
}
156+
});
157+
});
158+
159+
it('supports debug mode', () => {
160+
const options = command.options();
161+
let containsOption = false;
162+
options.forEach(o => {
163+
if (o.option === '--debug') {
164+
containsOption = true;
165+
}
166+
});
167+
assert(containsOption);
168+
});
169+
170+
it('supports specifying name', () => {
171+
const options = command.options();
172+
let containsOption = false;
173+
options.forEach(o => {
174+
if (o.option.indexOf('--name') > -1) {
175+
containsOption = true;
176+
}
177+
});
178+
assert(containsOption);
179+
});
180+
});
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Logger } from '../../../../cli';
2+
import {
3+
CommandOption
4+
} from '../../../../Command';
5+
import GlobalOptions from '../../../../GlobalOptions';
6+
import request from '../../../../request';
7+
import AzmgmtCommand from '../../../base/AzmgmtCommand';
8+
import commands from '../../commands';
9+
10+
interface CommandArgs {
11+
options: Options;
12+
}
13+
14+
interface Options extends GlobalOptions {
15+
name: string;
16+
}
17+
18+
class PaEnvironmentGetCommand extends AzmgmtCommand {
19+
public get name(): string {
20+
return commands.ENVIRONMENT_GET;
21+
}
22+
23+
public get description(): string {
24+
return 'Gets information about the specified Microsoft Power Apps environment';
25+
}
26+
27+
public commandAction(logger: Logger, args: CommandArgs, cb: () => void): void {
28+
if (this.verbose) {
29+
logger.logToStderr(`Retrieving information about Microsoft Power Apps environment ${args.options.name}...`);
30+
}
31+
32+
const requestOptions: any = {
33+
url: `${this.resource}providers/Microsoft.PowerApps/environments/${encodeURIComponent(args.options.name)}?api-version=2016-11-01`,
34+
headers: {
35+
accept: 'application/json'
36+
},
37+
responseType: 'json'
38+
};
39+
40+
request
41+
.get(requestOptions)
42+
.then((res: any): void => {
43+
logger.log(res);
44+
45+
cb();
46+
}, (rawRes: any): void => this.handleRejectedODataJsonPromise(rawRes, logger, cb));
47+
}
48+
49+
public options(): CommandOption[] {
50+
const options: CommandOption[] = [
51+
{
52+
option: '-n, --name <name>'
53+
}
54+
];
55+
56+
const parentOptions: CommandOption[] = super.options();
57+
return options.concat(parentOptions);
58+
}
59+
}
60+
61+
module.exports = new PaEnvironmentGetCommand();

0 commit comments

Comments
 (0)