diff --git a/src/Dfe.SignIn.Core.Contracts/Users/GetUserOrganisationService.cs b/src/Dfe.SignIn.Core.Contracts/Users/GetUserOrganisationService.cs index d41cabe2..18492f98 100644 --- a/src/Dfe.SignIn.Core.Contracts/Users/GetUserOrganisationService.cs +++ b/src/Dfe.SignIn.Core.Contracts/Users/GetUserOrganisationService.cs @@ -34,7 +34,7 @@ public class GetUserOrganisationService /// /// Gets or sets the unique identifier of the organisation. /// - public Guid OrganisationId { get; set; } + public Guid? OrganisationId { get; set; } /// /// Gets or sets the name of the organisation. @@ -69,7 +69,7 @@ public class GetUserOrganisationService /// /// Gets or sets the organisation status identifier. /// - public int StatusId { get; set; } + public int? StatusId { get; set; } /// /// Gets or sets the date the organisation was closed. @@ -159,7 +159,7 @@ public class GetUserOrganisationService /// /// Gets or sets the unique identifier of the service. /// - public Guid ServiceId { get; set; } + public Guid? ServiceId { get; set; } /// /// Gets or sets the name of the service. @@ -184,5 +184,10 @@ public class GetUserOrganisationService /// /// Gets or sets the organisation role identifier. /// - public short OrgRoleId { get; set; } + public short? OrgRoleId { get; set; } + + /// + /// True if the user belongs to the passed in service at any active organisation. + /// + public int IsInService { get; set; } } diff --git a/src/Dfe.SignIn.PublicApi/MappingExtensions/GetUserOrganisationServiceMapping.cs b/src/Dfe.SignIn.PublicApi/MappingExtensions/GetUserOrganisationServiceMapping.cs index ea7685b4..622c9de0 100644 --- a/src/Dfe.SignIn.PublicApi/MappingExtensions/GetUserOrganisationServiceMapping.cs +++ b/src/Dfe.SignIn.PublicApi/MappingExtensions/GetUserOrganisationServiceMapping.cs @@ -18,10 +18,12 @@ public static class GetUserOrganisationServiceMapping /// public static IEnumerable ToUserDtos(this IEnumerable models) { + var hasService = models.Any(x => x.IsInService == 1); + return models .GroupBy(x => x.UserId) .Select(userGroup => { - GetUserOrganisationService u = userGroup.First(); + var u = userGroup.First(); return new GetUserOrganisationServicesResponse { UserId = u.UserId, @@ -29,9 +31,9 @@ public static IEnumerable ToUserDtos(this I Email = u.Email, FamilyName = u.FamilyName, GivenName = u.GivenName, - - Organisations = userGroup.ToOrganisationDtos() - + Organisations = hasService + ? userGroup.ToOrganisationDtos() + : [] }; }); } @@ -45,12 +47,13 @@ public static IEnumerable ToOrganisationDtos( this IGrouping userGroup) { return userGroup - .GroupBy(x => x.OrganisationId) + .Where(x => x.OrganisationId.HasValue) // Filter out nulls + .GroupBy(x => x.OrganisationId!.Value) // Use ! to suppress nullable warning .Select(static orgGroup => { var o = orgGroup.First(); return new OrganisationDto { - Id = o.OrganisationId, + Id = o.OrganisationId ?? Guid.Empty, Name = o.OrganisationName, Category = new CategoryDto { @@ -63,10 +66,12 @@ public static IEnumerable ToOrganisationDtos( Ukprn = o.Ukprn, EstablishmentNumber = o.EstablishmentNumber, - Status = new StatusDto { - Id = o.StatusId, - Name = EnumHelpers.MapEnum(o.StatusId).GetDescription() - }, + Status = o.StatusId.HasValue + ? new StatusDto { + Id = o.StatusId.Value, + Name = EnumHelpers.MapEnum(o.StatusId).GetDescription() + } + : new StatusDto(), ClosedOn = o.ClosedOn, Address = o.Address, @@ -88,8 +93,8 @@ public static IEnumerable ToOrganisationDtos( Services = orgGroup.ToServiceDtos(), - OrgRoleId = o.OrgRoleId, - OrgRoleName = OrganisationRoles.FromId(o.OrgRoleId)?.Name + OrgRoleId = o.OrgRoleId ?? 0, // Fix: Use fallback if null + OrgRoleName = o.OrgRoleId.HasValue ? OrganisationRoles.FromId(o.OrgRoleId.Value)?.Name : null // Fix: Only access .Value if not null }; }); diff --git a/src/Dfe.SignIn.PublicApi/Repository/OrganisationRepository.cs b/src/Dfe.SignIn.PublicApi/Repository/OrganisationRepository.cs index 9b307296..46717e20 100644 --- a/src/Dfe.SignIn.PublicApi/Repository/OrganisationRepository.cs +++ b/src/Dfe.SignIn.PublicApi/Repository/OrganisationRepository.cs @@ -89,36 +89,38 @@ u.sub AS UserId ,r.[Name] AS RoleName ,r.Code AS RoleCode ,uo.role_id AS OrgRoleId - FROM dbo.user_organisation uo - JOIN dbo.[user] u - ON u.sub = uo.user_id - JOIN dbo.organisation o - ON o.Id = uo.organisation_id - JOIN dbo.user_services us + ,CASE WHEN EXISTS ( + SELECT * + FROM dbo.user_services us2 + JOIN dbo.[service] s2 + ON s2.id = us2.service_id + JOIN dbo.[organisation] o2 + ON o2.Id = us2.organisation_id + WHERE + us2.organisation_id = uo.organisation_id + AND us2.user_id = uo.user_id + AND s2.clientId = {clientName} + AND o2.[Status] <> 0 + ) + THEN 1 ELSE 0 END AS IsInService + FROM dbo.[user] u + LEFT OUTER JOIN dbo.user_organisation uo + ON uo.user_id = u.sub + LEFT OUTER JOIN dbo.organisation o + ON o.Id = uo.organisation_id AND o.[Status] <> 0 + LEFT OUTER JOIN dbo.user_services us ON us.organisation_id = uo.organisation_id AND us.user_id = uo.user_id - LEFT JOIN dbo.[service] s + LEFT OUTER JOIN dbo.[service] s ON s.id = us.service_id - LEFT JOIN dbo.user_service_roles usr + LEFT OUTER JOIN dbo.user_service_roles usr ON usr.organisation_id = us.organisation_id AND usr.service_id = us.service_id AND usr.user_id = us.user_id - LEFT JOIN dbo.[Role] r + LEFT OUTER JOIN dbo.[Role] r ON r.Id = usr.role_id WHERE - uo.user_id = {userId} - AND - o.[status] <> 0 - AND EXISTS ( - SELECT 1 - FROM dbo.user_services us2 - JOIN dbo.[service] s2 - ON s2.id = us2.service_id - WHERE - us2.organisation_id = uo.organisation_id - AND us2.user_id = uo.user_id - AND s2.clientId = {clientName} - ); + u.sub = {userId}; """) .ToListAsync(cancellationToken); diff --git a/tests/Dfe.SignIn.PublicApi.UnitTests/MappingExtensions/GetUserOrganisationServiceMappingTests.cs b/tests/Dfe.SignIn.PublicApi.UnitTests/MappingExtensions/GetUserOrganisationServiceMappingTests.cs index 9b083675..7c9eab48 100644 --- a/tests/Dfe.SignIn.PublicApi.UnitTests/MappingExtensions/GetUserOrganisationServiceMappingTests.cs +++ b/tests/Dfe.SignIn.PublicApi.UnitTests/MappingExtensions/GetUserOrganisationServiceMappingTests.cs @@ -8,12 +8,13 @@ namespace Dfe.SignIn.PublicApi.UnitTests.MappingExtensions; [TestClass] public class GetUserOrganisationServiceMappingTests { - private static GetUserOrganisationService CreateModel( + private static GetUserOrganisationService CreateUserOrganisationServiceModel( Guid userId, Guid orgId, string? serviceName, string? roleName, - string? roleCode) + string? roleCode, + int inService = 1) { return new GetUserOrganisationService { UserId = userId, @@ -33,10 +34,33 @@ private static GetUserOrganisationService CreateModel( RoleName = roleName, RoleCode = roleCode, - OrgRoleId = OrganisationRoles.Approver.Id + OrgRoleId = OrganisationRoles.Approver.Id, + IsInService = inService }; } + [TestMethod] + public void ToUserDtos_WhenServiceNotFound_ReturnsSingleUserWithZeroOrganisations() + { + var userId = Guid.NewGuid(); + var orgId = Guid.NewGuid(); + + var models = new[] + { + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1", 0), + CreateUserOrganisationServiceModel(userId, orgId, "ServiceB", "Role2", "R2", 0) + }; + + var result = models.ToUserDtos().Single(); + + Assert.IsEmpty(result.Organisations); + Assert.AreEqual(userId, result.UserId); + Assert.AreEqual("test@test.com", result.Email); + Assert.AreEqual("Doe", result.FamilyName); + Assert.AreEqual("John", result.GivenName); + Assert.AreEqual(1, result.UserStatus); + } + [TestMethod] public void ToUserDtos_GroupsByUser_ReturnsSingleUser() { @@ -44,8 +68,8 @@ public void ToUserDtos_GroupsByUser_ReturnsSingleUser() var models = new[] { - CreateModel(userId, Guid.NewGuid(), "ServiceA", "Role1", "R1"), - CreateModel(userId, Guid.NewGuid(), "ServiceB", "Role2", "R2") + CreateUserOrganisationServiceModel(userId, Guid.NewGuid(), "ServiceA", "Role1", "R1"), + CreateUserOrganisationServiceModel(userId, Guid.NewGuid(), "ServiceB", "Role2", "R2") }; var result = models.ToUserDtos().ToList(); @@ -59,7 +83,7 @@ public void ToUserDtos_MapsUserFields_Correctly() { var userId = Guid.NewGuid(); - var model = CreateModel(userId, Guid.NewGuid(), "ServiceA", "Role1", "R1"); + var model = CreateUserOrganisationServiceModel(userId, Guid.NewGuid(), "ServiceA", "Role1", "R1"); var result = new[] { model }.ToUserDtos().Single(); @@ -77,8 +101,8 @@ public void ToOrganisationDtos_GroupsByOrganisation() var models = new[] { - CreateModel(userId, orgId, "ServiceA", "Role1", "R1"), - CreateModel(userId, orgId, "ServiceB", "Role2", "R2") + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"), + CreateUserOrganisationServiceModel(userId, orgId, "ServiceB", "Role2", "R2") }; var result = models.ToUserDtos().Single(); @@ -92,7 +116,7 @@ public void ToOrganisationDtos_MapsOrganisationFields() var userId = Guid.NewGuid(); var orgId = Guid.NewGuid(); - var model = CreateModel(userId, orgId, "ServiceA", "Role1", "R1"); + var model = CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"); var org = new[] { model } .ToUserDtos() @@ -114,8 +138,8 @@ public void ToServiceDtos_GroupsByServiceName() var models = new[] { - CreateModel(userId, orgId, "ServiceA", "Role1", "R1"), - CreateModel(userId, orgId, "ServiceA", "Role2", "R2") + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"), + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role2", "R2") }; var services = models @@ -136,8 +160,8 @@ public void ToServiceDtos_OrdersServices_ByName() var models = new[] { - CreateModel(userId, orgId, "BService", "Role1", "R1"), - CreateModel(userId, orgId, "AService", "Role1", "R1") + CreateUserOrganisationServiceModel(userId, orgId, "BService", "Role1", "R1"), + CreateUserOrganisationServiceModel(userId, orgId, "AService", "Role1", "R1") }; var services = models @@ -160,8 +184,8 @@ public void ToRoleDtos_RemovesDuplicateRoles() var models = new[] { - CreateModel(userId, orgId, "ServiceA", "Role1", "R1"), - CreateModel(userId, orgId, "ServiceA", "Role1", "R1") // duplicate + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"), + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1") // duplicate }; var roles = models @@ -184,8 +208,8 @@ public void ToRoleDtos_ExcludesNullRoleNames() var models = new[] { - CreateModel(userId, orgId, "ServiceA", null, "R1"), - CreateModel(userId, orgId, "ServiceA", "Role1", "R1") + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", null, "R1"), + CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1") }; var roles = models @@ -208,7 +232,7 @@ public void ToOrganisationDtos_ConvertsLegacyIdToString() var userId = Guid.NewGuid(); var orgId = Guid.NewGuid(); - var model = CreateModel(userId, orgId, "ServiceA", "Role1", "R1"); + var model = CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"); model.LegacyId = 12345; var org = new[] { model } @@ -226,7 +250,7 @@ public void ToOrganisationDtos_MapsOrgRoleName_WhenExists() var userId = Guid.NewGuid(); var orgId = Guid.NewGuid(); - var model = CreateModel(userId, orgId, "ServiceA", "Role1", "R1"); + var model = CreateUserOrganisationServiceModel(userId, orgId, "ServiceA", "Role1", "R1"); var org = new[] { model } .ToUserDtos()