From 78588d0246d21cfbd8bb01b36d6cec380647c9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Thu, 3 Aug 2023 18:36:47 +0100 Subject: [PATCH] [PM-3007] Caching user policies on PolicyService variable (#3117) * [PM-3007] Caching user policies on PolicyService variable * [PM-3007] Added missing newlines on sql files --- .../IOrganizationUserRepository.cs | 2 +- .../Services/Implementations/PolicyService.cs | 17 +++++++--- .../OrganizationUserRepository.cs | 4 +-- .../OrganizationUserRepository.cs | 5 ++- ...tionUser_ReadByUserIdWithPolicyDetails.sql | 30 ++++++++---------- test/Core.Test/Services/PolicyServiceTests.cs | 10 ++---- .../OrganizationUserRepositoryTests.cs | 2 +- ...ationUserReadByUserIdWithPolicyDetails.sql | 31 +++++++++++++++++++ 8 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 util/Migrator/DbScripts/2023-07-18_00_OrganizationUserReadByUserIdWithPolicyDetails.sql diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index f9dfa12c2..fb012c623 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -39,6 +39,6 @@ public interface IOrganizationUserRepository : IRepository> GetManyByMinimumRoleAsync(Guid organizationId, OrganizationUserType minRole); Task RevokeAsync(Guid id); Task RestoreAsync(Guid id, OrganizationUserStatusType status); - Task> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType); + Task> GetByUserIdWithPolicyDetailsAsync(Guid userId); Task GetOccupiedSmSeatCountByOrganizationIdAsync(Guid organizationId); } diff --git a/src/Core/Services/Implementations/PolicyService.cs b/src/Core/Services/Implementations/PolicyService.cs index 6b1009093..595a798e7 100644 --- a/src/Core/Services/Implementations/PolicyService.cs +++ b/src/Core/Services/Implementations/PolicyService.cs @@ -20,6 +20,8 @@ public class PolicyService : IPolicyService private readonly IMailService _mailService; private readonly GlobalSettings _globalSettings; + private IEnumerable _cachedOrganizationUserPolicyDetails; + public PolicyService( IEventService eventService, IOrganizationRepository organizationRepository, @@ -194,18 +196,25 @@ public class PolicyService : IPolicyService return result.Any(); } - private async Task> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted) + private async Task> QueryOrganizationUserPolicyDetailsAsync(Guid userId, PolicyType? policyType, OrganizationUserStatusType minStatus = OrganizationUserStatusType.Accepted) { - var organizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId, policyType); + // Check if the cached policies are available + if (_cachedOrganizationUserPolicyDetails == null) + { + // Cached policies not available, retrieve from the repository + _cachedOrganizationUserPolicyDetails = await _organizationUserRepository.GetByUserIdWithPolicyDetailsAsync(userId); + } + var excludedUserTypes = GetUserTypesExcludedFromPolicy(policyType); - return organizationUserPolicyDetails.Where(o => + return _cachedOrganizationUserPolicyDetails.Where(o => + (policyType == null || o.PolicyType == policyType) && o.PolicyEnabled && !excludedUserTypes.Contains(o.OrganizationUserType) && o.OrganizationUserStatus >= minStatus && !o.IsProvider); } - private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType policyType) + private OrganizationUserType[] GetUserTypesExcludedFromPolicy(PolicyType? policyType) { switch (policyType) { diff --git a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs index 008242c26..27410fb3f 100644 --- a/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.Dapper/Repositories/OrganizationUserRepository.cs @@ -505,13 +505,13 @@ public class OrganizationUserRepository : Repository, IO } } - public async Task> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType) + public async Task> GetByUserIdWithPolicyDetailsAsync(Guid userId) { using (var connection = new SqlConnection(ConnectionString)) { var results = await connection.QueryAsync( $"[{Schema}].[{Table}_ReadByUserIdWithPolicyDetails]", - new { UserId = userId, PolicyType = policyType }, + new { UserId = userId }, commandType: CommandType.StoredProcedure); return results.ToList(); diff --git a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs index 8256696d9..f5ba2b9c1 100644 --- a/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/OrganizationUserRepository.cs @@ -588,7 +588,7 @@ public class OrganizationUserRepository : Repository> GetByUserIdWithPolicyDetailsAsync(Guid userId, PolicyType policyType) + public async Task> GetByUserIdWithPolicyDetailsAsync(Guid userId) { using (var scope = ServiceScopeFactory.CreateScope()) { @@ -604,8 +604,7 @@ public class OrganizationUserRepository : Repository sutProvider) { sutProvider.GetDependency() - .GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.RequireSso) + .GetByUserIdWithPolicyDetailsAsync(userId) .Returns(new List { new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = false, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false}, new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = false }, - new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true } - }); - - sutProvider.GetDependency() - .GetByUserIdWithPolicyDetailsAsync(userId, PolicyType.DisableSend) - .Returns(new List - { + new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.RequireSso, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.Owner, OrganizationUserStatus = OrganizationUserStatusType.Confirmed, IsProvider = true }, new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = false }, new() { OrganizationId = Guid.NewGuid(), PolicyType = PolicyType.DisableSend, PolicyEnabled = true, OrganizationUserType = OrganizationUserType.User, OrganizationUserStatus = OrganizationUserStatusType.Invited, IsProvider = true } }); diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs index 4f3b912b6..b1ee1cbc9 100644 --- a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs +++ b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationUserRepositoryTests.cs @@ -274,7 +274,7 @@ public class OrganizationUserRepositoryTests } // Act - var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id, policy.Type); + var result = await orgUserRepos[i].GetByUserIdWithPolicyDetailsAsync(savedUser.Id); results.Add(result.FirstOrDefault()); } diff --git a/util/Migrator/DbScripts/2023-07-18_00_OrganizationUserReadByUserIdWithPolicyDetails.sql b/util/Migrator/DbScripts/2023-07-18_00_OrganizationUserReadByUserIdWithPolicyDetails.sql new file mode 100644 index 000000000..a56d8cec5 --- /dev/null +++ b/util/Migrator/DbScripts/2023-07-18_00_OrganizationUserReadByUserIdWithPolicyDetails.sql @@ -0,0 +1,31 @@ +CREATE OR ALTER PROCEDURE [dbo].[OrganizationUser_ReadByUserIdWithPolicyDetails] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON +SELECT + OU.[Id] AS OrganizationUserId, + P.[OrganizationId], + P.[Type] AS PolicyType, + P.[Enabled] AS PolicyEnabled, + P.[Data] AS PolicyData, + OU.[Type] AS OrganizationUserType, + OU.[Status] AS OrganizationUserStatus, + OU.[Permissions] AS OrganizationUserPermissionsData, + CASE WHEN PU.[ProviderId] IS NOT NULL THEN 1 ELSE 0 END AS IsProvider +FROM [dbo].[PolicyView] P +INNER JOIN [dbo].[OrganizationUserView] OU + ON P.[OrganizationId] = OU.[OrganizationId] +LEFT JOIN [dbo].[ProviderUserView] PU + ON PU.[UserId] = OU.[UserId] +LEFT JOIN [dbo].[ProviderOrganizationView] PO + ON PO.[ProviderId] = PU.[ProviderId] AND PO.[OrganizationId] = P.[OrganizationId] +WHERE + (OU.[Status] != 0 AND OU.[UserId] = @UserId) -- OrgUsers who have accepted their invite and are linked to a UserId + OR EXISTS ( + SELECT 1 + FROM [dbo].[UserView] U + WHERE U.[Id] = @UserId AND OU.[Email] = U.[Email] AND OU.[Status] = 0 -- 'Invited' OrgUsers are not linked to a UserId yet, so we have to look up their email + ) +END +GO