Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions docs/docs/cmd/pp/pipeline/pipeline-list.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import Global from '/docs/cmd/_global.mdx';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# pp pipeline list

Lists the Power Platform pipelines for the specified environment.

## Usage

```sh
m365 pp pipeline list [options]
```

## Options

`-e, --environmentName <environment>`
: The name or id of the environment for which to list the pipelines.

`--asAdmin`
: Run the command as admin and retrieve pipelines from environments you do not have explicitly assigned permissions to.

<Global />

## Remarks

:::warning

This command is based on an API that is currently in preview and is subject to change once the API reaches general availability.

Register the CLI for Microsoft 365 or Microsoft Entra application as a management application for the Power Platform using

m365 pp managementapp add [options]

:::

## Examples

Retrieve the list of deployment pipelines in the specified Power Platform environment.

```sh
m365 pp pipeline list --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5
```

Retrieve the list of deployment pipelines as admin in the specified Power Platform environment.

```sh
m365 pp pipeline list --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5 --asAdmin
```

## Response

<Tabs>
<TabItem value="JSON">

```json
[
{
"name": "Sales Pipeline",
"deploymentpipelineid": "3ef59632-f087-ef11-8a69-7c1e521ba3ec",
"ownerid": "27f5bb85-6b7a-ef11-ac22-000d3a4e3cc8",
"statuscode": 1
}
]
```
</TabItem>

<TabItem value="Text">

```text
deploymentpipelineid: 3ef59632-f087-ef11-8a69-7c1e521ba3ec
name : Sales Pipeline
ownerid : 27f5bb85-6b7a-ef11-ac22-000d3a4e3cc8
statuscode : 1
```
</TabItem>

<TabItem value="CSV">

```csv
name,deploymentpipelineid,ownerid,statuscode
Sales Pipeline,3ef59632-f087-ef11-8a69-7c1e521ba3ec,27f5bb85-6b7a-ef11-ac22-000d3a4e3cc8,1
```
</TabItem>

<TabItem value="Markdown">

```md
# m365 pp pipeline list --environmentName Default-d87a7535-dd31-4437-bfe1-95340acd55c5

Date: 17/10/2024

## environmentName (default) (/providers/Microsoft.BusinessAppPlatform/environments/Default-e1dd4023-a656-480a-8a0e-c1b1eec51e1d)

Property | Value
---------|-------
name | Sales Pipeline
deploymentpipelineid | 3ef59632-f087-ef11-8a69-7c1e521ba3ec
ownerid | 27f5bb85-6b7a-ef11-ac22-000d3a4e3cc8
statuscode | 1
```

</TabItem>

