-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add Microsoft Teams integration #6410
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
New Issues (2)Checkmarx found the following issues in this Pull Request
|
where oi.Type == IntegrationType.Teams && | ||
oi.Configuration != null && | ||
oi.Configuration.Contains($"\"TenantId\":\"{_tenantId}\"") && | ||
oi.Configuration.Contains($"\"id\":\"{_teamId}\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@withinfocus I couldn't find a good way to do a JSON query in EF and keep it generic, so I opted to use this Contains approach instead. I think it's functionally equivalent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works, but you could also parse it as JSON and make the checks a bit more cleanly. It doesn't have to all be in the where clause.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6410 +/- ##
==========================================
+ Coverage 50.54% 54.72% +4.17%
==========================================
Files 1854 1864 +10
Lines 82177 82541 +364
Branches 7271 7299 +28
==========================================
+ Hits 41535 45167 +3632
+ Misses 39050 35699 -3351
- Partials 1592 1675 +83 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
I've requested a review from the team as I'm trying to wrap up work before going on leave. |
…den/server into brant/microsoft-teams-integration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few things. This is a complex one.
src/Core/AdminConsole/Models/Teams/TeamsBotCredentialProvider.cs
Outdated
Show resolved
Hide resolved
src/Core/AdminConsole/Repositories/IOrganizationIntegrationRepository.cs
Outdated
Show resolved
Hide resolved
...or/DbScripts/2025-10-3_00_AddOrganizationIntegration_ReadByTenantIdTeamIdStoredProcedure.sql
Outdated
Show resolved
Hide resolved
src/Core/AdminConsole/Services/Implementations/EventIntegrations/TeamsService.cs
Outdated
Show resolved
Hide resolved
<PackageReference Include="Microsoft.Bot.Builder" Version="4.23.0" /> | ||
<PackageReference Include="Microsoft.Bot.Connector" Version="4.23.0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ These save a lot of time and code, but I don't love adding dependencies so strongly for an integration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree 100% in general. I did all of the graph api calls natively since they were just normal REST. But the incoming bot stuff seemed to push heavily in the dependency direction.
where oi.Type == IntegrationType.Teams && | ||
oi.Configuration != null && | ||
oi.Configuration.Contains($"\"TenantId\":\"{_tenantId}\"") && | ||
oi.Configuration.Contains($"\"id\":\"{_teamId}\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works, but you could also parse it as JSON and make the checks a bit more cleanly. It doesn't have to all be in the where clause.
src/Sql/dbo/Stored Procedures/OrganizationIntegration_ReadByTenantIdTeamId.sql
Outdated
Show resolved
Hide resolved
Oh, and do you also want to update the docs? |
@withinfocus Looking at what needs to change in the docs now, but wanted to push up the code changes I have. I think I've addressed all your comments. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation can be added here in another commit and I'll re-review but this all works. I still have one open comment about the EF query getting cleaned up, but what you have works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left two comments on the commit (that often gets me when I click on email notifications) with one being a ⛏️ you can quickly fix, but this is good to go at this point.
throw new BadRequestException("No teams were found."); | ||
} | ||
|
||
var teamsIntegration = new TeamsIntegration(TenantId: teams.First().TenantId, Teams: teams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var teamsIntegration = new TeamsIntegration(TenantId: teams.First().TenantId, Teams: teams); | |
var teamsIntegration = new TeamsIntegration(TenantId: teams[0].TenantId, Teams: teams); |
To get rid of warning.
throw new NotFoundException(); | ||
} | ||
|
||
string? callbackUrl = Url.RouteUrl( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string? callbackUrl = Url.RouteUrl( | |
var callbackUrl = Url.RouteUrl( |
To remove warning.
throw new NotFoundException(); | ||
} | ||
|
||
string? callbackUrl = Url.RouteUrl( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string? callbackUrl = Url.RouteUrl( | |
var callbackUrl = Url.RouteUrl( |
To remove warning.
string? ChannelId = null, | ||
Uri? ServiceUrl = null) | ||
{ | ||
public bool isVerified => (!string.IsNullOrEmpty(ChannelId) && ServiceUrl is not null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public bool isVerified => (!string.IsNullOrEmpty(ChannelId) && ServiceUrl is not null); | |
public bool IsVerified => !string.IsNullOrEmpty(ChannelId) && ServiceUrl is not null; |
To remove warning
GlobalSettings globalSettings, | ||
ILogger<TeamsService> logger) : ActivityHandler, ITeamsService | ||
{ | ||
private readonly IOrganizationIntegrationRepository _integrationRepository = integrationRepository; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't we just use the integration repository from the primary constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 fixed in next commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. Thanks for all the updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't realize this was waiting on me after AC review.
* Add Microsoft Teams integration * Fix method naming error * Expand and clean up unit test coverage * Update with PR feedback * Add documentation, add In Progress logic/tests for Teams * Fixed lowercase Slack * Added docs; Updated PR suggestions; * Fix broken tests
📔 Objective
This PR adds an integration with Microsoft Teams.
This is a multi-step process that we will need to take into account when building the UI for this.
Initiated
record inOrganizationIntegration
Initiated
record with a matching tenant id and team id. If it is present, we update that record to include the serviceUrl and conversation id of the channel, thus moving the Teams integration toCompleted
. If there is not a matching record, we disregard the requestOrganizationIntegrationConfiguration
records for the desired event types and template strings. These records will all containnull
for theConfiguration
at this level because all of the necessary configuration is provided in theOrganizationIntegration
configuration object.Setup tasks
globalSettings.EventLogging.AzureServiceBus.TeamsEventSubscriptionName
with a default name ofevents-teams-subscription
globalSettings.EventLogging.AzureServiceBus.TeamsIntegrationSubscriptionName
with a default name ofintegration-teams-subscription
Notes
IntegrationOAuthState
to do the OAuth validation piece. This helps validate the information coming back on the create method was actually generated by a legitimate actor and for the correct organization. As the API is public and not behind auth, we need to make sure that the requests received are validTeamsBotCredentialProvider
. It will validate that the requests are issued by Microsoft and contain our client id as the audience. Additionally, this API can only add information to an existing, matchingInitiated
record - you must have a matching tenant id and teams id. and t can't modify aCompleted
record⏰ Reminders before review
🦮 Reviewer guidelines
:+1:
) or similar for great changes:memo:
) or ℹ️ (:information_source:
) for notes or general info:question:
) for questions:thinking:
) or 💭 (:thought_balloon:
) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:
) for suggestions / improvements:x:
) or:warning:
) for more significant problems or concerns needing attention:seedling:
) or ♻️ (:recycle:
) for future improvements or indications of technical debt:pick:
) for minor or nitpick changes