Skip to content

Commit

Permalink
Adds the 'external connection urltoitemresolver add' command. Closes p…
Browse files Browse the repository at this point in the history
  • Loading branch information
waldekmastykarz committed Jan 20, 2024
1 parent 8b1e3c4 commit 5ae9ccb
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const dictionary = [
'installed',
'is',
'issue',
'item',
'label',
'list',
'link',
Expand All @@ -75,6 +76,7 @@ const dictionary = [
'property',
'records',
'recycle',
'resolver',
'retention',
'role',
'room',
Expand All @@ -93,9 +95,12 @@ const dictionary = [
'table',
'teams',
'threat',
'to',
'todo',
'token',
'type',
'unit',
'url',
'user',
'web',
'webhook'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Global from '/docs/cmd/_global.mdx';

# external connection urltoitemresolver add

Adds a URL to item resolver to an external connection

## Usage

```sh
m365 external connection urltoitemresolver add [options]
```

## Options

```md definition-list
`-c, --externalConnectionId <externalConnectionId>`
: The ID of the external connection to which to add the resolver

`--baseUrls <baseUrls>`
: Comma-separated list of base URLs

`--urlPattern <urlPattern>`
: Regex pattern to match URLs

`-i, --itemId <itemId>`
: Pattern that matches captured matches from the URL with the external item ID

`-p, --priority <priority>`
: Integer that defines the resolution order
```
<Global />

## Examples

Adds a URL to item resolver to an existing connection

```sh
m365 external connection urltoitemresolver add --externalConnectionId "samplesolutiongallery" --baseUrls "https://adoption.microsoft.com" --urlPattern "/sample-solution-gallery/sample/(?<sampleId>[^/]+)" --itemId "{sampleId}" --priority 1
```

## Response

The command won't return a response on success.

## More information

- Microsoft Graph external connection activitySettings: [https://learn.microsoft.com/graph/api/resources/externalconnectors-activitysettings?view=graph-rest-1.0](https://learn.microsoft.com/graph/api/resources/externalconnectors-activitysettings?view=graph-rest-1.0)
5 changes: 5 additions & 0 deletions docs/src/config/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,11 @@ const sidebars: SidebarsConfig = {
type: 'doc',
label: 'connection schema add',
id: 'cmd/external/connection/connection-schema-add'
},
{
type: 'doc',
label: 'connection urltoitemresolver add',
id: 'cmd/external/connection/connection-urltoitemresolver-add'
}
]
},
Expand Down
1 change: 1 addition & 0 deletions src/m365/external/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default {
CONNECTION_LIST: `${prefix} connection list`,
CONNECTION_REMOVE: `${prefix} connection remove`,
CONNECTION_SCHEMA_ADD: `${prefix} connection schema add`,
CONNECTION_URLTOITEMRESOLVER_ADD: `${prefix} connection urltoitemresolver add`,
EXTERNALCONNECTION_ADD: `${searchPrefix} externalconnection add`,
EXTERNALCONNECTION_GET: `${searchPrefix} externalconnection get`,
EXTERNALCONNECTION_LIST: `${searchPrefix} externalconnection list`,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import assert from 'assert';
import sinon from 'sinon';
import auth from '../../../../Auth.js';
import { CommandError } from '../../../../Command.js';
import { Logger } from '../../../../cli/Logger.js';
import request from '../../../../request.js';
import { telemetry } from '../../../../telemetry.js';
import { pid } from '../../../../utils/pid.js';
import { session } from '../../../../utils/session.js';
import { sinonUtil } from '../../../../utils/sinonUtil.js';
import commands from '../../commands.js';
import command from './connection-urltoitemresolver-add.js';

describe(commands.CONNECTION_URLTOITEMRESOLVER_ADD, () => {
let logger: Logger;

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

beforeEach(() => {
logger = {
log: async () => { },
logRaw: async () => { },
logToStderr: async () => { }
};
(command as any).items = [];
});

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

after(() => {
sinon.restore();
auth.service.connected = false;
});

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

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

it('adds item to URL resolver to an existing external connection', async () => {
sinon.stub(request, 'patch').callsFake(async (opts: any) => {
if (opts.url === `https://graph.microsoft.com/v1.0/external/connections/conn`) {
return {};
}
throw 'Invalid request';
});
const options: any = {
externalConnectionId: 'conn',
baseUrls: 'https://contoso.com',
urlPattern: '/(?<id>.*)',
itemId: '{id}',
priority: 1
};
await command.action(logger, { options } as any);
});

it('correctly handles error', async () => {
sinon.stub(request, 'patch').callsFake(() => {
throw {
"error": {
"code": "Error",
"message": "An error has occurred",
"innerError": {
"request-id": "9b0df954-93b5-4de9-8b99-43c204a8aaf8",
"date": "2018-04-24T18:56:48"
}
}
};
});

const options: any = {
externalConnectionId: 'conn',
baseUrls: 'https://contoso.com',
urlPattern: '/(?<id>.*)',
itemId: '{id}',
priority: 1
};

await assert.rejects(command.action(logger, { options } as any),
new CommandError(`An error has occurred`));
});

it('supports specifying connection ID', () => {
const containsOption = !!command.options
.find(o => o.option.indexOf('--externalConnectionId') > -1);
assert(containsOption);
});

it('supports specifying base URLs', () => {
const containsOption = !!command.options
.find(o => o.option.indexOf('--baseUrls') > -1);
assert(containsOption);
});

it('supports specifying URL patterns', () => {
const containsOption = !!command.options
.find(o => o.option.indexOf('--urlPattern') > -1);
assert(containsOption);
});

it('supports specifying item ID', () => {
const containsOption = !!command.options
.find(o => o.option.indexOf('--itemId') > -1);
assert(containsOption);
});

it('supports specifying priority', () => {
const containsOption = !!command.options
.find(o => o.option.indexOf('--priority') > -1);
assert(containsOption);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { ExternalConnectors } from '@microsoft/microsoft-graph-types/microsoft-graph';
import GlobalOptions from '../../../../GlobalOptions.js';
import { Logger } from '../../../../cli/Logger.js';
import request, { CliRequestOptions } from '../../../../request.js';
import GraphCommand from '../../../base/GraphCommand.js';
import commands from '../../commands.js';

interface CommandArgs {
options: Options;
}

interface Options extends GlobalOptions {
externalConnectionId: string;
baseUrls: string;
urlPattern: string;
itemId: string;
priority: number;
}

class ExternalConnectionUrlToItemResolverAddCommand extends GraphCommand {
public get name(): string {
return commands.CONNECTION_URLTOITEMRESOLVER_ADD;
}

public get description(): string {
return 'Adds a URL to item resolver to an external connection';
}

constructor() {
super();

this.#initOptions();
}

#initOptions(): void {
this.options.unshift(
{
option: '-c, --externalConnectionId <externalConnectionId>'
},
{
option: '--baseUrls <baseUrls>'
},
{
option: '--urlPattern <urlPattern>'
},
{
option: '-i, --itemId <itemId>'
},
{
option: '-p, --priority <priority>'
}
);
}

public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
const baseUrls: string[] = args.options.baseUrls.split(',').map(b => b.trim());

const itemIdResolver: ExternalConnectors.ItemIdResolver = {
itemId: args.options.itemId,
priority: args.options.priority,
urlMatchInfo: {
baseUrls: baseUrls,
urlPattern: args.options.urlPattern
}
};
// not a part of the type definition, but required by the API
(itemIdResolver as any)['@odata.type'] = '#microsoft.graph.externalConnectors.itemIdResolver';

const requestOptions: CliRequestOptions = {
url: `${this.resource}/v1.0/external/connections/${args.options.externalConnectionId}`,
headers: {
accept: 'application/json;odata.metadata=none',
'content-type': 'application/json'
},
responseType: 'json',
data: {
activitySettings: {
urlToItemResolvers: [itemIdResolver]
}
} as ExternalConnectors.ExternalConnection
};

try {
await request.patch(requestOptions);
}
catch (err: any) {
this.handleRejectedODataJsonPromise(err);
}
}
}

export default new ExternalConnectionUrlToItemResolverAddCommand();

0 comments on commit 5ae9ccb

Please sign in to comment.