</Tabs>
9 changes: 9 additions & 0 deletions docs/src/config/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,15 @@ const sidebars: SidebarsConfig = {
}
]
},
{
pipeline: [
{
type: 'doc',
label: 'pipeline list',
id: 'cmd/pp/pipeline/pipeline-list'
}
]
},
{
solution: [
{
Expand Down
1 change: 1 addition & 0 deletions src/m365/pp/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default {
GATEWAY_LIST: `${prefix} gateway list`,
MANAGEMENTAPP_ADD: `${prefix} managementapp add`,
MANAGEMENTAPP_LIST: `${prefix} managementapp list`,
PIPELINE_LIST: `${prefix} pipeline list`,
SOLUTION_GET: `${prefix} solution get`,
SOLUTION_LIST: `${prefix} solution list`,
SOLUTION_PUBLISH: `${prefix} solution publish`,
Expand Down
138 changes: 138 additions & 0 deletions src/m365/pp/commands/pipeline/pipeline-list.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import assert from "assert";
import commands from "../../commands.js";
import command from './pipeline-list.js';
import sinon from 'sinon';
import request from '../../../../request.js';
import { telemetry } from '../../../../telemetry.js';
import auth from '../../../../Auth.js';
import { Logger } from '../../../../cli/Logger.js';
import { pid } from '../../../../utils/pid.js';
import { session } from "../../../../utils/session.js";
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import { accessToken } from '../../../../utils/accessToken.js';
import { CommandError } from "../../../../Command.js";
import { powerPlatform } from "../../../../utils/powerPlatform.js";

describe(commands.PIPELINE_LIST, () => {
const environmentName = 'environmentName';
const mockPipelineListResponse: any = [
{
'_ownerid_value': 'owner1',
deploymentpipelineid: 'deploymentpipelineid1',
name: 'pipeline1',
statuscode: 'statuscode1'
}
];
const mockEnvironmentResponse = {
"id": `/providers/Microsoft.BusinessAppPlatform/environments/Default-Environment`,
"type": "Microsoft.BusinessAppPlatform/environments",
"location": "unitedstates",
"name": "Default-Environment",
"properties": {
"displayName": "contoso (default)",
"isDefault": true,
linkedEnvironmentMetadata: {
instanceApiUrl: 'https://contoso.crm.dynamics.com'
}
}
};

let log: string[];
let logger: Logger;

let loggerLogSpy: sinon.SinonSpy;

before(() => {
sinon.stub(auth, 'restoreAuth').resolves();
sinon.stub(telemetry, 'trackEvent').returns();
sinon.stub(pid, 'getProcessName').returns('');
sinon.stub(session, 'getId').returns('');
sinon.stub(accessToken, 'assertDelegatedAccessToken').returns();
auth.connection.active = true;
});

beforeEach(() => {
log = [];
logger = {
log: async (msg: string) => {
log.push(msg);
},
logRaw: async (msg: string) => {
log.push(msg);
},
logToStderr: async (msg: string) => {
log.push(msg);
}
};
loggerLogSpy = sinon.spy(logger, 'log');
});

afterEach(() => {
sinonUtil.restore([
request.get
]);
});

after(() => {
sinon.restore();
auth.connection.active = false;
});

it('has correct name', () => {
assert.strictEqual(commands.PIPELINE_LIST, command.name);
});

it('has a description', () => {
assert.notStrictEqual(command.description, null);
});

it('defines correct properties for the default output', () => {
assert.deepStrictEqual(command.defaultProperties(), ['name', 'deploymentpipelineid', '_ownerid_value', 'statuscode']);
});

it('retrieves pipelines in the specified Power Platform environment', async () => {
const getEnvironmentStub = await sinon.stub(powerPlatform as any, 'getDynamicsInstanceApiUrl').callsFake(() => Promise.resolve(mockEnvironmentResponse));
const getPipelineStub = sinon.stub(request, 'get').callsFake(async (opts) => {
if ((opts.url as string).indexOf('/api/data/v9.0/deploymentpipelines') > -1) {
return {
value: [
{
'_ownerid_value': 'owner1',
deploymentpipelineid: 'deploymentpipelineid1',
name: 'pipeline1',
statuscode: 'statuscode1'
}
]
};
}
throw new Error('Invalid request');
});

await command.action(logger, {
options: {
environmentName: environmentName,
asAdmin: false
}
});

assert(getEnvironmentStub.called);
assert(getPipelineStub.called);

assert(loggerLogSpy.calledWith(sinon.match(mockPipelineListResponse)));
});

it('correctly handles error when retrieving environment details or pipelines', async () => {
const errorMessage = 'An error has occurred';
sinon.stub(request, 'get').callsFake(async () => {
throw errorMessage;
});

await assert.rejects(command.action(logger, {
options: {
environmentName: environmentName,
asAdmin: false
}
}), new CommandError(errorMessage));
});

});
89 changes: 89 additions & 0 deletions src/m365/pp/commands/pipeline/pipeline-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@

import { Logger } from '../../../../cli/Logger.js';
import GlobalOptions from '../../../../GlobalOptions.js';
import request, { CliRequestOptions } from '../../../../request.js';
import { powerPlatform } from '../../../../utils/powerPlatform.js';
import PowerPlatformCommand from '../../../base/PowerPlatformCommand.js';
import commands from '../../commands.js';

interface Options extends GlobalOptions {
environmentName: string;
asAdmin?: boolean;
}

interface CommandArgs {
options: Options;
}

class PpPipelineListCommand extends PowerPlatformCommand {

constructor() {
super();
this.#initTelemetry();
this.#initOptions();
}

public get name(): string {
return commands.PIPELINE_LIST;
}

public get description(): string {
return 'Lists Microsoft Power Platform pipelines in the specified Power Platform environment.';
}

#initTelemetry(): void {
this.telemetry.push((args: CommandArgs) => {
Object.assign(this.telemetryProperties, {
asAdmin: !!args.options.asAdmin
});
});
}

#initOptions(): void {
this.options.unshift(
{
option: '-e, --environmentName <environmentName>'
},
{
option: '--asAdmin'
}
);
}

public defaultProperties(): string[] | undefined {
return ['name', 'deploymentpipelineid', '_ownerid_value', 'statuscode'];
}

public async commandAction(logger: Logger, args: any): Promise<void> {

try {
const dynamicsApiUrl = await powerPlatform.getDynamicsInstanceApiUrl(args.options.environmentName, args.options.asAdmin);

const pipelines = await this.listPipelines(dynamicsApiUrl);

await logger.log(pipelines);
}
catch (ex: any) {
this.handleRejectedODataJsonPromise(ex);
}

}

private async listPipelines(instanceUrl: string): Promise<any> {

const pipelineListRequestOptions: CliRequestOptions = {
url: `${instanceUrl}/api/data/v9.0/deploymentpipelines`,
headers: {
accept: 'application/json'
},
responseType: 'json'
};

const pipelines = await request.get<any>(pipelineListRequestOptions);

return pipelines.value;
}

}

export default new PpPipelineListCommand();
Loading