From 988b9946240739df2df87ab533678e3a5f2190b1 Mon Sep 17 00:00:00 2001 From: Brant DeBow <125889545+brant-livefront@users.noreply.github.com> Date: Wed, 23 Jul 2025 14:24:59 -0400 Subject: [PATCH] [PM-17562] Add GET endpoints for event integrations (#6104) * [PM-17562] Add GET endpoints for event integrations * Default to null for Service * Respond to PR Feedback --- ...ationIntegrationConfigurationController.cs | 21 +++ .../OrganizationIntegrationController.cs | 14 ++ .../OrganizationIntegrationResponseModel.cs | 2 + .../Data/EventIntegrations/HecIntegration.cs | 2 +- ...ationIntegrationConfigurationRepository.cs | 2 + .../IOrganizationIntegrationRepository.cs | 1 + ...ationIntegrationConfigurationRepository.cs | 16 +++ .../OrganizationIntegrationRepository.cs | 18 ++- ...ationIntegrationConfigurationRepository.cs | 14 ++ .../OrganizationIntegrationRepository.cs | 19 ++- ...eadManyByOrganizationIntegrationIdQuery.cs | 33 +++++ ...ntegrationReadManyByOrganizationIdQuery.cs | 30 +++++ ...on_ReadManyByOrganizationIntegrationId.sql | 13 ++ ...onIntegration_ReadManyByOrganizationId.sql | 13 ++ .../OrganizationIntegrationControllerTests.cs | 54 ++++++++ ...ntegrationsConfigurationControllerTests.cs | 125 ++++++++++++++++++ ...onIntegrationAndConfigurationByIdProcs.sql | 29 ++++ 17 files changed, 402 insertions(+), 4 deletions(-) create mode 100644 src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery.cs create mode 100644 src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationReadManyByOrganizationIdQuery.cs create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId.sql create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationIntegration_ReadManyByOrganizationId.sql create mode 100644 util/Migrator/DbScripts/2025-07-18_00_AddReadManyOrganizationIntegrationAndConfigurationByIdProcs.sql diff --git a/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs b/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs index 848098ef00..319fbbe707 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs @@ -18,6 +18,27 @@ public class OrganizationIntegrationConfigurationController( IOrganizationIntegrationRepository integrationRepository, IOrganizationIntegrationConfigurationRepository integrationConfigurationRepository) : Controller { + [HttpGet("")] + public async Task> GetAsync( + Guid organizationId, + Guid integrationId) + { + if (!await HasPermission(organizationId)) + { + throw new NotFoundException(); + } + var integration = await integrationRepository.GetByIdAsync(integrationId); + if (integration == null || integration.OrganizationId != organizationId) + { + throw new NotFoundException(); + } + + var configurations = await integrationConfigurationRepository.GetManyByIntegrationAsync(integrationId); + return configurations + .Select(configuration => new OrganizationIntegrationConfigurationResponseModel(configuration)) + .ToList(); + } + [HttpPost("")] public async Task CreateAsync( Guid organizationId, diff --git a/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs b/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs index 3b52e7a8da..7052350c9a 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs @@ -19,6 +19,20 @@ public class OrganizationIntegrationController( ICurrentContext currentContext, IOrganizationIntegrationRepository integrationRepository) : Controller { + [HttpGet("")] + public async Task> GetAsync(Guid organizationId) + { + if (!await HasPermission(organizationId)) + { + throw new NotFoundException(); + } + + var integrations = await integrationRepository.GetManyByOrganizationAsync(organizationId); + return integrations + .Select(integration => new OrganizationIntegrationResponseModel(integration)) + .ToList(); + } + [HttpPost("")] public async Task CreateAsync(Guid organizationId, [FromBody] OrganizationIntegrationRequestModel model) { diff --git a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationIntegrationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationIntegrationResponseModel.cs index cc6e778528..f062ff46a2 100644 --- a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationIntegrationResponseModel.cs +++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationIntegrationResponseModel.cs @@ -15,8 +15,10 @@ public class OrganizationIntegrationResponseModel : ResponseModel Id = organizationIntegration.Id; Type = organizationIntegration.Type; + Configuration = organizationIntegration.Configuration; } public Guid Id { get; set; } public IntegrationType Type { get; set; } + public string? Configuration { get; set; } } diff --git a/src/Core/AdminConsole/Models/Data/EventIntegrations/HecIntegration.cs b/src/Core/AdminConsole/Models/Data/EventIntegrations/HecIntegration.cs index 472ca70c0c..eff9f8e1be 100644 --- a/src/Core/AdminConsole/Models/Data/EventIntegrations/HecIntegration.cs +++ b/src/Core/AdminConsole/Models/Data/EventIntegrations/HecIntegration.cs @@ -2,4 +2,4 @@ namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations; -public record HecIntegration(Uri Uri, string Scheme, string Token); +public record HecIntegration(Uri Uri, string Scheme, string Token, string? Service = null); diff --git a/src/Core/AdminConsole/Repositories/IOrganizationIntegrationConfigurationRepository.cs b/src/Core/AdminConsole/Repositories/IOrganizationIntegrationConfigurationRepository.cs index 53159c98e7..0a774cf395 100644 --- a/src/Core/AdminConsole/Repositories/IOrganizationIntegrationConfigurationRepository.cs +++ b/src/Core/AdminConsole/Repositories/IOrganizationIntegrationConfigurationRepository.cs @@ -12,4 +12,6 @@ public interface IOrganizationIntegrationConfigurationRepository : IRepository> GetAllConfigurationDetailsAsync(); + + Task> GetManyByIntegrationAsync(Guid organizationIntegrationId); } diff --git a/src/Core/AdminConsole/Repositories/IOrganizationIntegrationRepository.cs b/src/Core/AdminConsole/Repositories/IOrganizationIntegrationRepository.cs index cd7700c310..434c8ddee3 100644 --- a/src/Core/AdminConsole/Repositories/IOrganizationIntegrationRepository.cs +++ b/src/Core/AdminConsole/Repositories/IOrganizationIntegrationRepository.cs @@ -4,4 +4,5 @@ namespace Bit.Core.Repositories; public interface IOrganizationIntegrationRepository : IRepository { + Task> GetManyByOrganizationAsync(Guid organizationId); } diff --git a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs index 5a7e1ce152..005e93c6aa 100644 --- a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs @@ -52,4 +52,20 @@ public class OrganizationIntegrationConfigurationRepository : Repository> GetManyByIntegrationAsync(Guid organizationIntegrationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId]", + new + { + OrganizationIntegrationId = organizationIntegrationId + }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } } diff --git a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationRepository.cs b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationRepository.cs index 99f0e35378..ece9697a31 100644 --- a/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationRepository.cs +++ b/src/Infrastructure.Dapper/AdminConsole/Repositories/OrganizationIntegrationRepository.cs @@ -1,6 +1,9 @@ -using Bit.Core.AdminConsole.Entities; +using System.Data; +using Bit.Core.AdminConsole.Entities; using Bit.Core.Repositories; using Bit.Core.Settings; +using Dapper; +using Microsoft.Data.SqlClient; namespace Bit.Infrastructure.Dapper.Repositories; @@ -13,4 +16,17 @@ public class OrganizationIntegrationRepository : Repository> GetManyByOrganizationAsync(Guid organizationId) + { + using (var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[OrganizationIntegration_ReadManyByOrganizationId]", + new { OrganizationId = organizationId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs index 1e1dcd3ba4..fc391b958c 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationConfigurationRepository.cs @@ -3,6 +3,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; using Bit.Infrastructure.EntityFramework.AdminConsole.Models; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories.Queries; using Microsoft.EntityFrameworkCore; @@ -40,4 +41,17 @@ public class OrganizationIntegrationConfigurationRepository : Repository> GetManyByIntegrationAsync( + Guid organizationIntegrationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var query = new OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery( + organizationIntegrationId + ); + return await query.Run(dbContext).ToListAsync(); + } + } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationRepository.cs index 816ad3b25f..5670b2ae9b 100644 --- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationRepository.cs +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationIntegrationRepository.cs @@ -1,14 +1,29 @@ using AutoMapper; using Bit.Core.Repositories; using Bit.Infrastructure.EntityFramework.AdminConsole.Models; +using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; using Bit.Infrastructure.EntityFramework.Repositories; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories; -public class OrganizationIntegrationRepository : Repository, IOrganizationIntegrationRepository +public class OrganizationIntegrationRepository : + Repository, + IOrganizationIntegrationRepository { public OrganizationIntegrationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationIntegrations) - { } + { + } + + public async Task> GetManyByOrganizationAsync(Guid organizationId) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + var dbContext = GetDatabaseContext(scope); + var query = new OrganizationIntegrationReadManyByOrganizationIdQuery(organizationId); + return await query.Run(dbContext).ToListAsync(); + } + } } diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery.cs new file mode 100644 index 0000000000..3ed3a48723 --- /dev/null +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery.cs @@ -0,0 +1,33 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; + +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; + +public class OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery : IQuery +{ + private readonly Guid _organizationIntegrationId; + + public OrganizationIntegrationConfigurationReadManyByOrganizationIntegrationIdQuery(Guid organizationIntegrationId) + { + _organizationIntegrationId = organizationIntegrationId; + } + + public IQueryable Run(DatabaseContext dbContext) + { + var query = from oic in dbContext.OrganizationIntegrationConfigurations + where oic.OrganizationIntegrationId == _organizationIntegrationId + select new OrganizationIntegrationConfiguration() + { + Id = oic.Id, + OrganizationIntegrationId = oic.OrganizationIntegrationId, + Configuration = oic.Configuration, + EventType = oic.EventType, + Filters = oic.Filters, + Template = oic.Template, + RevisionDate = oic.RevisionDate + }; + return query; + } + +} diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationReadManyByOrganizationIdQuery.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationReadManyByOrganizationIdQuery.cs new file mode 100644 index 0000000000..df87ad0bc1 --- /dev/null +++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/Queries/OrganizationIntegrationReadManyByOrganizationIdQuery.cs @@ -0,0 +1,30 @@ +using Bit.Core.AdminConsole.Entities; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Infrastructure.EntityFramework.Repositories.Queries; + +namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories.Queries; + +public class OrganizationIntegrationReadManyByOrganizationIdQuery : IQuery +{ + private readonly Guid _organizationId; + + public OrganizationIntegrationReadManyByOrganizationIdQuery(Guid organizationId) + { + _organizationId = organizationId; + } + + public IQueryable Run(DatabaseContext dbContext) + { + var query = from oi in dbContext.OrganizationIntegrations + where oi.OrganizationId == _organizationId + select new OrganizationIntegration() + { + Id = oi.Id, + OrganizationId = oi.OrganizationId, + Type = oi.Type, + Configuration = oi.Configuration, + }; + return query; + } + +} diff --git a/src/Sql/dbo/Stored Procedures/OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId.sql new file mode 100644 index 0000000000..b187ff1d5f --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId] + @OrganizationIntegrationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationIntegrationConfigurationView] +WHERE + [OrganizationIntegrationId] = @OrganizationIntegrationId +END diff --git a/src/Sql/dbo/Stored Procedures/OrganizationIntegration_ReadManyByOrganizationId.sql b/src/Sql/dbo/Stored Procedures/OrganizationIntegration_ReadManyByOrganizationId.sql new file mode 100644 index 0000000000..939cfc0288 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationIntegration_ReadManyByOrganizationId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationIntegration_ReadManyByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationIntegrationView] +WHERE + [OrganizationId] = @OrganizationId +END diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationControllerTests.cs index fbb3ecbfe0..1dd0e86f39 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationControllerTests.cs @@ -25,6 +25,60 @@ public class OrganizationIntegrationControllerTests Type = IntegrationType.Webhook }; + [Theory, BitAutoData] + public async Task GetAsync_UserIsNotOrganizationAdmin_ThrowsNotFound( + SutProvider sutProvider, + Guid organizationId) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(false); + + await Assert.ThrowsAsync(() => sutProvider.Sut.GetAsync(organizationId)); + } + + [Theory, BitAutoData] + public async Task GetAsync_IntegrationsExist_ReturnsIntegrations( + SutProvider sutProvider, + Guid organizationId, + List integrations) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organizationId) + .Returns(integrations); + + var result = await sutProvider.Sut.GetAsync(organizationId); + + await sutProvider.GetDependency().Received(1) + .GetManyByOrganizationAsync(organizationId); + + Assert.Equal(integrations.Count, result.Count); + Assert.All(result, r => Assert.IsType(r)); + } + + [Theory, BitAutoData] + public async Task GetAsync_NoIntegrations_ReturnsEmptyList( + SutProvider sutProvider, + Guid organizationId) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetManyByOrganizationAsync(organizationId) + .Returns([]); + + var result = await sutProvider.Sut.GetAsync(organizationId); + + Assert.Empty(result); + } + [Theory, BitAutoData] public async Task CreateAsync_Webhook_AllParamsProvided_Succeeds( SutProvider sutProvider, diff --git a/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationsConfigurationControllerTests.cs b/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationsConfigurationControllerTests.cs index e2ee854793..4ccfa70308 100644 --- a/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationsConfigurationControllerTests.cs +++ b/test/Api.Test/AdminConsole/Controllers/OrganizationIntegrationsConfigurationControllerTests.cs @@ -141,6 +141,131 @@ public class OrganizationIntegrationsConfigurationControllerTests await Assert.ThrowsAsync(async () => await sutProvider.Sut.DeleteAsync(organizationId, Guid.Empty, Guid.Empty)); } + [Theory, BitAutoData] + public async Task GetAsync_ConfigurationsExist_Succeeds( + SutProvider sutProvider, + Guid organizationId, + OrganizationIntegration organizationIntegration, + List organizationIntegrationConfigurations) + { + organizationIntegration.OrganizationId = organizationId; + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetByIdAsync(Arg.Any()) + .Returns(organizationIntegration); + sutProvider.GetDependency() + .GetManyByIntegrationAsync(Arg.Any()) + .Returns(organizationIntegrationConfigurations); + + var result = await sutProvider.Sut.GetAsync(organizationId, organizationIntegration.Id); + Assert.NotNull(result); + Assert.Equal(organizationIntegrationConfigurations.Count, result.Count); + Assert.All(result, r => Assert.IsType(r)); + + await sutProvider.GetDependency().Received(1) + .GetByIdAsync(organizationIntegration.Id); + await sutProvider.GetDependency().Received(1) + .GetManyByIntegrationAsync(organizationIntegration.Id); + } + + [Theory, BitAutoData] + public async Task GetAsync_NoConfigurationsExist_ReturnsEmptyList( + SutProvider sutProvider, + Guid organizationId, + OrganizationIntegration organizationIntegration) + { + organizationIntegration.OrganizationId = organizationId; + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetByIdAsync(Arg.Any()) + .Returns(organizationIntegration); + sutProvider.GetDependency() + .GetManyByIntegrationAsync(Arg.Any()) + .Returns([]); + + var result = await sutProvider.Sut.GetAsync(organizationId, organizationIntegration.Id); + Assert.NotNull(result); + Assert.Empty(result); + + await sutProvider.GetDependency().Received(1) + .GetByIdAsync(organizationIntegration.Id); + await sutProvider.GetDependency().Received(1) + .GetManyByIntegrationAsync(organizationIntegration.Id); + } + + // [Theory, BitAutoData] + // public async Task GetAsync_IntegrationConfigurationDoesNotExist_ThrowsNotFound( + // SutProvider sutProvider, + // Guid organizationId, + // OrganizationIntegration organizationIntegration) + // { + // organizationIntegration.OrganizationId = organizationId; + // sutProvider.Sut.Url = Substitute.For(); + // sutProvider.GetDependency() + // .OrganizationOwner(organizationId) + // .Returns(true); + // sutProvider.GetDependency() + // .GetByIdAsync(Arg.Any()) + // .Returns(organizationIntegration); + // sutProvider.GetDependency() + // .GetByIdAsync(Arg.Any()) + // .ReturnsNull(); + // + // await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetAsync(organizationId, Guid.Empty, Guid.Empty)); + // } + // + [Theory, BitAutoData] + public async Task GetAsync_IntegrationDoesNotExist_ThrowsNotFound( + SutProvider sutProvider, + Guid organizationId) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetByIdAsync(Arg.Any()) + .ReturnsNull(); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetAsync(organizationId, Guid.NewGuid())); + } + + [Theory, BitAutoData] + public async Task GetAsync_IntegrationDoesNotBelongToOrganization_ThrowsNotFound( + SutProvider sutProvider, + Guid organizationId, + OrganizationIntegration organizationIntegration) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(true); + sutProvider.GetDependency() + .GetByIdAsync(Arg.Any()) + .Returns(organizationIntegration); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetAsync(organizationId, organizationIntegration.Id)); + } + + [Theory, BitAutoData] + public async Task GetAsync_UserIsNotOrganizationAdmin_ThrowsNotFound( + SutProvider sutProvider, + Guid organizationId) + { + sutProvider.Sut.Url = Substitute.For(); + sutProvider.GetDependency() + .OrganizationOwner(organizationId) + .Returns(false); + + await Assert.ThrowsAsync(async () => await sutProvider.Sut.GetAsync(organizationId, Guid.NewGuid())); + } + [Theory, BitAutoData] public async Task PostAsync_AllParamsProvided_Slack_Succeeds( SutProvider sutProvider, diff --git a/util/Migrator/DbScripts/2025-07-18_00_AddReadManyOrganizationIntegrationAndConfigurationByIdProcs.sql b/util/Migrator/DbScripts/2025-07-18_00_AddReadManyOrganizationIntegrationAndConfigurationByIdProcs.sql new file mode 100644 index 0000000000..365b3e5350 --- /dev/null +++ b/util/Migrator/DbScripts/2025-07-18_00_AddReadManyOrganizationIntegrationAndConfigurationByIdProcs.sql @@ -0,0 +1,29 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationIntegration_ReadManyByOrganizationId] + @OrganizationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationIntegrationView] +WHERE + [OrganizationId] = @OrganizationId +END +GO + +CREATE OR ALTER PROCEDURE [dbo].[OrganizationIntegrationConfiguration_ReadManyByOrganizationIntegrationId] + @OrganizationIntegrationId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + +SELECT + * +FROM + [dbo].[OrganizationIntegrationConfigurationView] +WHERE + [OrganizationIntegrationId] = @OrganizationIntegrationId +END +GO