Skip to content
Open
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
31 changes: 25 additions & 6 deletions packages/api/src/clients/conversation/activity.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Client } from '@microsoft/teams.common';

import { AGENTIC_IDENTITY_EXTENSION } from '../auth-provider-interceptor';

import { ConversationActivityClient } from './activity';

describe('ConversationActivityClient', () => {
Expand All @@ -16,7 +18,7 @@ describe('ConversationActivityClient', () => {
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities', {
type: 'message',
text: 'hi',
});
}, {});
});

it('should use client options', async () => {
Expand All @@ -31,7 +33,7 @@ describe('ConversationActivityClient', () => {
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities', {
type: 'message',
text: 'hi',
});
}, {});
});

it('should create', async () => {
Expand All @@ -46,7 +48,24 @@ describe('ConversationActivityClient', () => {
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities', {
type: 'message',
text: 'hi',
}, {});
});

it('should pass serviceUrl and agentic identity options', async () => {
const client = new ConversationActivityClient('https://default.service');
const spy = jest.spyOn(client.http, 'post').mockResolvedValueOnce({});
const agenticIdentity = { agenticAppId: 'agent-app', agenticUserId: 'agent-user' };

await client.create('1', { type: 'message', text: 'hi' }, {
serviceUrl: 'https://override.service/',
agenticIdentity,
});

expect(spy).toHaveBeenCalledWith(
'https://override.service/v3/conversations/1/activities',
{ type: 'message', text: 'hi' },
{ extensions: { [AGENTIC_IDENTITY_EXTENSION]: agenticIdentity } }
);
});

it('should update', async () => {
Expand All @@ -61,7 +80,7 @@ describe('ConversationActivityClient', () => {
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities/2', {
type: 'message',
text: 'hi',
});
}, {});
});

it('should reply', async () => {
Expand All @@ -77,21 +96,21 @@ describe('ConversationActivityClient', () => {
type: 'message',
text: 'hi',
replyToId: '2',
});
}, {});
});

it('should delete', async () => {
const client = new ConversationActivityClient('');
const spy = jest.spyOn(client.http, 'delete').mockResolvedValueOnce({});
await client.delete('1', '2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities/2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities/2', {});
});

it('should get members', async () => {
const client = new ConversationActivityClient('');
const spy = jest.spyOn(client.http, 'get').mockResolvedValueOnce({ data: [] });
await client.getMembers('1', '2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities/2/members');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/activities/2/members', {});
});

it('should resolve objectId to aadObjectId in getMembers', async () => {
Expand Down
62 changes: 25 additions & 37 deletions packages/api/src/clients/conversation/activity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { Activity } from '../../activities';
import { resolveAadObjectId, Resource, TeamsChannelAccount } from '../../models';
import { ApiClientSettings, mergeApiClientSettings } from '../api-client-settings';
import { agenticIdentityExtension, RequestOptions, resolveServiceUrl } from '../request-options';

export type ActivityParams = Pick<Activity, 'type'> & Partial<Activity>;

Expand Down Expand Up @@ -35,65 +36,52 @@ export class ConversationActivityClient {
this._apiClientSettings = mergeApiClientSettings(apiClientSettings);
}

async create(conversationId: string, params: ActivityParams) {
const res = await this.http.post<Resource>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities`,
params
);
async create(conversationId: string, params: ActivityParams, options?: RequestOptions) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities`;
const res = await this.http.post<Resource>(url, params, agenticIdentityExtension(options));
return res.data;
Comment thread
heyitsaamir marked this conversation as resolved.
}

async update(conversationId: string, id: string, params: ActivityParams) {
const res = await this.http.put<Resource>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}`,
params
);
async update(conversationId: string, id: string, params: ActivityParams, options?: RequestOptions) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}`;
const res = await this.http.put<Resource>(url, params, agenticIdentityExtension(options));
return res.data;
}

async reply(conversationId: string, id: string, params: ActivityParams) {
async reply(conversationId: string, id: string, params: ActivityParams, options?: RequestOptions) {
params.replyToId = id;
const res = await this.http.post<Resource>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}`,
params
);
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}`;
const res = await this.http.post<Resource>(url, params, agenticIdentityExtension(options));
return res.data;
}

async delete(conversationId: string, id: string) {
const res = await this.http.delete<void>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}`
);
async delete(conversationId: string, id: string, options?: RequestOptions) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}`;
const res = await this.http.delete<void>(url, agenticIdentityExtension(options));
return res.data;
}

async getMembers(conversationId: string, id: string): Promise<TeamsChannelAccount[]> {
const res = await this.http.get<TeamsChannelAccount[]>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}/members`
);
async getMembers(conversationId: string, id: string, options?: RequestOptions): Promise<TeamsChannelAccount[]> {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}/members`;
const res = await this.http.get<TeamsChannelAccount[]>(url, agenticIdentityExtension(options));
return (res.data ?? []).map(resolveAadObjectId);
}
Comment thread
heyitsaamir marked this conversation as resolved.

