Skip to content

Commit c23b397

Browse files
committed
Adds command 'teams callrecord list'. Closes #6737
1 parent a6007fc commit c23b397

File tree

109 files changed

+980
-126
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+980
-126
lines changed
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import Global from '/docs/cmd/_global.mdx';
2+
import Tabs from '@theme/Tabs';
3+
import TabItem from '@theme/TabItem';
4+
5+
# teams callrecord list
6+
7+
Lists all Teams calls within the tenant
8+
9+
## Usage
10+
11+
```sh
12+
m365 teams callrecord list [options]
13+
```
14+
15+
## Options
16+
17+
```md definition-list
18+
`--userId [userId]`
19+
: Filters call records to only include those where the user participated. Use the Entra user ID of the user. Specify either `userId` or `userName` but not both.
20+
21+
`--userName [userName]`
22+
: Filters call records to only include those where the user participated. Use the UPN of the user. Specify either `userId` or `userName` but not both.
23+
24+
`--startDateTime [startDateTime]`
25+
: Only show calls that started after this time. Defaults to 30 days ago.
26+
27+
`--endDateTime [endDateTime]`
28+
: Only show calls that started before this time (exclusive). Defaults to now.
29+
```
30+
31+
<Global />
32+
33+
## Remarks
34+
35+
:::info
36+
37+
This command only supports application permissions. Use scope `CallRecords.Read.All`.
38+
39+
:::
40+
41+
:::note
42+
43+
It is only possible to retrieve call records from the past 30 days.
44+
45+
:::
46+
47+
## Examples
48+
49+
Get all call records from the past 30 days.
50+
51+
```sh
52+
m365 teams callrecord list
53+
```
54+
55+
Get all call records where a specific user participated in.
56+
57+
```sh
58+
m365 teams callrecord list --userName "john.doe@contoso.com"
59+
```
60+
61+
Get all call records that started within a specific timeframe.
62+
63+
```sh
64+
m365 teams callrecord list --startDateTime "2025-05-1T00:00:00Z" --endDateTime "2025-05-14T00:00:00Z"
65+
```
66+
67+
## Response
68+
69+
<Tabs>
70+
<TabItem value="JSON">
71+
72+
```json
73+
[
74+
{
75+
"id": "145ae53b-7781-47c4-a5f7-b0e043012624",
76+
"version": 1,
77+
"type": "peerToPeer",
78+
"modalities": [
79+
"audio"
80+
],
81+
"lastModifiedDateTime": "2025-05-29T17:41:33.5066667Z",
82+
"startDateTime": "2025-05-29T17:27:20.428943Z",
83+
"endDateTime": "2025-05-29T17:37:20.428943Z",
84+
"joinWebUrl": "",
85+
"organizer": {
86+
"acsUser": null,
87+
"spoolUser": null,
88+
"phone": null,
89+
"guest": null,
90+
"encrypted": null,
91+
"onPremises": null,
92+
"acsApplicationInstance": null,
93+
"spoolApplicationInstance": null,
94+
"applicationInstance": null,
95+
"application": null,
96+
"device": null,
97+
"user": {
98+
"id": "b51efc8b-0adf-486c-ba54-b239ffa7c1f9",
99+
"displayName": "John Doe",
100+
"tenantId": "de5e90f2-9a07-4ed1-b903-f801afa7b1fd"
101+
}
102+
},
103+
"participants": [],
104+
"organizer_v2": {
105+
"id": "b51efc8b-0adf-486c-ba54-b239ffa7c1f9",
106+
"identity": {
107+
"endpointType": null,
108+
"acsUser": null,
109+
"spoolUser": null,
110+
"phone": null,
111+
"guest": null,
112+
"encrypted": null,
113+
"onPremises": null,
114+
"acsApplicationInstance": null,
115+
"spoolApplicationInstance": null,
116+
"applicationInstance": null,
117+
"application": null,
118+
"device": null,
119+
"azureCommunicationServicesUser": null,
120+
"assertedIdentity": null,
121+
"user": {
122+
"id": "b51efc8b-0adf-486c-ba54-b239ffa7c1f9",
123+
"displayName": "John Doe",
124+
"tenantId": "de5e90f2-9a07-4ed1-b903-f801afa7b1fd",
125+
"userPrincipalName": "john.doe@contoso.com"
126+
}
127+
},
128+
"administrativeUnitInfos": []
129+
}
130+
}
131+
]
132+
```
133+
134+
</TabItem>
135+
<TabItem value="Text">
136+
137+
```text
138+
id type startDateTime endDateTime
139+
------------------------------------ ---------- ---------------------------- ----------------------------
140+
145ae53b-7781-47c4-a5f7-b0e043012624 peerToPeer 2025-05-29T17:27:20.428943Z 2025-05-29T17:37:20.428943Z
141+
```
142+
143+
</TabItem>
144+
<TabItem value="CSV">
145+
146+
```csv
147+
id,version,type,lastModifiedDateTime,startDateTime,endDateTime,joinWebUrl
148+
145ae53b-7781-47c4-a5f7-b0e043012624,1,peerToPeer,2025-05-29T17:41:33.5066667Z,2025-05-29T17:27:20.428943Z,2025-05-29T17:37:20.428943Z,
149+
```
150+
151+
</TabItem>
152+
<TabItem value="Markdown">
153+
154+
```md
155+
# teams callrecord list
156+
157+
Date: 03/06/2025
158+
159+
## 145ae53b-7781-47c4-a5f7-b0e043012624
160+
161+
Property | Value
162+
---------|-------
163+
id | 145ae53b-7781-47c4-a5f7-b0e043012624
164+
version | 1
165+
type | peerToPeer
166+
lastModifiedDateTime | 2025-05-29T17:41:33.5066667Z
167+
startDateTime | 2025-05-29T17:27:20.428943Z
168+
endDateTime | 2025-05-29T17:37:20.428943Z
169+
joinWebUrl |
170+
```
171+
172+
</TabItem>
173+
</Tabs>

