diff --git a/Libraries/Microsoft.Teams.Api/Account.cs b/Libraries/Microsoft.Teams.Api/Account.cs index 5a3cc3b0..1f78284c 100644 --- a/Libraries/Microsoft.Teams.Api/Account.cs +++ b/Libraries/Microsoft.Teams.Api/Account.cs @@ -35,6 +35,49 @@ public class Account public Dictionary? Properties { get; set; } } +/// +/// Represents a Teams channel account, extending the basic channel account with Teams-specific properties. +/// This is used to represent a user or bot in Microsoft Teams conversations. +/// +/// +public class TeamsChannelAccount : Account +{ + /// + /// Given name (first name) of the user. + /// + [JsonPropertyName("givenName")] + [JsonPropertyOrder(6)] + public string? GivenName { get; set; } + + /// + /// Surname (last name) of the user. + /// + [JsonPropertyName("surname")] + [JsonPropertyOrder(7)] + public string? Surname { get; set; } + + /// + /// Email address of the user. + /// + [JsonPropertyName("email")] + [JsonPropertyOrder(8)] + public string? Email { get; set; } + + /// + /// Unique User Principal Name (UPN) for the user in AAD. + /// + [JsonPropertyName("userPrincipalName")] + [JsonPropertyOrder(9)] + public string? UserPrincipalName { get; set; } + + /// + /// Unique identifier for the user's Azure AD tenant. + /// + [JsonPropertyName("tenantId")] + [JsonPropertyOrder(10)] + public string? TenantId { get; set; } +} + [JsonConverter(typeof(JsonConverter))] public class Role(string value) : StringEnum(value) { diff --git a/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantJoinActivity.cs b/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantJoinActivity.cs index 4b1fcd9a..264bbfd8 100644 --- a/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantJoinActivity.cs +++ b/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantJoinActivity.cs @@ -42,7 +42,7 @@ public class Member /// [JsonPropertyName("user")] [JsonPropertyOrder(0)] - public required Account User { get; set; } + public required TeamsChannelAccount User { get; set; } /// /// The participants info. diff --git a/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantLeaveActivity.cs b/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantLeaveActivity.cs index ca82b901..700e6ce9 100644 --- a/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantLeaveActivity.cs +++ b/Libraries/Microsoft.Teams.Api/Activities/Events/MeetingParticipantLeaveActivity.cs @@ -42,7 +42,7 @@ public class Member /// [JsonPropertyName("user")] [JsonPropertyOrder(0)] - public required Account User { get; set; } + public required TeamsChannelAccount User { get; set; } /// /// The participants info. diff --git a/Libraries/Microsoft.Teams.Api/Clients/MeetingClient.cs b/Libraries/Microsoft.Teams.Api/Clients/MeetingClient.cs index 9c8ef2da..b51c57a1 100644 --- a/Libraries/Microsoft.Teams.Api/Clients/MeetingClient.cs +++ b/Libraries/Microsoft.Teams.Api/Clients/MeetingClient.cs @@ -64,7 +64,7 @@ public class MeetingParticipant /// [JsonPropertyName("user")] [JsonPropertyOrder(1)] - public Account? User { get; set; } + public TeamsChannelAccount? User { get; set; } /// /// The participant's role in the meeting diff --git a/Libraries/Microsoft.Teams.Api/Clients/MemberClient.cs b/Libraries/Microsoft.Teams.Api/Clients/MemberClient.cs index d69c96a2..77cc2c6a 100644 --- a/Libraries/Microsoft.Teams.Api/Clients/MemberClient.cs +++ b/Libraries/Microsoft.Teams.Api/Clients/MemberClient.cs @@ -29,17 +29,17 @@ public MemberClient(string serviceUrl, IHttpClientFactory factory, CancellationT ServiceUrl = serviceUrl; } - public async Task> GetAsync(string conversationId) + public async Task> GetAsync(string conversationId) { var request = HttpRequest.Get($"{ServiceUrl}v3/conversations/{conversationId}/members"); - var response = await _http.SendAsync>(request, _cancellationToken); + var response = await _http.SendAsync>(request, _cancellationToken); return response.Body; } - public async Task GetByIdAsync(string conversationId, string memberId) + public async Task GetByIdAsync(string conversationId, string memberId) { var request = HttpRequest.Get($"{ServiceUrl}v3/conversations/{conversationId}/members/{memberId}"); - var response = await _http.SendAsync(request, _cancellationToken); + var response = await _http.SendAsync(request, _cancellationToken); return response.Body; } diff --git a/Libraries/Microsoft.Teams.Api/Meetings/Meeting.cs b/Libraries/Microsoft.Teams.Api/Meetings/Meeting.cs index 0f9d4c9c..6b2760b7 100644 --- a/Libraries/Microsoft.Teams.Api/Meetings/Meeting.cs +++ b/Libraries/Microsoft.Teams.Api/Meetings/Meeting.cs @@ -36,5 +36,5 @@ public class Meeting /// [JsonPropertyName("organizer")] [JsonPropertyOrder(3)] - public Account? Organizer { get; set; } + public TeamsChannelAccount? Organizer { get; set; } } \ No newline at end of file diff --git a/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantJoinActivityTests.cs b/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantJoinActivityTests.cs index d7d3cdc3..e5402174 100644 --- a/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantJoinActivityTests.cs +++ b/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantJoinActivityTests.cs @@ -26,7 +26,7 @@ public MeetingParticipantJoinActivity SetupMeetingParticipantJoinActivity() Members = new List() { new Member() { - User = new Account() + User = new TeamsChannelAccount() { Id = "userId", Name = "userName" @@ -39,7 +39,7 @@ public MeetingParticipantJoinActivity SetupMeetingParticipantJoinActivity() }, new Member() { - User = new Account() + User = new TeamsChannelAccount() { Id = "botId", Name = "BotUser" diff --git a/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantLeaveActivityTests.cs b/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantLeaveActivityTests.cs index 44c7a3cb..66cb9dfc 100644 --- a/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantLeaveActivityTests.cs +++ b/Tests/Microsoft.Teams.Api.Tests/Activities/Events/MeetingParticipantLeaveActivityTests.cs @@ -26,7 +26,7 @@ public MeetingParticipantLeaveActivity SetupMeetingParticipantLeaveActivity() Members = new List() { new Member() { - User = new Account() + User = new TeamsChannelAccount() { Id = "userId", Name = "userName" @@ -39,7 +39,7 @@ public MeetingParticipantLeaveActivity SetupMeetingParticipantLeaveActivity() }, new Member() { - User = new Account() + User = new TeamsChannelAccount() { Id = "botId", Name = "BotUser" diff --git a/Tests/Microsoft.Teams.Api.Tests/Clients/MeetingClientTests.cs b/Tests/Microsoft.Teams.Api.Tests/Clients/MeetingClientTests.cs index 700c7ae4..abf43607 100644 --- a/Tests/Microsoft.Teams.Api.Tests/Clients/MeetingClientTests.cs +++ b/Tests/Microsoft.Teams.Api.Tests/Clients/MeetingClientTests.cs @@ -56,7 +56,7 @@ public async Task MeetingClient_GetParticipantAsync() Body = new MeetingParticipant { Id = "participant1", - User = new Account { Id = "user1", Name = "John Doe" }, + User = new TeamsChannelAccount { Id = "user1", Name = "John Doe" }, Role = "Presenter", IsOrganizer = true, JoinTime = DateTime.UtcNow diff --git a/Tests/Microsoft.Teams.Api.Tests/Clients/MemberClientTests.cs b/Tests/Microsoft.Teams.Api.Tests/Clients/MemberClientTests.cs index d102809c..54fa20d5 100644 --- a/Tests/Microsoft.Teams.Api.Tests/Clients/MemberClientTests.cs +++ b/Tests/Microsoft.Teams.Api.Tests/Clients/MemberClientTests.cs @@ -16,15 +16,15 @@ public async Task MemberClient_GetAsync() responseMessage.Headers.Add("Custom-Header", "HeaderValue"); var mockHandler = new Mock(); mockHandler - .Setup(handler => handler.SendAsync>(It.IsAny(), It.IsAny())) - .ReturnsAsync(new HttpResponse>() + .Setup(handler => handler.SendAsync>(It.IsAny(), It.IsAny())) + .ReturnsAsync(new HttpResponse>() { Headers = responseMessage.Headers, StatusCode = HttpStatusCode.OK, - Body = new List + Body = new List { - new Account { Id = "member1", Name = "User 1" }, - new Account { Id = "member2", Name = "User 2" } + new TeamsChannelAccount { Id = "member1", Name = "User 1" }, + new TeamsChannelAccount { Id = "member2", Name = "User 2" } } }); @@ -39,7 +39,7 @@ public async Task MemberClient_GetAsync() string expectedUrl = "https://serviceurl.com/v3/conversations/conv123/members"; HttpMethod expectedMethod = HttpMethod.Get; - mockHandler.Verify(x => x.SendAsync>( + mockHandler.Verify(x => x.SendAsync>( It.Is(arg => arg.Url == expectedUrl && arg.Method == expectedMethod), It.IsAny()), Times.Once); @@ -52,12 +52,12 @@ public async Task MemberClient_GetByIdAsync() responseMessage.Headers.Add("Custom-Header", "HeaderValue"); var mockHandler = new Mock(); mockHandler - .Setup(handler => handler.SendAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new HttpResponse() + .Setup(handler => handler.SendAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(new HttpResponse() { Headers = responseMessage.Headers, StatusCode = HttpStatusCode.OK, - Body = new Account { Id = "member1", Name = "User 1" } + Body = new TeamsChannelAccount { Id = "member1", Name = "User 1" } }); string serviceUrl = "https://serviceurl.com/"; @@ -72,7 +72,7 @@ public async Task MemberClient_GetByIdAsync() string expectedUrl = "https://serviceurl.com/v3/conversations/conv123/members/member1"; HttpMethod expectedMethod = HttpMethod.Get; - mockHandler.Verify(x => x.SendAsync( + mockHandler.Verify(x => x.SendAsync( It.Is(arg => arg.Url == expectedUrl && arg.Method == expectedMethod), It.IsAny()), Times.Once); diff --git a/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingJoinEventTests.cs b/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingJoinEventTests.cs index a536e47c..b18e0738 100644 --- a/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingJoinEventTests.cs +++ b/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingJoinEventTests.cs @@ -47,7 +47,7 @@ public async Task Should_CallHandler_OnMeetingJoinEvent() { new MeetingParticipantJoinActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantJoinActivityValue.Meeting { InMeeting = true, @@ -103,7 +103,7 @@ public void MeetingJoinAttribute_Select_ReturnsTrueForMeetingJoinActivity() { new MeetingParticipantJoinActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantJoinActivityValue.Meeting { InMeeting = true, @@ -148,7 +148,7 @@ public async Task MeetingJoinAttribute_Controller_Call() { new MeetingParticipantJoinActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantJoinActivityValue.Meeting { InMeeting = true, diff --git a/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingLeaveEventTests.cs b/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingLeaveEventTests.cs index f75ba98b..9170c09b 100644 --- a/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingLeaveEventTests.cs +++ b/Tests/Microsoft.Teams.Apps.Tests/Activities/Events/MeetingLeaveEventTests.cs @@ -49,7 +49,7 @@ public async Task Should_CallHandler_OnMeetingLeaveEvent() { new MeetingParticipantLeaveActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantLeaveActivityValue.Meeting { InMeeting = true, @@ -105,7 +105,7 @@ public void MeetingLeaveAttribute_Select_ReturnsTrueForMeetingLeaveActivity() { new MeetingParticipantLeaveActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantLeaveActivityValue.Meeting { InMeeting = true, @@ -150,7 +150,7 @@ public async Task MeetingLeaveAttribute_Controller_Call() { new MeetingParticipantLeaveActivityValue.Member { - User = new Account { Id = "user1", Name = "Test User" }, + User = new TeamsChannelAccount { Id = "user1", Name = "Test User" }, Meeting = new MeetingParticipantLeaveActivityValue.Meeting { InMeeting = true,