async createTargeted(conversationId: string, params: ActivityParams) {
const res = await this.http.post<Resource>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities?isTargetedActivity=true`,
params
);
async createTargeted(conversationId: string, params: ActivityParams, options?: RequestOptions<'serviceUrl'>) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in PY, create_targeted accepts agentic_identity. Is this difference intentional?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities?isTargetedActivity=true`;
const res = await this.http.post<Resource>(url, params);
return res.data;
}

async updateTargeted(conversationId: string, id: string, params: ActivityParams) {
const res = await this.http.put<Resource>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}?isTargetedActivity=true`,
params
);
async updateTargeted(conversationId: string, id: string, params: ActivityParams, options?: RequestOptions<'serviceUrl'>) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}?isTargetedActivity=true`;
const res = await this.http.put<Resource>(url, params);
return res.data;
}

async deleteTargeted(conversationId: string, id: string) {
const res = await this.http.delete<void>(
`${this.serviceUrl}/v3/conversations/${conversationId}/activities/${id}?isTargetedActivity=true`
);
async deleteTargeted(conversationId: string, id: string, options?: RequestOptions<'serviceUrl'>) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/activities/${id}?isTargetedActivity=true`;
const res = await this.http.delete<void>(url);
return res.data;
}
}
46 changes: 25 additions & 21 deletions packages/api/src/clients/conversation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { Account, Conversation, ConversationResource } from '../../models';

import { ApiClientSettings, mergeApiClientSettings } from '../api-client-settings';
import { agenticIdentityExtension, RequestOptions, resolveServiceUrl } from '../request-options';

import { ActivityParams, ConversationActivityClient } from './activity';
import { ConversationMemberClient } from './member';
Expand Down Expand Up @@ -85,30 +86,35 @@ export class ConversationClient {

activities(conversationId: string) {
return {
create: (params: ActivityParams) => this._activities.create(conversationId, params),
update: (id: string, params: ActivityParams) =>
this._activities.update(conversationId, id, params),
reply: (id: string, params: ActivityParams) =>
this._activities.reply(conversationId, id, params),
delete: (id: string) => this._activities.delete(conversationId, id),
members: (activityId: string) => this._activities.getMembers(conversationId, activityId),
createTargeted: (params: ActivityParams) => this._activities.createTargeted(conversationId, params),
updateTargeted: (id: string, params: ActivityParams) =>
this._activities.updateTargeted(conversationId, id, params),
deleteTargeted: (id: string) => this._activities.deleteTargeted(conversationId, id),
create: (params: ActivityParams, options?: RequestOptions) =>
this._activities.create(conversationId, params, options),
update: (id: string, params: ActivityParams, options?: RequestOptions) =>
this._activities.update(conversationId, id, params, options),
reply: (id: string, params: ActivityParams, options?: RequestOptions) =>
this._activities.reply(conversationId, id, params, options),
delete: (id: string, options?: RequestOptions) =>
this._activities.delete(conversationId, id, options),
members: (activityId: string, options?: RequestOptions) =>
this._activities.getMembers(conversationId, activityId, options),
createTargeted: (params: ActivityParams, options?: RequestOptions<'serviceUrl'>) =>
this._activities.createTargeted(conversationId, params, options),
updateTargeted: (id: string, params: ActivityParams, options?: RequestOptions<'serviceUrl'>) =>
this._activities.updateTargeted(conversationId, id, params, options),
deleteTargeted: (id: string, options?: RequestOptions<'serviceUrl'>) =>
this._activities.deleteTargeted(conversationId, id, options),
};
}

members(conversationId: string) {
return {
get: () => this._members.get(conversationId),
getById: (id: string) => this._members.getById(conversationId, id),
getPaged: (pageSize?: number, continuationToken?: string) =>
this._members.getPaged(conversationId, pageSize, continuationToken),
get: (options?: RequestOptions) => this._members.get(conversationId, options),
getById: (id: string, options?: RequestOptions) => this._members.getById(conversationId, id, options),
getPaged: (pageSize?: number, continuationToken?: string, options?: RequestOptions) =>
this._members.getPaged(conversationId, pageSize, continuationToken, options),
/**
* @deprecated This will be removed by end of summer 2026.
*/
delete: (id: string) => this._members.delete(conversationId, id),
delete: (id: string, options?: RequestOptions) => this._members.delete(conversationId, id, options),
};
}

Expand All @@ -123,11 +129,9 @@ export class ConversationClient {
return res.data;
}

async create(params: CreateConversationParams) {
const res = await this.http.post<ConversationResource>(
`${this.serviceUrl}/v3/conversations`,
params
);
async create(params: CreateConversationParams, options?: RequestOptions) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations`;
const res = await this.http.post<ConversationResource>(url, params, agenticIdentityExtension(options));
return res.data;
}
}
Expand Down
30 changes: 13 additions & 17 deletions packages/api/src/clients/conversation/member.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {

import { PagedMembersResult, resolveAadObjectId, TeamsChannelAccount } from '../../models';
import { ApiClientSettings, mergeApiClientSettings } from '../api-client-settings';
import { agenticIdentityExtension, RequestOptions, resolveServiceUrl } from '../request-options';

export class ConversationMemberClient {
readonly serviceUrl: string;
Expand All @@ -31,17 +32,15 @@ export class ConversationMemberClient {
this._apiClientSettings = mergeApiClientSettings(apiClientSettings);
}

async get(conversationId: string): Promise<TeamsChannelAccount[]> {
const res = await this.http.get<TeamsChannelAccount[]>(
`${this.serviceUrl}/v3/conversations/${conversationId}/members`
);
async get(conversationId: string, options?: RequestOptions): Promise<TeamsChannelAccount[]> {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/members`;
const res = await this.http.get<TeamsChannelAccount[]>(url, agenticIdentityExtension(options));
return res.data.map(resolveAadObjectId);
}

async getById(conversationId: string, id: string): Promise<TeamsChannelAccount> {
const res = await this.http.get<TeamsChannelAccount>(
`${this.serviceUrl}/v3/conversations/${conversationId}/members/${id}`
);
async getById(conversationId: string, id: string, options?: RequestOptions): Promise<TeamsChannelAccount> {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/members/${id}`;
const res = await this.http.get<TeamsChannelAccount>(url, agenticIdentityExtension(options));
Comment thread
heyitsaamir marked this conversation as resolved.
return resolveAadObjectId(res.data);
}

Expand All @@ -52,25 +51,22 @@ export class ConversationMemberClient {
* @param continuationToken - Optional token from a previous call to fetch the next page.
* @returns PagedMembersResult containing members and an optional continuation token.
*/
async getPaged(conversationId: string, pageSize?: number, continuationToken?: string): Promise<PagedMembersResult> {
async getPaged(conversationId: string, pageSize?: number, continuationToken?: string, options?: RequestOptions): Promise<PagedMembersResult> {
const params: Record<string, string | number> = {};
if (pageSize !== undefined) params['pageSize'] = pageSize;
if (continuationToken !== undefined) params['continuationToken'] = continuationToken;

const res = await this.http.get<PagedMembersResult>(
`${this.serviceUrl}/v3/conversations/${conversationId}/pagedMembers`,
{ params }
);
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/pagedMembers`;
const res = await this.http.get<PagedMembersResult>(url, { params, ...agenticIdentityExtension(options) });
return { ...res.data, members: res.data.members.map(resolveAadObjectId) };
}

/**
* @deprecated This will be removed by end of summer 2026.
*/
async delete(conversationId: string, id: string) {
const res = await this.http.delete<void>(
`${this.serviceUrl}/v3/conversations/${conversationId}/members/${id}`
);
async delete(conversationId: string, id: string, options?: RequestOptions) {
const url = `${resolveServiceUrl(this.serviceUrl, options)}/v3/conversations/${conversationId}/members/${id}`;
const res = await this.http.delete<void>(url, agenticIdentityExtension(options));
return res.data;
}
}
10 changes: 5 additions & 5 deletions packages/api/src/clients/conversation/members.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ describe('ConversationMemberClient', () => {
const client = new ConversationMemberClient('', http);
const spy = jest.spyOn(http, 'get').mockResolvedValueOnce({ data: [] });
await client.get('1');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members', {});
});

it('should use client options', async () => {
const client = new ConversationMemberClient('', {});
const spy = jest.spyOn(client.http, 'get').mockResolvedValueOnce({ data: [] });
await client.get('1');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members', {});
});

it('should get', async () => {
const client = new ConversationMemberClient('');
const spy = jest.spyOn(client.http, 'get').mockResolvedValueOnce({ data: [] });
await client.get('1');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members', {});
});

it('should get by id', async () => {
const client = new ConversationMemberClient('');
const spy = jest.spyOn(client.http, 'get').mockResolvedValueOnce({ data: {} });
await client.getById('1', '2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members/2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members/2', {});
});

it('should get paged', async () => {
Expand All @@ -52,6 +52,6 @@ describe('ConversationMemberClient', () => {
const client = new ConversationMemberClient('');
const spy = jest.spyOn(client.http, 'delete').mockResolvedValueOnce({});
await client.delete('1', '2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members/2');
expect(spy).toHaveBeenCalledWith('/v3/conversations/1/members/2', {});
});
});
1 change: 1 addition & 0 deletions packages/api/src/clients/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,4 @@ export * from './team';
export * from './api-client-settings';
export * from './auth';
export * from './auth-provider-interceptor';
export * from './request-options';
Comment thread
heyitsaamir marked this conversation as resolved.
Loading
Loading