docs/src/config/sidebars.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4317,6 +4317,15 @@ const sidebars: SidebarsConfig = {
43174317
}
43184318
]
43194319
},
4320+
{
4321+
callrecord: [
4322+
{
4323+
type: 'doc',
4324+
label: 'callrecord list',
4325+
id: 'cmd/teams/callrecord/callrecord-list'
4326+
}
4327+
]
4328+
},
43204329
{
43214330
channel: [
43224331
{
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import assert from 'assert';
2+
import sinon from 'sinon';
3+
import auth from '../../Auth.js';
4+
import { telemetry } from '../../telemetry.js';
5+
import GraphApplicationCommand from './GraphApplicationCommand.js';
6+
import { accessToken } from '../../utils/accessToken.js';
7+
import { CommandError } from '../../Command.js';
8+
9+
class MockCommand extends GraphApplicationCommand {
10+
public get name(): string {
11+
return 'mock';
12+
}
13+
14+
public get description(): string {
15+
return 'Mock command';
16+
}
17+
18+
public async commandAction(): Promise<void> {
19+
}
20+
21+
public commandHelp(): void {
22+
}
23+
}
24+
25+
describe('GraphApplicationCommand', () => {
26+
const cmd = new MockCommand();
27+
28+
before(() => {
29+
sinon.stub(telemetry, 'trackEvent').resolves();
30+
auth.connection.active = true;
31+
auth.connection.accessTokens[auth.defaultResource] = {
32+
expiresOn: 'abc',
33+
accessToken: 'abc'
34+
};
35+
});
36+
37+
beforeEach(() => {
38+
auth.connection.active = true;
39+
});
40+
41+
after(() => {
42+
sinon.restore();
43+
auth.connection.active = false;
44+
auth.connection.accessTokens = {};
45+
});
46+
47+
it(`doesn't throw error when not connected`, async () => {
48+
auth.connection.active = false;
49+
await (cmd as any).initAction({ options: {} }, {});
50+
});
51+
52+
it('throws error when using delegated permissions', async () => {
53+
sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(false);
54+
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command requires application-only permissions.'));
55+
});
56+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import auth from '../../Auth.js';
2+
import { CommandArgs } from '../../Command.js';
3+
import { Logger } from '../../cli/Logger.js';
4+
import { accessToken } from '../../utils/accessToken.js';
5+
import GraphCommand from './GraphCommand.js';
6+
7+
/**
8+
* This command class is for application-only Graph commands.
9+
*/
10+
export default abstract class GraphApplicationCommand extends GraphCommand {
11+
protected async initAction(args: CommandArgs, logger: Logger): Promise<void> {
12+
await super.initAction(args, logger);
13+
14+
if (!auth.connection.active) {
15+
// we fail no login in the base command command class
16+
return;
17+
}
18+
19+
accessToken.assertAccessTokenType('application');
20+
}
21+
}

src/m365/base/DelegatedGraphCommand.spec.ts renamed to src/m365/base/GraphDelegatedCommand.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import assert from 'assert';
22
import sinon from 'sinon';
33
import auth from '../../Auth.js';
44
import { telemetry } from '../../telemetry.js';
5-
import DelegatedGraphCommand from './DelegatedGraphCommand.js';
5+
import GraphDelegatedCommand from './GraphDelegatedCommand.js';
66
import { accessToken } from '../../utils/accessToken.js';
77
import { CommandError } from '../../Command.js';
88

9-
class MockCommand extends DelegatedGraphCommand {
9+
class MockCommand extends GraphDelegatedCommand {
1010
public get name(): string {
1111
return 'mock';
1212
}
@@ -22,7 +22,7 @@ class MockCommand extends DelegatedGraphCommand {
2222
}
2323
}
2424

25-
describe('DelegatedGraphCommand', () => {
25+
describe('GraphDelegatedCommand', () => {
2626
const cmd = new MockCommand();
2727

2828
before(() => {
@@ -51,6 +51,6 @@ describe('DelegatedGraphCommand', () => {
5151

5252
it('throws error when using application-only permissions', async () => {
5353
sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true);
54-
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command does not support application-only permissions.'));
54+
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command requires delegated permissions.'));
5555
});
5656
});

src/m365/base/DelegatedGraphCommand.ts renamed to src/m365/base/GraphDelegatedCommand.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { accessToken } from '../../utils/accessToken.js';
55
import GraphCommand from './GraphCommand.js';
66

77
/**
8-
* This command class is for delegated-only Graph commands.
8+
* This command class is for delegated Graph commands.
99
*/
10-
export default abstract class DelegatedGraphCommand extends GraphCommand {
10+
export default abstract class GraphDelegatedCommand extends GraphCommand {
1111
protected async initAction(args: CommandArgs, logger: Logger): Promise<void> {
1212
await super.initAction(args, logger);
1313

@@ -16,6 +16,6 @@ export default abstract class DelegatedGraphCommand extends GraphCommand {
1616
return;
1717
}
1818

19-
accessToken.assertDelegatedAccessToken();
19+
accessToken.assertAccessTokenType('delegated');
2020
}
2121
}

src/m365/base/PowerAppsCommand.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,6 @@ describe('PowerAppsCommand', () => {
9090
sinonUtil.restore(accessToken.isAppOnlyAccessToken);
9191
sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true);
9292
auth.connection.cloudType = CloudType.Public;
93-
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command does not support application-only permissions.'));
93+
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command requires delegated permissions.'));
9494
});
9595
});

src/m365/base/PowerAppsCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export default abstract class PowerAppsCommand extends Command {
2020
throw new CommandError(`Power Apps commands only support the public cloud at the moment. We'll add support for other clouds in the future. Sorry for the inconvenience.`);
2121
}
2222

23-
accessToken.assertDelegatedAccessToken();
23+
accessToken.assertAccessTokenType('delegated');
2424
}
2525
}

src/m365/base/PowerAutomateCommand.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,6 @@ describe('PowerAutomateCommand', () => {
8989
sinonUtil.restore(accessToken.isAppOnlyAccessToken);
9090
sinon.stub(accessToken, 'isAppOnlyAccessToken').returns(true);
9191
auth.connection.cloudType = CloudType.Public;
92-
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command does not support application-only permissions.'));
92+
await assert.rejects(() => (cmd as any).initAction({ options: {} }, {}), new CommandError('This command requires delegated permissions.'));
9393
});
9494
});

src/m365/base/PowerAutomateCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ export default abstract class PowerAutomateCommand extends Command {
2020
throw new CommandError(`Power Automate commands only support the public cloud at the moment. We'll add support for other clouds in the future. Sorry for the inconvenience.`);
2121
}
2222

23-
accessToken.assertDelegatedAccessToken();
23+
accessToken.assertAccessTokenType('delegated');
2424
}
2525
}

0 commit comments

Comments
 (0)