Skip to content

Commit

Permalink
Adds 'pp aibuildermodel list' command. Closes #4174
Browse files Browse the repository at this point in the history
  • Loading branch information
nicodecleyre authored and milanholemans committed Jan 1, 2023
1 parent 0f4e62e commit 8e72dc4
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Expand Up @@ -5,11 +5,13 @@ const dictionary = [
'activation',
'activations',
'adaptive',
'ai',
'app',
'apply',
'approve',
'assets',
'bin',
'builder',
'catalog',
'checklist',
'client',
Expand Down Expand Up @@ -42,6 +44,7 @@ const dictionary = [
'management',
'member',
'messaging',
'model',
'news',
'oauth2',
'office365',
Expand Down
95 changes: 95 additions & 0 deletions docs/docs/cmd/pp/aibuildermodel/aibuildermodel-list.md
@@ -0,0 +1,95 @@
# pp aibuildermodel list

List available AI builder models in the specified Power Platform environment

## Usage

```sh
pp aibuildermodel list [options]
```

## Options

`-e, --environment <environment>`
: The name of the environment

`--asAdmin`
: Run the command as admin for environments you do not have explicitly assigned permissions to

--8<-- "docs/cmd/_global.md"

## Examples

List all AI Builder models in a specific environment

```sh
m365 pp aibuildermodel list --environment "Default-d87a7535-dd31-4437-bfe1-95340acd55c5"
```

List all AI Builder models in a specific environment as admin

```sh
m365 pp aibuildermodel list --environment "Default-d87a7535-dd31-4437-bfe1-95340acd55c5" --asAdmin
```

## Response

=== "JSON"

```json
[
{
"statecode": 0,
"_msdyn_templateid_value": "10707e4e-1d56-e911-8194-000d3a6cd5a5",
"msdyn_modelcreationcontext": "{}",
"createdon": "2022-11-29T11:58:45Z",
"_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"modifiedon": "2022-11-29T11:58:45Z",
"msdyn_sharewithorganizationoncreate": false,
"msdyn_aimodelidunique": "b0328b67-47e2-4202-8189-e617ec9a88bd",
"solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238",
"ismanaged": false,
"versionnumber": 1458121,
"msdyn_name": "Document Processing 11/29/2022, 12:58:43 PM",
"introducedversion": "1.0",
"statuscode": 0,
"_modifiedby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"overwritetime": "1900-01-01T00:00:00Z",
"componentstate": 0,
"_createdby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"_owningbusinessunit_value": "6da087c1-1c4d-ed11-bba1-000d3a2caf7f",
"_owninguser_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"msdyn_aimodelid": "08ffffbe-ec1c-4e64-b64b-dd1db926c613",
"_msdyn_activerunconfigurationid_value": null,
"overriddencreatedon": null,
"_msdyn_retrainworkflowid_value": null,
"importsequencenumber": null,
"_msdyn_scheduleinferenceworkflowid_value": null,
"_modifiedonbehalfby_value": null,
"utcconversiontimezonecode": null,
"_createdonbehalfby_value": null,
"_owningteam_value": null,
"timezoneruleversionnumber": null,
"iscustomizable": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "iscustomizableanddeletable"
}
}
]
```

=== "Text"

```text
createdon modifiedon msdyn_aimodelid msdyn_name
-------------------- -------------------- ------------------------------------ -------------------------------------------
2022-10-25T14:44:48Z 2022-10-25T14:44:48Z 08ffffbe-ec1c-4e64-b64b-dd1db926c613 Document Processing 11/29/2022, 12:58:43 PM
```

=== "CSV"

```csv
msdyn_name,msdyn_aimodelid,createdon,modifiedon
"Document Processing 11/29/2022, 12:58:43 PM",08ffffbe-ec1c-4e64-b64b-dd1db926c613,2022-11-29T11:58:45Z,2022-11-29T11:58:45Z
```
2 changes: 2 additions & 0 deletions docs/mkdocs.yml
Expand Up @@ -263,6 +263,8 @@ nav:
- run list: cmd/flow/run/run-list.md
- run resubmit: cmd/flow/run/run-resubmit.md
- Power Platform (pp):
- aibuildermodel:
- aibuildermodel list: cmd/pp/aibuildermodel/aibuildermodel-list.md
- card:
- card clone: cmd/pp/card/card-clone.md
- card get: cmd/pp/card/card-get.md
Expand Down
1 change: 1 addition & 0 deletions src/m365/pp/commands.ts
@@ -1,6 +1,7 @@
const prefix: string = 'pp';

export default {
AIBUILDERMODEL_LIST: `${prefix} aibuildermodel list`,
CARD_CLONE: `${prefix} card clone`,
CARD_GET: `${prefix} card get`,
CARD_LIST: `${prefix} card list`,
Expand Down
161 changes: 161 additions & 0 deletions src/m365/pp/commands/aibuildermodel/aibuildermodel-list.spec.ts
@@ -0,0 +1,161 @@
import * as assert from 'assert';
import * as sinon from 'sinon';
import { telemetry } from '../../../../telemetry';
import auth from '../../../../Auth';
import { Logger } from '../../../../cli/Logger';
import Command, { CommandError } from '../../../../Command';
import request from '../../../../request';
import { pid } from '../../../../utils/pid';
import { sinonUtil } from '../../../../utils/sinonUtil';
import commands from '../../commands';
import { powerPlatform } from '../../../../utils/powerPlatform';
const command: Command = require('./aibuildermodel-list');

describe(commands.AIBUILDERMODEL_LIST, () => {
//#region Mocked Responses
const envUrl = "https://contoso-dev.api.crm4.dynamics.com";
const validEnvironment = "4be50206-9576-4237-8b17-38d8aadfaa36";
const modelsResponse: any = {
"value": [
{
"@odata.etag": "W/\"1458121\"",
"statecode": 0,
"_msdyn_templateid_value": "10707e4e-1d56-e911-8194-000d3a6cd5a5",
"msdyn_modelcreationcontext": "{}",
"createdon": "2022-11-29T11:58:45Z",
"_ownerid_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"modifiedon": "2022-11-29T11:58:45Z",
"msdyn_sharewithorganizationoncreate": false,
"msdyn_aimodelidunique": "b0328b67-47e2-4202-8189-e617ec9a88bd",
"solutionid": "fd140aae-4df4-11dd-bd17-0019b9312238",
"ismanaged": false,
"versionnumber": 1458121,
"msdyn_name": "Document Processing 11/29/2022, 12:58:43 PM",
"introducedversion": "1.0",
"statuscode": 0,
"_modifiedby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"overwritetime": "1900-01-01T00:00:00Z",
"componentstate": 0,
"_createdby_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"_owningbusinessunit_value": "6da087c1-1c4d-ed11-bba1-000d3a2caf7f",
"_owninguser_value": "5fa787c1-1c4d-ed11-bba1-000d3a2caf7f",
"msdyn_aimodelid": "08ffffbe-ec1c-4e64-b64b-dd1db926c613",
"_msdyn_activerunconfigurationid_value": null,
"overriddencreatedon": null,
"_msdyn_retrainworkflowid_value": null,
"importsequencenumber": null,
"_msdyn_scheduleinferenceworkflowid_value": null,
"_modifiedonbehalfby_value": null,
"utcconversiontimezonecode": null,
"_createdonbehalfby_value": null,
"_owningteam_value": null,
"timezoneruleversionnumber": null,
"iscustomizable": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "iscustomizableanddeletable"
}
}
]
};
//#endregion

let log: string[];
let logger: Logger;
let loggerLogSpy: sinon.SinonSpy;

before(() => {
sinon.stub(auth, 'restoreAuth').callsFake(() => Promise.resolve());
sinon.stub(telemetry, 'trackEvent').callsFake(() => { });
sinon.stub(pid, 'getProcessName').callsFake(() => '');
auth.service.connected = true;
});

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

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

after(() => {
sinonUtil.restore([
auth.restoreAuth,
telemetry.trackEvent,
pid.getProcessName
]);
auth.service.connected = false;
});

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

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

it('defines correct properties for the default output', () => {
assert.deepStrictEqual(command.defaultProperties(), ['msdyn_name', 'msdyn_aimodelid', 'createdon', 'modifiedon']);
});

it('retrieves AI Builder models', async () => {
sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl);

sinon.stub(request, 'get').callsFake(async opts => {
if ((opts.url === `https://contoso-dev.api.crm4.dynamics.com/api/data/v9.0/msdyn_aimodels?$filter=iscustomizable/Value eq true`)) {
if (opts.headers &&
opts.headers.accept &&
(opts.headers.accept as string).indexOf('application/json') === 0) {
return modelsResponse;
}
}

throw 'Invalid request';
});

await command.action(logger, { options: { verbose: true, environment: validEnvironment } });
assert(loggerLogSpy.calledWith(modelsResponse.value));

});

it('correctly handles API OData error', async () => {
sinon.stub(powerPlatform, 'getDynamicsInstanceApiUrl').callsFake(async () => envUrl);

sinon.stub(request, 'get').callsFake(async (opts) => {
if (opts.url === `https://contoso-dev.api.crm4.dynamics.com/api/data/v9.0/msdyn_aimodels?$filter=iscustomizable/Value eq true`) {
if ((opts.headers?.accept as string)?.indexOf('application/json') === 0) {
throw {
error: {
'odata.error': {
code: '-1, InvalidOperationException',
message: {
value: `Resource '' does not exist or one of its queried reference-property objects are not present`
}
}
}
};
}
}
});

await assert.rejects(command.action(logger, { options: { environment: validEnvironment } } as any),
new CommandError(`Resource '' does not exist or one of its queried reference-property objects are not present`));
});
});
73 changes: 73 additions & 0 deletions src/m365/pp/commands/aibuildermodel/aibuildermodel-list.ts
@@ -0,0 +1,73 @@
import { Logger } from '../../../../cli/Logger';
import GlobalOptions from '../../../../GlobalOptions';
import { odata } from '../../../../utils/odata';
import { powerPlatform } from '../../../../utils/powerPlatform';
import PowerPlatformCommand from '../../../base/PowerPlatformCommand';
import commands from '../../commands';

interface CommandArgs {
options: Options;
}

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

class PpAiBuilderModelListCommand extends PowerPlatformCommand {
public get name(): string {
return commands.AIBUILDERMODEL_LIST;
}

public get description(): string {
return 'List available AI builder models in the specified Power Platform environment.';
}

public defaultProperties(): string[] | undefined {
return ['msdyn_name', 'msdyn_aimodelid', 'createdon', 'modifiedon'];
}

constructor() {
super();

this.#initTelemetry();
this.#initOptions();
}

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

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

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
if (this.verbose) {
logger.logToStderr(`Retrieving available AI Builder models`);
}

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

const aimodels = await odata.getAllItems<any>(`${dynamicsApiUrl}/api/data/v9.0/msdyn_aimodels?$filter=iscustomizable/Value eq true`);
logger.log(aimodels);
}
catch (err: any) {
this.handleRejectedODataJsonPromise(err);
}
}
}

module.exports = new PpAiBuilderModelListCommand();

0 comments on commit 8e72dc4

Please sign in to comment.