From 057b0d3f751e41e4ebd82ae4e25530b072f3855d Mon Sep 17 00:00:00 2001 From: FrostyApeOne <78855469+FrostyApeOne@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:17:45 +0100 Subject: [PATCH] New Endpoint "getAssociatedPersonsWithtrust" (#621) * Added GetAllPersonsAssociatedWithTrust endpoint --- ...frastructureServiceCollectionExtensions.cs | 4 +- .../MopRepository.cs | 16 -- .../MstrContext.cs | 32 ++- .../MstrRepository.cs | 15 - .../EstablishmentQueryService.cs | 2 +- .../QueryServices/TrustQueryService.cs | 35 +++ .../Repositories/EstablishmentRepository.cs | 1 - .../Repositories/Repository.cs | 194 ------------- .../ApplicationServiceCollectionExtensions.cs | 8 +- .../Interfaces/IEstablishmentQueryService.cs | 2 +- .../Common/Interfaces/ITrustQueryService.cs | 9 + .../Models/AcademyGovernanceQueryModel.cs | 4 +- .../Common/Models/TrustGovernance.cs | 8 + .../Models/TrustGovernanceQueryModel.cs | 10 + .../GetMemberOfParliamentByConstituencies.cs | 4 +- .../GetMemberOfParliamentByConstituency.cs | 4 +- ...GetAllPersonsAssociatedWithAcademyByUrn.cs | 4 +- .../AcademyWithGovernanceProfile.cs | 3 +- .../TrustWithGovernanceProfile.cs | 23 ++ ...lPersonsAssociatedWithTrustByTrnOrUkprn.cs | 43 +++ ...ssociatedWithTrustByTrnOrUkprnValidator.cs | 19 ++ .../Common/BaseAggregateRoot.cs | 6 + Dfe.Academies.Domain/Common/IAggregateRoot.cs | 4 +- Dfe.Academies.Domain/Common/IEntity.cs | 6 +- .../Common/IStronglyTypedId.cs | 6 + Dfe.Academies.Domain/Common/ValueObject.cs | 45 --- .../Constituencies/Constituency.cs | 2 +- .../Interfaces/Repositories/IMopRepository.cs | 10 - .../Repositories/IMstrRepository.cs | 10 - .../Interfaces/Repositories/IRepository.cs | 235 ---------------- Dfe.Academies.Domain/Trust/TrustGovernance.cs | 20 ++ .../ValueObjects/ConstituencyId.cs | 34 +-- Dfe.Academies.Domain/ValueObjects/MemberId.cs | 34 +-- .../ValueObjects/NameDetails.cs | 25 +- Dfe.Academies.Utils/Enums/TrustIdType.cs | 9 + .../Helpers/IdentifierHelper.cs | 22 ++ .../Helpers/TrustIdValidator.cs | 26 ++ Dfe.PersonsApi.Client/Generated/Client.g.cs | 257 ++++++++++++++++++ .../Generated/Contracts.g.cs | 46 ++++ Dfe.PersonsApi.Client/Generated/swagger.json | 60 ++++ PersonsApi/Controllers/TrustsController.cs | 32 +++ PersonsApi/appsettings.Development.json | 3 + PersonsApi/appsettings.json | 6 +- .../Dfe.Academies.Application.Tests.csproj | 4 +- .../Helpers/IdentifierHelperTests.cs | 24 ++ ...rliamentByConstituencyQueryHandlerTests.cs | 6 +- ...amentsByConstituenciesQueryHandlerTests.cs | 6 +- ...lPersonsAssociatedWithAcademyByUrnTests.cs | 5 +- ...onsAssociatedWithTrustByTrnOrUkprnTests.cs | 71 +++++ .../Aggregates/ConstituencyTests.cs | 4 +- .../Aggregates/MemberContactDetailsTests.cs | 2 +- .../Dfe.Academies.Domain.Tests.csproj | 2 +- .../ConstituenciesControllerTests.cs | 6 +- .../EstablishmentsControllerTests.cs | 6 +- .../Controllers/TrustsControllerTests.cs | 83 ++++++ ...demies.PersonsApi.Tests.Integration.csproj | 3 +- .../OpenApiTests/OpenApiDocumentTests.cs | 8 +- .../Attributes/CustomAutoDataAttribute.cs | 5 +- .../InlineCustomAutoDataAttribute.cs | 2 +- .../Customizations/AutoMapperCustomization.cs | 3 +- ...pplicationDbContextFactoryCustomization.cs | 11 +- ...ustomWebApplicationFactoryCustomization.cs | 8 +- .../Customizations/DateOnlyCustomization.cs | 2 +- .../Customizations/DbContextCustomization.cs | 4 +- .../Entities/ConstituencyCustomization.cs | 2 +- .../Models/AcademyGovernanceCustomization.cs | 0 ...cademyGovernanceQueryModelCustomization.cs | 2 +- .../Models/MemberOfParliamentCustomization.cs | 0 .../Models/TrustGovernanceCustomization.cs | 28 ++ .../TrustGovernanceQueryModelCustomization.cs | 24 ++ .../NSubstituteCustomization.cs | 2 +- .../OmitCircularReferenceCustomization.cs | 2 +- .../Dfe.Academies.Tests.Common.csproj} | 0 .../Helpers/DbContextHelper.cs | 85 +++++- .../Helpers/FixtureFactoryHelper.cs | 2 +- .../CustomWebApplicationDbContextFactory.cs | 10 +- .../Mocks/CustomWebApplicationFactory.cs | 8 +- .../Mocks/MockJwtBearerHandler.cs | 2 +- TramsDataApi.sln | 14 +- 79 files changed, 1064 insertions(+), 720 deletions(-) delete mode 100644 Dfe.Academies.Api.Infrastructure/MopRepository.cs delete mode 100644 Dfe.Academies.Api.Infrastructure/MstrRepository.cs create mode 100644 Dfe.Academies.Api.Infrastructure/QueryServices/TrustQueryService.cs delete mode 100644 Dfe.Academies.Api.Infrastructure/Repositories/Repository.cs create mode 100644 Dfe.Academies.Application/Common/Interfaces/ITrustQueryService.cs create mode 100644 Dfe.Academies.Application/Common/Models/TrustGovernance.cs create mode 100644 Dfe.Academies.Application/Common/Models/TrustGovernanceQueryModel.cs create mode 100644 Dfe.Academies.Application/MappingProfiles/TrustWithGovernanceProfile.cs create mode 100644 Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprn.cs create mode 100644 Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator.cs create mode 100644 Dfe.Academies.Domain/Common/BaseAggregateRoot.cs create mode 100644 Dfe.Academies.Domain/Common/IStronglyTypedId.cs delete mode 100644 Dfe.Academies.Domain/Common/ValueObject.cs delete mode 100644 Dfe.Academies.Domain/Interfaces/Repositories/IMopRepository.cs delete mode 100644 Dfe.Academies.Domain/Interfaces/Repositories/IMstrRepository.cs delete mode 100644 Dfe.Academies.Domain/Interfaces/Repositories/IRepository.cs create mode 100644 Dfe.Academies.Domain/Trust/TrustGovernance.cs create mode 100644 Dfe.Academies.Utils/Enums/TrustIdType.cs create mode 100644 Dfe.Academies.Utils/Helpers/IdentifierHelper.cs create mode 100644 Dfe.Academies.Utils/Helpers/TrustIdValidator.cs create mode 100644 PersonsApi/Controllers/TrustsController.cs create mode 100644 Tests/Dfe.Academies.Application.Tests/Helpers/IdentifierHelperTests.cs create mode 100644 Tests/Dfe.Academies.Application.Tests/QueryHandlers/Trust/GetAllPersonsAssociatedWithTrustByTrnOrUkprnTests.cs create mode 100644 Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/TrustsControllerTests.cs rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Attributes/CustomAutoDataAttribute.cs (79%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Attributes/InlineCustomAutoDataAttribute.cs (81%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/AutoMapperCustomization.cs (92%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs (87%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/CustomWebApplicationFactoryCustomization.cs (92%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/DateOnlyCustomization.cs (85%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/DbContextCustomization.cs (87%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/Entities/ConstituencyCustomization.cs (94%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/Models/AcademyGovernanceCustomization.cs (100%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs (94%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/Models/MemberOfParliamentCustomization.cs (100%) create mode 100644 Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceCustomization.cs create mode 100644 Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceQueryModelCustomization.cs rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/NSubstituteCustomization.cs (83%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Customizations/OmitCircularReferenceCustomization.cs (87%) rename Tests/{Dfe.Academies.Testing.Common/Dfe.Academies.Testing.Common.csproj => Dfe.Academies.Tests.Common/Dfe.Academies.Tests.Common.csproj} (100%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Helpers/DbContextHelper.cs (63%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Helpers/FixtureFactoryHelper.cs (91%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Mocks/CustomWebApplicationDbContextFactory.cs (94%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Mocks/CustomWebApplicationFactory.cs (91%) rename Tests/{Dfe.Academies.Testing.Common => Dfe.Academies.Tests.Common}/Mocks/MockJwtBearerHandler.cs (95%) diff --git a/Dfe.Academies.Api.Infrastructure/InfrastructureServiceCollectionExtensions.cs b/Dfe.Academies.Api.Infrastructure/InfrastructureServiceCollectionExtensions.cs index 05363a197..1e10a1105 100644 --- a/Dfe.Academies.Api.Infrastructure/InfrastructureServiceCollectionExtensions.cs +++ b/Dfe.Academies.Api.Infrastructure/InfrastructureServiceCollectionExtensions.cs @@ -43,11 +43,11 @@ public static IServiceCollection AddPersonsApiInfrastructureDependencyGroup( services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(typeof(IMstrRepository<,>), typeof(MstrRepository<,>)); - services.AddScoped(typeof(IMopRepository<,>), typeof(MopRepository<,>)); // Query Services services.AddScoped(); + services.AddScoped(); + //Cache service services.Configure(config.GetSection("CacheSettings")); diff --git a/Dfe.Academies.Api.Infrastructure/MopRepository.cs b/Dfe.Academies.Api.Infrastructure/MopRepository.cs deleted file mode 100644 index 47a97574b..000000000 --- a/Dfe.Academies.Api.Infrastructure/MopRepository.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Dfe.Academies.Domain.Common; -using Dfe.Academies.Domain.Interfaces.Repositories; -using Dfe.Academies.Infrastructure.Repositories; - -namespace Dfe.Academies.Infrastructure -{ - [ExcludeFromCodeCoverage] - public class MopRepository(MopContext dbContext) - : Repository(dbContext), IMopRepository - where TAggregate : class, IAggregateRoot - where TId : ValueObject - { - - } -} diff --git a/Dfe.Academies.Api.Infrastructure/MstrContext.cs b/Dfe.Academies.Api.Infrastructure/MstrContext.cs index 19e700702..660816bd1 100644 --- a/Dfe.Academies.Api.Infrastructure/MstrContext.cs +++ b/Dfe.Academies.Api.Infrastructure/MstrContext.cs @@ -28,6 +28,7 @@ public MstrContext(DbContextOptions options) : base(options) public DbSet IfdPipelines { get; set; } = null!; public DbSet GovernanceRoleTypes { get; set; } = null!; public DbSet EducationEstablishmentGovernances { get; set; } = null!; + public DbSet TrustGovernances { get; set; } = null!; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -57,6 +58,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(ConfigureGovernanceRoleType); modelBuilder.Entity().Metadata.SetIsTableExcludedFromMigrations(false); + + modelBuilder.Entity(ConfigureTrustGovernance); + modelBuilder.Entity().Metadata.SetIsTableExcludedFromMigrations(false); } else { @@ -65,6 +69,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(ConfigureGovernanceRoleType); modelBuilder.Entity().Metadata.SetIsTableExcludedFromMigrations(true); + + modelBuilder.Entity(ConfigureTrustGovernance); + modelBuilder.Entity().Metadata.SetIsTableExcludedFromMigrations(true); } base.OnModelCreating(modelBuilder); @@ -338,7 +345,6 @@ private static void ConfigureEducationEstablishmentGovernance(EntityTypeBuilder< } - private static void ConfigureGovernanceRoleType(EntityTypeBuilder governanceRoleTypeConfiguration) { governanceRoleTypeConfiguration.HasKey(e => e.SK); @@ -351,4 +357,28 @@ private static void ConfigureGovernanceRoleType(EntityTypeBuilder e.ModifiedBy).HasColumnName("Modified By"); } + + private static void ConfigureTrustGovernance(EntityTypeBuilder trustGovernanceConfiguration) + { + trustGovernanceConfiguration.HasKey(e => e.SK); + trustGovernanceConfiguration.ToTable("TrustGovernance", DEFAULT_SCHEMA); + trustGovernanceConfiguration.Property(e => e.SK).HasColumnName("SK"); + trustGovernanceConfiguration.Property(e => e.TrustId).HasColumnName("FK_Trust"); + trustGovernanceConfiguration.Property(e => e.GovernanceRoleTypeId).HasColumnName("FK_GovernanceRoleType"); + trustGovernanceConfiguration.Property(e => e.GID) + .HasColumnName("GID") + .IsRequired() + .HasMaxLength(100); + + trustGovernanceConfiguration.Property(e => e.Title).HasColumnName("Title"); + trustGovernanceConfiguration.Property(e => e.Forename1).HasColumnName("Forename1"); + trustGovernanceConfiguration.Property(e => e.Forename2).HasColumnName("Forename2"); + trustGovernanceConfiguration.Property(e => e.Surname).HasColumnName("Surname"); + trustGovernanceConfiguration.Property(e => e.Email).HasColumnName("Email").HasMaxLength(200); + trustGovernanceConfiguration.Property(e => e.DateOfAppointment).HasColumnName("Date of appointment"); + trustGovernanceConfiguration.Property(e => e.DateTermOfOfficeEndsOrEnded).HasColumnName("Date term of office ends/ended"); + trustGovernanceConfiguration.Property(e => e.AppointingBody).HasColumnName("Appointing body"); + trustGovernanceConfiguration.Property(e => e.Modified).HasColumnName("Modified"); + trustGovernanceConfiguration.Property(e => e.ModifiedBy).HasColumnName("Modified By"); + } } \ No newline at end of file diff --git a/Dfe.Academies.Api.Infrastructure/MstrRepository.cs b/Dfe.Academies.Api.Infrastructure/MstrRepository.cs deleted file mode 100644 index 9f84cd13e..000000000 --- a/Dfe.Academies.Api.Infrastructure/MstrRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Dfe.Academies.Infrastructure.Repositories; -using Dfe.Academies.Domain.Interfaces.Repositories; -using Dfe.Academies.Domain.Common; -using System.Diagnostics.CodeAnalysis; - -namespace Dfe.Academies.Infrastructure -{ - [ExcludeFromCodeCoverage] - public class MstrRepository(MstrContext dbContext) - : Repository(dbContext), IMstrRepository - where TAggregate : class, IAggregateRoot - where TId : ValueObject - { - } -} diff --git a/Dfe.Academies.Api.Infrastructure/QueryServices/EstablishmentQueryService.cs b/Dfe.Academies.Api.Infrastructure/QueryServices/EstablishmentQueryService.cs index daf753c9e..b220462cb 100644 --- a/Dfe.Academies.Api.Infrastructure/QueryServices/EstablishmentQueryService.cs +++ b/Dfe.Academies.Api.Infrastructure/QueryServices/EstablishmentQueryService.cs @@ -1,5 +1,5 @@ using Dfe.Academies.Application.Common.Interfaces; -using Dfe.Academies.Infrastructure.Models; +using Dfe.Academies.Application.Common.Models; using Microsoft.EntityFrameworkCore; namespace Dfe.Academies.Infrastructure.QueryServices diff --git a/Dfe.Academies.Api.Infrastructure/QueryServices/TrustQueryService.cs b/Dfe.Academies.Api.Infrastructure/QueryServices/TrustQueryService.cs new file mode 100644 index 000000000..43ac2d5e3 --- /dev/null +++ b/Dfe.Academies.Api.Infrastructure/QueryServices/TrustQueryService.cs @@ -0,0 +1,35 @@ +using Dfe.Academies.Application.Common.Interfaces; +using Dfe.Academies.Application.Common.Models; +using Microsoft.EntityFrameworkCore; + +namespace Dfe.Academies.Infrastructure.QueryServices +{ + internal class TrustQueryService(MstrContext context) : ITrustQueryService + { + public IQueryable? GetTrustGovernanceByGroupIdOrUkprn(string? groupId, string? ukPrn) + { + + // Check if the trust exists based on GroupID or UKPRN + var trustExists = context.Trusts.AsNoTracking().Any(t => + (!string.IsNullOrEmpty(groupId) && t.GroupID == groupId) || + (!string.IsNullOrEmpty(ukPrn) && t.UKPRN == ukPrn)); + + if (!trustExists) + { + return null; + } + + var query = from t in context.Trusts.AsNoTracking() + join tg in context.TrustGovernances.AsNoTracking() + on t.SK equals tg.TrustId + join grt in context.GovernanceRoleTypes.AsNoTracking() + on tg.GovernanceRoleTypeId equals grt.SK + where (!string.IsNullOrEmpty(groupId) && t.GroupID == groupId) || + (!string.IsNullOrEmpty(ukPrn) && t.UKPRN == ukPrn) + select new TrustGovernanceQueryModel(t, grt, tg); + + return query; + } + } + +} diff --git a/Dfe.Academies.Api.Infrastructure/Repositories/EstablishmentRepository.cs b/Dfe.Academies.Api.Infrastructure/Repositories/EstablishmentRepository.cs index 452a86368..363935275 100644 --- a/Dfe.Academies.Api.Infrastructure/Repositories/EstablishmentRepository.cs +++ b/Dfe.Academies.Api.Infrastructure/Repositories/EstablishmentRepository.cs @@ -2,7 +2,6 @@ using Dfe.Academies.Domain.Establishment; using Dfe.Academies.Domain.Interfaces.Repositories; using Microsoft.EntityFrameworkCore; -using Dfe.Academies.Infrastructure.Models; namespace Dfe.Academies.Infrastructure.Repositories { diff --git a/Dfe.Academies.Api.Infrastructure/Repositories/Repository.cs b/Dfe.Academies.Api.Infrastructure/Repositories/Repository.cs deleted file mode 100644 index 8dd48f956..000000000 --- a/Dfe.Academies.Api.Infrastructure/Repositories/Repository.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Dfe.Academies.Domain.Common; -using Dfe.Academies.Domain.Interfaces.Repositories; -using Microsoft.EntityFrameworkCore; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -namespace Dfe.Academies.Infrastructure.Repositories -{ -#pragma warning disable CS8603, S2436 - - [ExcludeFromCodeCoverage] - public abstract class Repository : IRepository - where TAggregate : class, IAggregateRoot - where TId : ValueObject - where TDbContext : DbContext - { - /// - /// The - /// - protected readonly TDbContext DbContext; - - /// Constructor - /// - protected Repository(TDbContext dbContext) => this.DbContext = dbContext; - - /// Shorthand for _dbContext.Set - /// - protected virtual DbSet DbSet() - { - return this.DbContext.Set(); - } - - /// - public virtual IQueryable Query() => (IQueryable)this.DbSet(); - - /// - public virtual ICollection Fetch(Expression> predicate) - { - return (ICollection)((IQueryable)this.DbSet()).Where(predicate).ToList(); - } - - /// - public virtual async Task> FetchAsync( - Expression> predicate, - CancellationToken cancellationToken = default(CancellationToken)) - { - return (ICollection)await EntityFrameworkQueryableExtensions.ToListAsync(((IQueryable)this.DbSet()).Where(predicate), cancellationToken); - } - - /// - public virtual TAggregate Find(params TId[] keyValues) => this.DbSet().Find(keyValues); - - /// - public virtual TAggregate Find(Expression> predicate) - { - return ((IQueryable)this.DbSet()).FirstOrDefault(predicate); - } - - /// - public virtual async Task FindAsync(params TId[] keyValues) - { - return await this.DbSet().FindAsync(keyValues); - } - - /// - public virtual async Task FindAsync( - Expression> predicate, - CancellationToken cancellationToken = default(CancellationToken)) - { - return await EntityFrameworkQueryableExtensions.FirstOrDefaultAsync((IQueryable)this.DbSet(), predicate, cancellationToken); - } - - /// - public virtual TAggregate Get(Expression> predicate) - { - return ((IQueryable)this.DbSet()).Single(predicate); - } - - /// - public virtual TAggregate Get(params TId[] keyValues) - { - return this.Find(keyValues) ?? throw new InvalidOperationException( - $"Entity type {(object)typeof(TAggregate)} is null for primary key {(object)keyValues}"); - } - - /// - public virtual async Task GetAsync(Expression> predicate) - { - return await EntityFrameworkQueryableExtensions.SingleAsync((IQueryable)this.DbSet(), predicate, new CancellationToken()); - } - - /// - public virtual async Task GetAsync(params TId[] keyValues) - { - return await this.FindAsync(keyValues) ?? throw new InvalidOperationException( - $"Entity type {(object)typeof(TAggregate)} is null for primary key {(object)keyValues}"); - } - - /// - public virtual TAggregate Add(TAggregate entity) - { - this.DbContext.Add(entity); - this.DbContext.SaveChanges(); - return entity; - } - - /// - public virtual async Task AddAsync(TAggregate entity, CancellationToken cancellationToken = default(CancellationToken)) - { - await this.DbContext.AddAsync(entity, cancellationToken); - await this.DbContext.SaveChangesAsync(cancellationToken); - return entity; - } - - /// - public virtual IEnumerable AddRange(ICollection entities) - { - this.DbContext.AddRange((IEnumerable)entities); - this.DbContext.SaveChanges(); - return (IEnumerable)entities; - } - - /// - public virtual async Task> AddRangeAsync( - ICollection entities, - CancellationToken cancellationToken = default(CancellationToken)) - { - await this.DbContext.AddRangeAsync((IEnumerable)entities, cancellationToken); - await this.DbContext.SaveChangesAsync(cancellationToken); - return (IEnumerable)entities; - } - - /// - public virtual TAggregate Remove(TAggregate entity) - { - this.DbContext.Remove(entity); - this.DbContext.SaveChanges(); - return entity; - } - - /// - public virtual async Task RemoveAsync( - TAggregate entity, - CancellationToken cancellationToken = default(CancellationToken)) - { - this.DbContext.Remove(entity); - await this.DbContext.SaveChangesAsync(cancellationToken); - return entity; - } - - /// - public virtual int Delete(Expression> predicate) - { - return DbSet().Where(predicate).ExecuteDelete(); - } - - /// - public virtual IEnumerable RemoveRange(ICollection entities) - { - this.DbSet().RemoveRange((IEnumerable)entities); - this.DbContext.SaveChanges(); - return (IEnumerable)entities; - } - - /// - public virtual async Task> RemoveRangeAsync( - ICollection entities, - CancellationToken cancellationToken = default(CancellationToken)) - { - this.DbSet().RemoveRange((IEnumerable)entities); - await this.DbContext.SaveChangesAsync(cancellationToken); - return (IEnumerable)entities; - } - - /// - public virtual TAggregate Update(TAggregate entity) - { - this.DbContext.Update(entity); - this.DbContext.SaveChanges(); - return entity; - } - - /// - public virtual async Task UpdateAsync( - TAggregate entity, - CancellationToken cancellationToken = default(CancellationToken)) - { - this.DbContext.Update(entity); - await this.DbContext.SaveChangesAsync(cancellationToken); - return entity; - } - } -#pragma warning restore CS8603, S2436 -} diff --git a/Dfe.Academies.Application/ApplicationServiceCollectionExtensions.cs b/Dfe.Academies.Application/ApplicationServiceCollectionExtensions.cs index b25294557..d15690807 100644 --- a/Dfe.Academies.Application/ApplicationServiceCollectionExtensions.cs +++ b/Dfe.Academies.Application/ApplicationServiceCollectionExtensions.cs @@ -26,6 +26,8 @@ public static IServiceCollection AddApplicationDependencyGroup( public static IServiceCollection AddPersonsApiApplicationDependencyGroup( this IServiceCollection services, IConfiguration config) { + var performanceLoggingEnabled = config.GetValue("Features:PerformanceLoggingEnabled"); + services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddMediatR(cfg => @@ -33,7 +35,11 @@ public static IServiceCollection AddPersonsApiApplicationDependencyGroup( cfg.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly()); cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>)); cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); - cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>)); + + if (performanceLoggingEnabled) + { + cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(PerformanceBehaviour<,>)); + } }); return services; diff --git a/Dfe.Academies.Application/Common/Interfaces/IEstablishmentQueryService.cs b/Dfe.Academies.Application/Common/Interfaces/IEstablishmentQueryService.cs index c845c6308..502ea1353 100644 --- a/Dfe.Academies.Application/Common/Interfaces/IEstablishmentQueryService.cs +++ b/Dfe.Academies.Application/Common/Interfaces/IEstablishmentQueryService.cs @@ -1,4 +1,4 @@ -using Dfe.Academies.Infrastructure.Models; +using Dfe.Academies.Application.Common.Models; namespace Dfe.Academies.Application.Common.Interfaces { diff --git a/Dfe.Academies.Application/Common/Interfaces/ITrustQueryService.cs b/Dfe.Academies.Application/Common/Interfaces/ITrustQueryService.cs new file mode 100644 index 000000000..3575019a3 --- /dev/null +++ b/Dfe.Academies.Application/Common/Interfaces/ITrustQueryService.cs @@ -0,0 +1,9 @@ +using Dfe.Academies.Application.Common.Models; + +namespace Dfe.Academies.Application.Common.Interfaces +{ + public interface ITrustQueryService + { + IQueryable? GetTrustGovernanceByGroupIdOrUkprn(string? groupId, string? ukPrn); + } +} diff --git a/Dfe.Academies.Application/Common/Models/AcademyGovernanceQueryModel.cs b/Dfe.Academies.Application/Common/Models/AcademyGovernanceQueryModel.cs index d53cea071..db01184ab 100644 --- a/Dfe.Academies.Application/Common/Models/AcademyGovernanceQueryModel.cs +++ b/Dfe.Academies.Application/Common/Models/AcademyGovernanceQueryModel.cs @@ -1,10 +1,10 @@ using Dfe.Academies.Domain.Establishment; -namespace Dfe.Academies.Infrastructure.Models +namespace Dfe.Academies.Application.Common.Models { public record AcademyGovernanceQueryModel( EducationEstablishmentGovernance EducationEstablishmentGovernance, GovernanceRoleType GovernanceRoleType, - Establishment Establishment + Domain.Establishment.Establishment Establishment ); } diff --git a/Dfe.Academies.Application/Common/Models/TrustGovernance.cs b/Dfe.Academies.Application/Common/Models/TrustGovernance.cs new file mode 100644 index 000000000..5fcc06a06 --- /dev/null +++ b/Dfe.Academies.Application/Common/Models/TrustGovernance.cs @@ -0,0 +1,8 @@ +namespace Dfe.Academies.Application.Common.Models +{ + public class TrustGovernance : Person + { + public string? UKPRN { get; set; } + public string? TRN { get; set; } + } +} diff --git a/Dfe.Academies.Application/Common/Models/TrustGovernanceQueryModel.cs b/Dfe.Academies.Application/Common/Models/TrustGovernanceQueryModel.cs new file mode 100644 index 000000000..fcfae95fe --- /dev/null +++ b/Dfe.Academies.Application/Common/Models/TrustGovernanceQueryModel.cs @@ -0,0 +1,10 @@ +using Dfe.Academies.Domain.Establishment; + +namespace Dfe.Academies.Application.Common.Models +{ + public record TrustGovernanceQueryModel( + Domain.Trust.Trust Trust, + GovernanceRoleType GovernanceRoleType, + Domain.Trust.TrustGovernance TrustGovernance + ); +} diff --git a/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituencies/GetMemberOfParliamentByConstituencies.cs b/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituencies/GetMemberOfParliamentByConstituencies.cs index 6512845a7..4b3820c89 100644 --- a/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituencies/GetMemberOfParliamentByConstituencies.cs +++ b/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituencies/GetMemberOfParliamentByConstituencies.cs @@ -21,8 +21,6 @@ public async Task> Handle(GetMembersOfParliamentByConst { var cacheKey = $"MemberOfParliament_{CacheKeyHelper.GenerateHashedCacheKey(request.ConstituencyNames)}"; - var methodName = nameof(GetMembersOfParliamentByConstituenciesQueryHandler); - return await cacheService.GetOrAddAsync(cacheKey, async () => { var constituenciesQuery = constituencyRepository @@ -33,7 +31,7 @@ public async Task> Handle(GetMembersOfParliamentByConst .ToListAsync(cancellationToken); return membersOfParliament; - }, methodName); + }, nameof(GetMembersOfParliamentByConstituenciesQueryHandler)); } } diff --git a/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituency/GetMemberOfParliamentByConstituency.cs b/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituency/GetMemberOfParliamentByConstituency.cs index deb29fe9b..a61ab6543 100644 --- a/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituency/GetMemberOfParliamentByConstituency.cs +++ b/Dfe.Academies.Application/Constituencies/Queries/GetMemberOfParliamentByConstituency/GetMemberOfParliamentByConstituency.cs @@ -19,8 +19,6 @@ public class GetMemberOfParliamentByConstituencyQueryHandler( { var cacheKey = $"MemberOfParliament_{CacheKeyHelper.GenerateHashedCacheKey(request.ConstituencyName)}"; - var methodName = nameof(GetMemberOfParliamentByConstituencyQueryHandler); - return await cacheService.GetOrAddAsync(cacheKey, async () => { var constituencyWithMember = await constituencyRepository @@ -29,7 +27,7 @@ public class GetMemberOfParliamentByConstituencyQueryHandler( var result = mapper.Map(constituencyWithMember); return result; - }, methodName); + }, nameof(GetMemberOfParliamentByConstituencyQueryHandler)); } } } diff --git a/Dfe.Academies.Application/Establishment/Queries/GetAllPersonsAssociatedWithAcademyByUrn/GetAllPersonsAssociatedWithAcademyByUrn.cs b/Dfe.Academies.Application/Establishment/Queries/GetAllPersonsAssociatedWithAcademyByUrn/GetAllPersonsAssociatedWithAcademyByUrn.cs index e3d9e1339..581207f6c 100644 --- a/Dfe.Academies.Application/Establishment/Queries/GetAllPersonsAssociatedWithAcademyByUrn/GetAllPersonsAssociatedWithAcademyByUrn.cs +++ b/Dfe.Academies.Application/Establishment/Queries/GetAllPersonsAssociatedWithAcademyByUrn/GetAllPersonsAssociatedWithAcademyByUrn.cs @@ -21,8 +21,6 @@ public class GetAllPersonsAssociatedWithAcademyByUrnQueryHandler( { var cacheKey = $"PersonsAssociatedWithAcademy_{CacheKeyHelper.GenerateHashedCacheKey(request.Urn.ToString())}"; - var methodName = nameof(GetAllPersonsAssociatedWithAcademyByUrnQueryHandler); - return await cacheService.GetOrAddAsync(cacheKey, async () => { var query = establishmentQueryService.GetPersonsAssociatedWithAcademyByUrn(request.Urn); @@ -37,7 +35,7 @@ public class GetAllPersonsAssociatedWithAcademyByUrnQueryHandler( .ToListAsync(cancellationToken); return result; - }, methodName); + }, nameof(GetAllPersonsAssociatedWithAcademyByUrnQueryHandler)); } } } diff --git a/Dfe.Academies.Application/MappingProfiles/AcademyWithGovernanceProfile.cs b/Dfe.Academies.Application/MappingProfiles/AcademyWithGovernanceProfile.cs index 6ef42e8e2..395c8071f 100644 --- a/Dfe.Academies.Application/MappingProfiles/AcademyWithGovernanceProfile.cs +++ b/Dfe.Academies.Application/MappingProfiles/AcademyWithGovernanceProfile.cs @@ -1,6 +1,5 @@ using AutoMapper; using Dfe.Academies.Application.Common.Models; -using Dfe.Academies.Infrastructure.Models; namespace Dfe.Academies.Application.MappingProfiles { @@ -15,7 +14,7 @@ public AcademyWithGovernanceProfile() .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.EducationEstablishmentGovernance.Email)) .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => $"{src.EducationEstablishmentGovernance.Forename1} {src.EducationEstablishmentGovernance.Surname}")) .ForMember(dest => dest.DisplayNameWithTitle, opt => opt.MapFrom(src => $"{src.EducationEstablishmentGovernance.Title} {src.EducationEstablishmentGovernance.Forename1} {src.EducationEstablishmentGovernance.Surname}")) - .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => new List { src.GovernanceRoleType.Name })) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => new List { src.GovernanceRoleType.Name })) .ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => src.EducationEstablishmentGovernance.Modified)); } } diff --git a/Dfe.Academies.Application/MappingProfiles/TrustWithGovernanceProfile.cs b/Dfe.Academies.Application/MappingProfiles/TrustWithGovernanceProfile.cs new file mode 100644 index 000000000..90e3f329d --- /dev/null +++ b/Dfe.Academies.Application/MappingProfiles/TrustWithGovernanceProfile.cs @@ -0,0 +1,23 @@ +using AutoMapper; +using Dfe.Academies.Application.Common.Models; + +namespace Dfe.Academies.Application.MappingProfiles +{ + public class TrustWithGovernanceProfile : Profile + { + public TrustWithGovernanceProfile() + { + CreateMap() + .ForMember(dest => dest.Id, opt => opt.MapFrom(src => (int)src.TrustGovernance.SK)) + .ForMember(dest => dest.UKPRN, opt => opt.MapFrom(src => src.Trust.UKPRN)) + .ForMember(dest => dest.TRN, opt => opt.MapFrom(src => src.Trust.GroupID)) + .ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.TrustGovernance.Forename1)) + .ForMember(dest => dest.LastName, opt => opt.MapFrom(src => src.TrustGovernance.Surname)) + .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.TrustGovernance.Email)) + .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => $"{src.TrustGovernance.Forename1} {src.TrustGovernance.Surname}")) + .ForMember(dest => dest.DisplayNameWithTitle, opt => opt.MapFrom(src => $"{src.TrustGovernance.Title} {src.TrustGovernance.Forename1} {src.TrustGovernance.Surname}")) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => new List { src.GovernanceRoleType.Name })) + .ForMember(dest => dest.UpdatedAt, opt => opt.MapFrom(src => src.TrustGovernance.Modified)); + } + } +} \ No newline at end of file diff --git a/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprn.cs b/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprn.cs new file mode 100644 index 000000000..5b1a268e7 --- /dev/null +++ b/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprn.cs @@ -0,0 +1,43 @@ +using AutoMapper; +using AutoMapper.QueryableExtensions; +using Dfe.Academies.Application.Common.Interfaces; +using Dfe.Academies.Application.Common.Models; +using Dfe.Academies.Domain.Interfaces.Caching; +using Dfe.Academies.Utils.Caching; +using Dfe.Academies.Utils.Enums; +using Dfe.Academies.Utils.Helpers; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Dfe.Academies.Application.Trust.Queries.GetAllPersonsAssociatedWithTrustByTrnOrUkprn +{ + public record GetAllPersonsAssociatedWithTrustByTrnOrUkprnQuery(string Id) : IRequest?>; + + public class GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandler( + ITrustQueryService trustQueryService, + IMapper mapper, + ICacheService cacheService) + : IRequestHandler?> + { + public async Task?> Handle(GetAllPersonsAssociatedWithTrustByTrnOrUkprnQuery request, CancellationToken cancellationToken) + { + var idType = IdentifierHelper.DetermineIdType(request.Id, TrustIdValidator.GetTrustIdValidators()); + + var groupId = idType == TrustIdType.Trn ? request.Id : null; + var ukPrn = idType == TrustIdType.UkPrn ? request.Id : null; + + var cacheKey = $"PersonsAssociatedWithTrust_{CacheKeyHelper.GenerateHashedCacheKey(request.Id)}"; + + return await cacheService.GetOrAddAsync(cacheKey, async () => + { + var query = trustQueryService.GetTrustGovernanceByGroupIdOrUkprn(groupId, ukPrn); + + return query == null + ? null + : await query + .ProjectTo(mapper.ConfigurationProvider) + .ToListAsync(cancellationToken); + }, nameof(GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandler)); + } + } +} diff --git a/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator.cs b/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator.cs new file mode 100644 index 000000000..d2401c665 --- /dev/null +++ b/Dfe.Academies.Application/Trust/Queries/GetAllPersonsAssociatedWithTrustByTrnOrUkprn/GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator.cs @@ -0,0 +1,19 @@ +using Dfe.Academies.Application.Establishment.Queries.GetAllPersonsAssociatedWithAcademyByUrn; +using Dfe.Academies.Utils.Enums; +using Dfe.Academies.Utils.Helpers; +using FluentValidation; + +namespace Dfe.Academies.Application.Trust.Queries.GetAllPersonsAssociatedWithTrustByTrnOrUkprn +{ + public class GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator : AbstractValidator + { + public GetAllPersonsAssociatedWithTrustByTrnOrUkprnValidator() + { + RuleFor(query => query.Id) + .NotEmpty().WithMessage("An identifier must be provided.") + .Must(id => TrustIdValidator.GetTrustIdValidators()[TrustIdType.Trn](id) + || TrustIdValidator.GetTrustIdValidators()[TrustIdType.UkPrn](id)) + .WithMessage("The identifier must be either a valid TRN (TR{5 digits}) or a valid UKPRN (8 digits)."); + } + } +} \ No newline at end of file diff --git a/Dfe.Academies.Domain/Common/BaseAggregateRoot.cs b/Dfe.Academies.Domain/Common/BaseAggregateRoot.cs new file mode 100644 index 000000000..05c257052 --- /dev/null +++ b/Dfe.Academies.Domain/Common/BaseAggregateRoot.cs @@ -0,0 +1,6 @@ +namespace Dfe.Academies.Domain.Common +{ + public abstract class BaseAggregateRoot : IAggregateRoot + { + } +} diff --git a/Dfe.Academies.Domain/Common/IAggregateRoot.cs b/Dfe.Academies.Domain/Common/IAggregateRoot.cs index c132bfe5f..ee8c8a7f0 100644 --- a/Dfe.Academies.Domain/Common/IAggregateRoot.cs +++ b/Dfe.Academies.Domain/Common/IAggregateRoot.cs @@ -1,6 +1,6 @@ -namespace Dfe.Academies.Domain.Common +namespace Dfe.Academies.Domain.Common { - public interface IAggregateRoot : IEntity where TId : ValueObject + public interface IAggregateRoot { } } diff --git a/Dfe.Academies.Domain/Common/IEntity.cs b/Dfe.Academies.Domain/Common/IEntity.cs index 451911fb6..c8e63d11e 100644 --- a/Dfe.Academies.Domain/Common/IEntity.cs +++ b/Dfe.Academies.Domain/Common/IEntity.cs @@ -1,7 +1,7 @@ -namespace Dfe.Academies.Domain.Common +namespace Dfe.Academies.Domain.Common { - public interface IEntity where TId : ValueObject + public interface IEntity where TId : IStronglyTypedId { - TId Id { get; } + TId? Id { get; } } } diff --git a/Dfe.Academies.Domain/Common/IStronglyTypedId.cs b/Dfe.Academies.Domain/Common/IStronglyTypedId.cs new file mode 100644 index 000000000..461473c5e --- /dev/null +++ b/Dfe.Academies.Domain/Common/IStronglyTypedId.cs @@ -0,0 +1,6 @@ +namespace Dfe.Academies.Domain.Common +{ + public interface IStronglyTypedId + { + } +} diff --git a/Dfe.Academies.Domain/Common/ValueObject.cs b/Dfe.Academies.Domain/Common/ValueObject.cs deleted file mode 100644 index 1494d5014..000000000 --- a/Dfe.Academies.Domain/Common/ValueObject.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Dfe.Academies.Domain.Common -{ - public abstract class ValueObject - { - protected static bool EqualOperator(ValueObject left, ValueObject right) - { - if (left is null ^ right is null) - { - return false; - } - - return left?.Equals(right!) != false; - } - - protected static bool NotEqualOperator(ValueObject left, ValueObject right) - { - return !(EqualOperator(left, right)); - } - - protected abstract IEnumerable GetEqualityComponents(); - - public override bool Equals(object? obj) - { - if (obj == null || obj.GetType() != GetType()) - { - return false; - } - - var other = (ValueObject)obj; - return GetEqualityComponents().SequenceEqual(other.GetEqualityComponents()); - } - - public override int GetHashCode() - { - var hash = new HashCode(); - - foreach (var component in GetEqualityComponents()) - { - hash.Add(component); - } - - return hash.ToHashCode(); - } - } -} diff --git a/Dfe.Academies.Domain/Constituencies/Constituency.cs b/Dfe.Academies.Domain/Constituencies/Constituency.cs index 79347628f..834f4452e 100644 --- a/Dfe.Academies.Domain/Constituencies/Constituency.cs +++ b/Dfe.Academies.Domain/Constituencies/Constituency.cs @@ -4,7 +4,7 @@ namespace Dfe.Academies.Domain.Constituencies { #pragma warning disable CS8618 - public class Constituency : IAggregateRoot + public class Constituency : IAggregateRoot { public ConstituencyId Id { get; } public MemberId MemberId { get; private set; } diff --git a/Dfe.Academies.Domain/Interfaces/Repositories/IMopRepository.cs b/Dfe.Academies.Domain/Interfaces/Repositories/IMopRepository.cs deleted file mode 100644 index cd019be63..000000000 --- a/Dfe.Academies.Domain/Interfaces/Repositories/IMopRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Dfe.Academies.Domain.Common; - -namespace Dfe.Academies.Domain.Interfaces.Repositories -{ - public interface IMopRepository : IRepository - where TAggregate : class, IAggregateRoot - where TId : ValueObject - { - } -} diff --git a/Dfe.Academies.Domain/Interfaces/Repositories/IMstrRepository.cs b/Dfe.Academies.Domain/Interfaces/Repositories/IMstrRepository.cs deleted file mode 100644 index dc8058a61..000000000 --- a/Dfe.Academies.Domain/Interfaces/Repositories/IMstrRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Dfe.Academies.Domain.Common; - -namespace Dfe.Academies.Domain.Interfaces.Repositories -{ - public interface IMstrRepository : IRepository - where TAggregate : IAggregateRoot - where TId : ValueObject - { - } -} diff --git a/Dfe.Academies.Domain/Interfaces/Repositories/IRepository.cs b/Dfe.Academies.Domain/Interfaces/Repositories/IRepository.cs deleted file mode 100644 index ca50d764d..000000000 --- a/Dfe.Academies.Domain/Interfaces/Repositories/IRepository.cs +++ /dev/null @@ -1,235 +0,0 @@ -using Dfe.Academies.Domain.Common; -using System.Linq.Expressions; - -namespace Dfe.Academies.Domain.Interfaces.Repositories -{ - /// Repository - /// - /// - public interface IRepository - where TAggregate : IAggregateRoot - where TId : ValueObject - { - /// Returns a queryable (un-resolved!!!!) list of objects. - /// Do not expose IQueryable outside of the domain layer - IQueryable Query(); - - /// - /// Returns an enumerated (resolved!) list of objects based on known query predicate. - /// - /// - /// - /// We know that its the same as doing IQueryable`T.Where(p=> p.value=x).Enumerate but this limits the repo to only ever returning resolved lists. - ICollection Fetch(Expression> predicate); - - /// - /// Asynchronously returns an enumerated (resolved!) list of objects based on known query predicate. - /// - /// - /// - /// - /// We know that its the same as doing IQueryable`T.Where(p=> p.value=x).Enumerate but this limits the repo to only ever returning resolved lists. - Task> FetchAsync( - Expression> predicate, - CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Finds an entity with the given primary key value. If an entity with the - /// given primary key values exists in the context, then it is returned immediately - /// without making a request to the store. Otherwise, a request is made to the - /// store for an entity with the given primary key value and this entity, if - /// found, is attached to the context and returned. If no entity is found in - /// the context or the store, then null is returned. - /// - /// The key values. - /// The entity found, or null. - TAggregate Find(params TId[] keyValues); - - /// - /// Returns the first entity of a sequence that satisfies a specified condition - /// or a default value if no such entity is found. - /// - /// A function to test an entity for a condition - /// The entity found, or null - TAggregate Find(Expression> predicate); - - /// - /// Asynchronously finds an entity with the given primary key value. If an entity with the - /// given primary key values exists in the context, then it is returned immediately - /// without making a request to the store. Otherwise, a request is made to the - /// store for an entity with the given primary key value and this entity, if - /// found, is attached to the context and returned. If no entity is found in - /// the context or the store, then null is returned. - /// - /// The key values. - /// The entity found, or null. - Task FindAsync(params TId[] keyValues); - - /// - /// Asynchronously returns the first entity of a sequence that satisfies a specified condition - /// or a default value if no such entity is found. - /// - /// A function to test an entity for a condition - /// - /// The entity found, or null - Task FindAsync( - Expression> predicate, - CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Gets an entity with the given primary key value. If an entity with the - /// given primary key values exists in the context, then it is returned immediately - /// without making a request to the store. Otherwise, a request is made to the - /// store for an entity with the given primary key value and this entity, if - /// found, is attached to the context and returned. If no entity is found in - /// the context or the store -or- more than one entity is found, then an - /// InvalidOperationException is thrown. - /// - /// The key values. - /// The entity found - /// If no entity is found in the context or the store -or- more than one entity is found, then an - TAggregate Get(params TId[] keyValues); - - /// - /// Gets an entity that satisfies a specified condition, - /// and throws an exception if more than one such element exists. - /// - /// A function to test an element for a condition. - /// - /// No entity satisfies the condition in predicate. -or- More than one entity satisfies the condition in predicate. -or- The source sequence is empty. - /// - TAggregate Get(Expression> predicate); - - /// - /// Asynchronously gets an entity with the given primary key value. If an entity with the - /// given primary key values exists in the context, then it is returned immediately - /// without making a request to the store. Otherwise, a request is made to the - /// store for an entity with the given primary key value and this entity, if - /// found, is attached to the context and returned. If no entity is found in - /// the context or the store -or- more than one entity is found, then an - /// InvalidOperationException is thrown. - /// - /// The key values. - /// The entity found - /// If no entity is found in the context or the store -or- more than one entity is found, then an - Task GetAsync(params TId[] keyValues); - - /// - /// Asynchronously gets an entity that satisfies a specified condition, - /// and throws an exception if more than one such element exists. - /// - /// A function to test an element for a condition. - /// - /// No entity satisfies the condition in predicate. -or- More than one entity satisfies the condition in predicate. -or- The source sequence is empty. - /// - Task GetAsync(Expression> predicate); - - /// - /// Adds the given entity to the context underlying the set in the Added state - /// such that it will be inserted into the database when SaveChanges is called. - /// - /// The entity to add - /// The entity - /// Note that entities that are already in the context in some other state will - /// have their state set to Added. Add is a no-op if the entity is already in - /// the context in the Added state. - TAggregate Add(TAggregate entity); - - /// - /// Asynchronously adds the given entity to the context underlying the set in the Added state - /// such that it will be inserted into the database when SaveChanges is called. - /// - /// The entity to add - /// - /// The entity - /// Note that entities that are already in the context in some other state will - /// have their state set to Added. Add is a no-op if the entity is already in - /// the context in the Added state. - Task AddAsync(TAggregate entity, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// - /// - /// - /// - IEnumerable AddRange(ICollection entities); - - /// - /// - /// - /// - /// - /// - Task> AddRangeAsync( - ICollection entities, - CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Marks the given entity as Deleted such that it will be deleted from the database - /// when SaveChanges is called. Note that the entity must exist in the context - /// in some other state before this method is called. - /// - /// The entity to remove - /// The entity - /// Note that if the entity exists in the context in the Added state, then this - /// method will cause it to be detached from the context. This is because an - /// Added entity is assumed not to exist in the database such that trying to - /// delete it does not make sense. - TAggregate Remove(TAggregate entity); - - /// - /// Asynchronously marks the given entity as Deleted such that it will be deleted from the database - /// when SaveChanges is called. Note that the entity must exist in the context - /// in some other state before this method is called. - /// - /// The entity to remove - /// - /// The entity - /// Note that if the entity exists in the context in the Added state, then this - /// method will cause it to be detached from the context. This is because an - /// Added entity is assumed not to exist in the database such that trying to - /// delete it does not make sense. - Task RemoveAsync(TAggregate entity, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Executes a delete statement filtering the rows to be deleted. - /// - /// The predicate. - /// The number of row deleted. - /// - /// When executing this method, the statement is immediately executed on the - /// database provider and is not part of the change tracking system. Also, changes - /// will not be reflected on any entities that have already been materialized - /// in the current contex - /// - int Delete(Expression> predicate); - - /// - /// Removes the given collection of entities from the DbContext - /// - /// The collection of entities to remove. - IEnumerable RemoveRange(ICollection entities); - - /// - /// Asynchronously removes the given collection of entities from the DbContext - /// - /// The collection of entities to remove. - /// - Task> RemoveRangeAsync( - ICollection entities, - CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Updates the given entity in the DbContext and executes SaveChanges() - /// - /// The entity to update. - TAggregate Update(TAggregate entity); - - /// - /// Asynchronously updates the given entity in the DbContext and executes SaveChanges() - /// - /// The entity to update. - /// - Task UpdateAsync(TAggregate entity, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/Dfe.Academies.Domain/Trust/TrustGovernance.cs b/Dfe.Academies.Domain/Trust/TrustGovernance.cs new file mode 100644 index 000000000..d501bbbed --- /dev/null +++ b/Dfe.Academies.Domain/Trust/TrustGovernance.cs @@ -0,0 +1,20 @@ +namespace Dfe.Academies.Domain.Trust +{ + public class TrustGovernance + { + public long SK { get; set; } + public long? TrustId { get; set; } + public long? GovernanceRoleTypeId { get; set; } + public string GID { get; set; } = null!; + public string? Title { get; set; } + public string? Forename1 { get; set; } + public string? Forename2 { get; set; } + public string? Surname { get; set; } + public string? Email { get; set; } + public string? DateOfAppointment { get; set; } + public string? DateTermOfOfficeEndsOrEnded { get; set; } + public string? AppointingBody { get; set; } + public DateTime? Modified { get; set; } + public string? ModifiedBy { get; set; } + } +} diff --git a/Dfe.Academies.Domain/ValueObjects/ConstituencyId.cs b/Dfe.Academies.Domain/ValueObjects/ConstituencyId.cs index 25e51fce7..43f6261ed 100644 --- a/Dfe.Academies.Domain/ValueObjects/ConstituencyId.cs +++ b/Dfe.Academies.Domain/ValueObjects/ConstituencyId.cs @@ -2,37 +2,5 @@ namespace Dfe.Academies.Domain.ValueObjects { - public class ConstituencyId : ValueObject - { - public int Value { get; } - - public ConstituencyId(int value) - { - if (value <= 0) - { - throw new ArgumentException("Constituency ID must be a positive integer.", nameof(value)); - } - Value = value; - } - - protected override IEnumerable GetEqualityComponents() - { - yield return Value; - } - - public override string ToString() - { - return Value.ToString(); - } - - public static implicit operator int(ConstituencyId constituencyId) - { - return constituencyId.Value; - } - - public static implicit operator ConstituencyId(int value) - { - return new ConstituencyId(value); - } - } + public record ConstituencyId(int Value) : IStronglyTypedId; } diff --git a/Dfe.Academies.Domain/ValueObjects/MemberId.cs b/Dfe.Academies.Domain/ValueObjects/MemberId.cs index 66d8e294f..90819ba87 100644 --- a/Dfe.Academies.Domain/ValueObjects/MemberId.cs +++ b/Dfe.Academies.Domain/ValueObjects/MemberId.cs @@ -2,37 +2,5 @@ namespace Dfe.Academies.Domain.ValueObjects { - public class MemberId : ValueObject - { - public int Value { get; } - - public MemberId(int value) - { - if (value <= 0) - { - throw new ArgumentException("Member ID must be a positive integer.", nameof(value)); - } - Value = value; - } - - protected override IEnumerable GetEqualityComponents() - { - yield return Value; - } - - public override string ToString() - { - return Value.ToString(); - } - - public static implicit operator int(MemberId memberId) - { - return memberId.Value; - } - - public static implicit operator MemberId(int value) - { - return new MemberId(value); - } - } + public record MemberId(int Value) : IStronglyTypedId; } diff --git a/Dfe.Academies.Domain/ValueObjects/NameDetails.cs b/Dfe.Academies.Domain/ValueObjects/NameDetails.cs index 475acf625..1845b7413 100644 --- a/Dfe.Academies.Domain/ValueObjects/NameDetails.cs +++ b/Dfe.Academies.Domain/ValueObjects/NameDetails.cs @@ -2,28 +2,5 @@ namespace Dfe.Academies.Domain.ValueObjects { - public class NameDetails : ValueObject - { - public string NameListAs { get; } - public string NameDisplayAs { get; } - public string NameFullTitle { get; } - - public NameDetails(string nameListAs, string nameDisplayAs, string nameFullTitle) - { - if (string.IsNullOrEmpty(nameListAs)) throw new ArgumentNullException(nameof(nameListAs)); - if (string.IsNullOrEmpty(nameDisplayAs)) throw new ArgumentNullException(nameof(nameDisplayAs)); - if (string.IsNullOrEmpty(nameFullTitle)) throw new ArgumentNullException(nameof(nameFullTitle)); - - NameListAs = nameListAs; - NameDisplayAs = nameDisplayAs; - NameFullTitle = nameFullTitle; - } - - protected override IEnumerable GetEqualityComponents() - { - yield return NameListAs; - yield return NameDisplayAs; - yield return NameFullTitle; - } - } + public record NameDetails(string NameListAs, string NameDisplayAs, string NameFullTitle) : IStronglyTypedId; } \ No newline at end of file diff --git a/Dfe.Academies.Utils/Enums/TrustIdType.cs b/Dfe.Academies.Utils/Enums/TrustIdType.cs new file mode 100644 index 000000000..9c9362188 --- /dev/null +++ b/Dfe.Academies.Utils/Enums/TrustIdType.cs @@ -0,0 +1,9 @@ +namespace Dfe.Academies.Utils.Enums +{ + public enum TrustIdType + { + Trn, + UkPrn, + Invalid + } +} diff --git a/Dfe.Academies.Utils/Helpers/IdentifierHelper.cs b/Dfe.Academies.Utils/Helpers/IdentifierHelper.cs new file mode 100644 index 000000000..ec4d07641 --- /dev/null +++ b/Dfe.Academies.Utils/Helpers/IdentifierHelper.cs @@ -0,0 +1,22 @@ +namespace Dfe.Academies.Utils.Helpers +{ + public static class IdentifierHelper where TEnum : Enum + { + public static TEnum DetermineIdType(TId id, Dictionary> idValidators) + { + var matchingValidator = idValidators.FirstOrDefault(validator => validator.Value(id)); + + if (!Equals(matchingValidator, default(KeyValuePair>))) + { + return matchingValidator.Key; + } + + return (TEnum)Enum.Parse(typeof(TEnum), "Invalid"); + } + + public static Dictionary> GetDefaultIdValidators() + { + throw new NotImplementedException("Please implement this method in your specific use case."); + } + } +} diff --git a/Dfe.Academies.Utils/Helpers/TrustIdValidator.cs b/Dfe.Academies.Utils/Helpers/TrustIdValidator.cs new file mode 100644 index 000000000..8bb89f063 --- /dev/null +++ b/Dfe.Academies.Utils/Helpers/TrustIdValidator.cs @@ -0,0 +1,26 @@ +using Dfe.Academies.Utils.Enums; + +namespace Dfe.Academies.Utils.Helpers +{ + public static class TrustIdValidator + { + public static Dictionary> GetTrustIdValidators() + { + return new Dictionary> + { + { TrustIdType.Trn, IsValidTrn }, + { TrustIdType.UkPrn, IsValidUkPrn } + }; + } + + private static bool IsValidTrn(string id) + { + return id.StartsWith("TR") && id.Length == 7 && id.Substring(2).All(char.IsDigit); + } + + private static bool IsValidUkPrn(string id) + { + return id.Length == 8 && id.All(char.IsDigit); + } + } +} diff --git a/Dfe.PersonsApi.Client/Generated/Client.g.cs b/Dfe.PersonsApi.Client/Generated/Client.g.cs index 60d36e3ea..a9b9f13ec 100644 --- a/Dfe.PersonsApi.Client/Generated/Client.g.cs +++ b/Dfe.PersonsApi.Client/Generated/Client.g.cs @@ -643,6 +643,263 @@ private string ConvertToString(object value, System.Globalization.CultureInfo cu } } + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class TrustsClient : ITrustsClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public TrustsClient(string baseUrl, System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = baseUrl; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Retrieve All Members Associated With a Trust by Either UKPRN or TRN + /// + /// The identifier (UKPRN or TRN). + /// A Collection of Persons Associated With the Trust. + /// A server side error occurred. + public virtual System.Threading.Tasks.Task> GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync(string id) + { + return GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync(id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Retrieve All Members Associated With a Trust by Either UKPRN or TRN + /// + /// The identifier (UKPRN or TRN). + /// A Collection of Persons Associated With the Trust. + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task> GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync(string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "v1/Trusts/{id}/getAssociatedPersons" + urlBuilder_.Append("v1/Trusts/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append("/getAssociatedPersons"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("Trust not found.", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + } #pragma warning restore 108 diff --git a/Dfe.PersonsApi.Client/Generated/Contracts.g.cs b/Dfe.PersonsApi.Client/Generated/Contracts.g.cs index 5bdd5eb5d..5fb0bd29c 100644 --- a/Dfe.PersonsApi.Client/Generated/Contracts.g.cs +++ b/Dfe.PersonsApi.Client/Generated/Contracts.g.cs @@ -83,6 +83,28 @@ public partial interface IEstablishmentsClient } + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial interface ITrustsClient + { + /// + /// Retrieve All Members Associated With a Trust by Either UKPRN or TRN + /// + /// The identifier (UKPRN or TRN). + /// A Collection of Persons Associated With the Trust. + /// A server side error occurred. + System.Threading.Tasks.Task> GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync(string id); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Retrieve All Members Associated With a Trust by Either UKPRN or TRN + /// + /// The identifier (UKPRN or TRN). + /// A Collection of Persons Associated With the Trust. + /// A server side error occurred. + System.Threading.Tasks.Task> GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync(string id, System.Threading.CancellationToken cancellationToken); + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class MemberOfParliament : Person { @@ -194,6 +216,30 @@ public static AcademyGovernance FromJson(string data) } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class TrustGovernance : Person + { + [Newtonsoft.Json.JsonProperty("ukprn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Ukprn { get; set; } + + [Newtonsoft.Json.JsonProperty("trn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Trn { get; set; } + + public string ToJson() + { + + return Newtonsoft.Json.JsonConvert.SerializeObject(this, new Newtonsoft.Json.JsonSerializerSettings()); + + } + public static TrustGovernance FromJson(string data) + { + + return Newtonsoft.Json.JsonConvert.DeserializeObject(data, new Newtonsoft.Json.JsonSerializerSettings()); + + } + + } + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] diff --git a/Dfe.PersonsApi.Client/Generated/swagger.json b/Dfe.PersonsApi.Client/Generated/swagger.json index ffe057444..8a1a99aaf 100644 --- a/Dfe.PersonsApi.Client/Generated/swagger.json +++ b/Dfe.PersonsApi.Client/Generated/swagger.json @@ -124,6 +124,45 @@ } } } + }, + "/v1/Trusts/{id}/getAssociatedPersons": { + "get": { + "tags": [ + "Trusts" + ], + "summary": "Retrieve All Members Associated With a Trust by Either UKPRN or TRN", + "operationId": "Trusts_GetAllPersonsAssociatedWithTrustByTrnOrUkPrn", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The identifier (UKPRN or TRN).", + "schema": { + "type": "string" + }, + "x-position": 1 + } + ], + "responses": { + "200": { + "description": "A Collection of Persons Associated With the Trust.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TrustGovernance" + } + } + } + } + }, + "404": { + "description": "Trust not found." + } + } + } } }, "components": { @@ -217,6 +256,27 @@ } } ] + }, + "TrustGovernance": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "ukprn": { + "type": "string", + "nullable": true + }, + "trn": { + "type": "string", + "nullable": true + } + } + } + ] } } } diff --git a/PersonsApi/Controllers/TrustsController.cs b/PersonsApi/Controllers/TrustsController.cs new file mode 100644 index 000000000..4c439786f --- /dev/null +++ b/PersonsApi/Controllers/TrustsController.cs @@ -0,0 +1,32 @@ +using Dfe.Academies.Application.Common.Models; +using Dfe.Academies.Application.Trust.Queries.GetAllPersonsAssociatedWithTrustByTrnOrUkprn; +using MediatR; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; + +namespace PersonsApi.Controllers +{ + [ApiController] + [Authorize(Policy = "API.Read")] + [ApiVersion("1.0")] + [Route("v{version:apiVersion}/[controller]")] + public class TrustsController(ISender sender) : ControllerBase + { + /// + /// Retrieve All Members Associated With a Trust by Either UKPRN or TRN + /// + /// The identifier (UKPRN or TRN). + /// The cancellation token. + /// + [HttpGet("{id}/getAssociatedPersons")] + [SwaggerResponse(200, "A Collection of Persons Associated With the Trust.", typeof(List))] + [SwaggerResponse(404, "Trust not found.")] + public async Task GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync([FromRoute] string id, CancellationToken cancellationToken) + { + var result = await sender.Send(new GetAllPersonsAssociatedWithTrustByTrnOrUkprnQuery(id), cancellationToken); + + return result is null ? NotFound() : Ok(result); + } + } +} diff --git a/PersonsApi/appsettings.Development.json b/PersonsApi/appsettings.Development.json index d856d0f9c..c1db5df0c 100644 --- a/PersonsApi/appsettings.Development.json +++ b/PersonsApi/appsettings.Development.json @@ -28,5 +28,8 @@ "TenantId": "9c7d9dd3-840c-4b3f-818e-552865082e16", "ClientId": "930a077f-43d0-48cb-9316-1e0430eeaf6b", "Audience": "api://930a077f-43d0-48cb-9316-1e0430eeaf6b" + }, + "Features": { + "PerformanceLoggingEnabled": true } } \ No newline at end of file diff --git a/PersonsApi/appsettings.json b/PersonsApi/appsettings.json index 7f6a32871..e626f0e64 100644 --- a/PersonsApi/appsettings.json +++ b/PersonsApi/appsettings.json @@ -46,12 +46,16 @@ "DefaultDurationInSeconds": 60, "Durations": { "GetMembersOfParliamentByConstituenciesQueryHandler": 86400, - "GetMemberOfParliamentByConstituencyQueryHandler": 86400 + "GetMemberOfParliamentByConstituencyQueryHandler": 86400, + "GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandler": 86400 } }, "Authorization": { "Roles": [ "API.Read" ] + }, + "Features": { + "PerformanceLoggingEnabled": false } } diff --git a/Tests/Dfe.Academies.Application.Tests/Dfe.Academies.Application.Tests.csproj b/Tests/Dfe.Academies.Application.Tests/Dfe.Academies.Application.Tests.csproj index 86828612e..a3f8ae50e 100644 --- a/Tests/Dfe.Academies.Application.Tests/Dfe.Academies.Application.Tests.csproj +++ b/Tests/Dfe.Academies.Application.Tests/Dfe.Academies.Application.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -25,7 +25,7 @@ - + diff --git a/Tests/Dfe.Academies.Application.Tests/Helpers/IdentifierHelperTests.cs b/Tests/Dfe.Academies.Application.Tests/Helpers/IdentifierHelperTests.cs new file mode 100644 index 000000000..f66eeb5f9 --- /dev/null +++ b/Tests/Dfe.Academies.Application.Tests/Helpers/IdentifierHelperTests.cs @@ -0,0 +1,24 @@ +using Dfe.Academies.Utils.Enums; +using Dfe.Academies.Utils.Helpers; + +namespace Dfe.Academies.Application.Tests.Helpers +{ + public class IdentifierHelperTests + { + [Theory] + [InlineData("TR12345", TrustIdType.Trn)] // Valid TRN + [InlineData("12345678", TrustIdType.UkPrn)] // Valid UKPRN + [InlineData("INVALID", TrustIdType.Invalid)] // Invalid ID + public void DetermineIdType_ShouldReturnCorrectType_WhenIdIsProvided(string id, TrustIdType expectedType) + { + // Arrange + var validators = TrustIdValidator.GetTrustIdValidators(); + + // Act + var result = IdentifierHelper.DetermineIdType(id, validators); + + // Assert + Assert.Equal(expectedType, result); + } + } +} diff --git a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentByConstituencyQueryHandlerTests.cs b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentByConstituencyQueryHandlerTests.cs index b109b4e3e..b98fbcb0b 100644 --- a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentByConstituencyQueryHandlerTests.cs +++ b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentByConstituencyQueryHandlerTests.cs @@ -4,10 +4,10 @@ using Dfe.Academies.Application.Constituencies.Queries.GetMemberOfParliamentByConstituency; using Dfe.Academies.Domain.Interfaces.Caching; using Dfe.Academies.Domain.Interfaces.Repositories; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Customizations.Entities; using Dfe.Academies.Testing.Common.Customizations.Models; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Customizations.Entities; using Dfe.Academies.Utils.Caching; using NSubstitute; diff --git a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentsByConstituenciesQueryHandlerTests.cs b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentsByConstituenciesQueryHandlerTests.cs index 6733f5955..974749c46 100644 --- a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentsByConstituenciesQueryHandlerTests.cs +++ b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Constituency/GetMemberOfParliamentsByConstituenciesQueryHandlerTests.cs @@ -4,10 +4,10 @@ using Dfe.Academies.Application.Constituencies.Queries.GetMemberOfParliamentByConstituencies; using Dfe.Academies.Domain.Interfaces.Caching; using Dfe.Academies.Domain.Interfaces.Repositories; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Customizations.Entities; using Dfe.Academies.Testing.Common.Customizations.Models; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Customizations.Entities; using Dfe.Academies.Utils.Caching; using MockQueryable; using NSubstitute; diff --git a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Establishment/GetAllPersonsAssociatedWithAcademyByUrnTests.cs b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Establishment/GetAllPersonsAssociatedWithAcademyByUrnTests.cs index 1531576e1..33407c2bd 100644 --- a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Establishment/GetAllPersonsAssociatedWithAcademyByUrnTests.cs +++ b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Establishment/GetAllPersonsAssociatedWithAcademyByUrnTests.cs @@ -4,10 +4,9 @@ using Dfe.Academies.Application.Common.Models; using Dfe.Academies.Application.Establishment.Queries.GetAllPersonsAssociatedWithAcademyByUrn; using Dfe.Academies.Domain.Interfaces.Caching; -using Dfe.Academies.Infrastructure.Models; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; using Dfe.Academies.Testing.Common.Customizations.Models; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; using Dfe.Academies.Utils.Caching; using MockQueryable; using NSubstitute; diff --git a/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Trust/GetAllPersonsAssociatedWithTrustByTrnOrUkprnTests.cs b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Trust/GetAllPersonsAssociatedWithTrustByTrnOrUkprnTests.cs new file mode 100644 index 000000000..eaa42f340 --- /dev/null +++ b/Tests/Dfe.Academies.Application.Tests/QueryHandlers/Trust/GetAllPersonsAssociatedWithTrustByTrnOrUkprnTests.cs @@ -0,0 +1,71 @@ +using AutoFixture; +using AutoFixture.Xunit2; +using Dfe.Academies.Application.Common.Interfaces; +using Dfe.Academies.Application.Common.Models; +using Dfe.Academies.Application.Establishment.Queries.GetAllPersonsAssociatedWithAcademyByUrn; +using Dfe.Academies.Application.Trust.Queries.GetAllPersonsAssociatedWithTrustByTrnOrUkprn; +using Dfe.Academies.Domain.Interfaces.Caching; +using Dfe.Academies.Testing.Common.Customizations.Models; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Utils.Caching; +using MockQueryable; +using NSubstitute; + +namespace Dfe.Academies.Application.Tests.QueryHandlers.Trust +{ + public class GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandlerTests + { + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(TrustGovernanceCustomization), + typeof(TrustGovernanceQueryModelCustomization), + typeof(AutoMapperCustomization))] + public async Task Handle_ShouldReturnPersonsAssociatedWithTrust_WhenTrustExists( + [Frozen] ITrustQueryService mockTrustQueryService, + [Frozen] ICacheService mockCacheService, + GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandler handler, + List governanceQueryModels, + IFixture fixture) + { + // Arrange + var expectedGovernances = governanceQueryModels.Select(governance => + fixture.Customize(new TrustGovernanceCustomization + { + FirstName = governance?.TrustGovernance?.Forename1, + LastName = governance?.TrustGovernance?.Surname, + }).Create()).ToList(); + + var query = new GetAllPersonsAssociatedWithTrustByTrnOrUkprnQuery("09532567"); + + var cacheKey = $"PersonsAssociatedWithTrust_{CacheKeyHelper.GenerateHashedCacheKey(query.Id)}"; + + var mock = governanceQueryModels.BuildMock(); + + mockTrustQueryService.GetTrustGovernanceByGroupIdOrUkprn(Arg.Any(), Arg.Any()) + .Returns(mock); + + mockCacheService.GetOrAddAsync(cacheKey, Arg.Any>>>(), Arg.Any()) + .Returns(callInfo => + { + var callback = callInfo.ArgAt>>>(1); + return callback(); + }); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.Equal(expectedGovernances.Count, result.Count); + for (int i = 0; i < result.Count; i++) + { + Assert.Equal(expectedGovernances[i].FirstName, result[i].FirstName); + Assert.Equal(expectedGovernances[i].LastName, result[i].LastName); + } + + await mockCacheService.Received(1).GetOrAddAsync(cacheKey, Arg.Any?>>>(), nameof(GetAllPersonsAssociatedWithTrustByTrnOrUkprnQueryHandler)); + } + } +} diff --git a/Tests/Dfe.Academies.Domain.Tests/Aggregates/ConstituencyTests.cs b/Tests/Dfe.Academies.Domain.Tests/Aggregates/ConstituencyTests.cs index 763dd9ca0..6386f1e0f 100644 --- a/Tests/Dfe.Academies.Domain.Tests/Aggregates/ConstituencyTests.cs +++ b/Tests/Dfe.Academies.Domain.Tests/Aggregates/ConstituencyTests.cs @@ -1,8 +1,8 @@ using Dfe.Academies.Domain.Constituencies; using Dfe.Academies.Domain.ValueObjects; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; using Dfe.Academies.Testing.Common.Customizations.Models; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; namespace Dfe.Academies.Domain.Tests.Aggregates { diff --git a/Tests/Dfe.Academies.Domain.Tests/Aggregates/MemberContactDetailsTests.cs b/Tests/Dfe.Academies.Domain.Tests/Aggregates/MemberContactDetailsTests.cs index 5cfc821f1..727fa666c 100644 --- a/Tests/Dfe.Academies.Domain.Tests/Aggregates/MemberContactDetailsTests.cs +++ b/Tests/Dfe.Academies.Domain.Tests/Aggregates/MemberContactDetailsTests.cs @@ -1,6 +1,6 @@ using Dfe.Academies.Domain.Constituencies; using Dfe.Academies.Domain.ValueObjects; -using Dfe.Academies.Testing.Common.Attributes; +using Dfe.Academies.Tests.Common.Attributes; namespace Dfe.Academies.Domain.Tests.Aggregates { diff --git a/Tests/Dfe.Academies.Domain.Tests/Dfe.Academies.Domain.Tests.csproj b/Tests/Dfe.Academies.Domain.Tests/Dfe.Academies.Domain.Tests.csproj index 46c80eed0..2df83ed68 100644 --- a/Tests/Dfe.Academies.Domain.Tests/Dfe.Academies.Domain.Tests.csproj +++ b/Tests/Dfe.Academies.Domain.Tests/Dfe.Academies.Domain.Tests.csproj @@ -17,7 +17,7 @@ - + diff --git a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs index 6265c4fbc..1af641374 100644 --- a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs +++ b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/ConstituenciesControllerTests.cs @@ -1,12 +1,12 @@ using System.Net; using Dfe.Academies.Infrastructure; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Mocks; using Dfe.PersonsApi.Client.Contracts; using Microsoft.EntityFrameworkCore; using PersonsApi; using System.Security.Claims; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Mocks; namespace Dfe.Academies.PersonsApi.Tests.Integration.Controllers { diff --git a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/EstablishmentsControllerTests.cs b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/EstablishmentsControllerTests.cs index e5d0aa1fa..b0ff5e1bb 100644 --- a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/EstablishmentsControllerTests.cs +++ b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/EstablishmentsControllerTests.cs @@ -1,11 +1,11 @@ using Dfe.Academies.Infrastructure; -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Mocks; using Dfe.PersonsApi.Client.Contracts; using Microsoft.EntityFrameworkCore; using PersonsApi; using System.Security.Claims; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Mocks; namespace Dfe.Academies.PersonsApi.Tests.Integration.Controllers { diff --git a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/TrustsControllerTests.cs b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/TrustsControllerTests.cs new file mode 100644 index 000000000..ccb187573 --- /dev/null +++ b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Controllers/TrustsControllerTests.cs @@ -0,0 +1,83 @@ +using Dfe.Academies.Infrastructure; +using Dfe.PersonsApi.Client.Contracts; +using PersonsApi; +using System.Security.Claims; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Mocks; + +namespace Dfe.Academies.PersonsApi.Tests.Integration.Controllers +{ + public class TrustsControllerTests + { + [Theory] + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] + public async Task GetAllPersonsAssociatedWithTrustAsync_ShouldReturnPeople_WhenTrustExists( + CustomWebApplicationDbContextFactory factory, + ITrustsClient trustsClient) + { + // Arrange + factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Read")]; + + // Act + var result = await trustsClient.GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync("87654321"); + + // Assert + Assert.NotNull(result); + Assert.NotEmpty(result); + Assert.Equal(2, result.Count); + Assert.True(result.All(x => x.Roles.Count > 0)); + Assert.Contains(result, x => x.FirstName == "John"); + Assert.Contains(result, x => x.FirstName == "Joe"); + } + + [Theory] + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] + public async Task GetAllPersonsAssociatedWithTrustAsync_ShouldReturnEmptyList_WhenTrustExistWithNoPeople( + CustomWebApplicationDbContextFactory factory, + ITrustsClient trustsClient) + { + // Arrange + factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Read")]; + + // Act + var result = await trustsClient.GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync("TR00024"); + + // Assert + Assert.NotNull(result); + Assert.Empty(result); + } + + [Theory] + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] + public async Task GetAllPersonsAssociatedWithTrustAsync_ShouldThrowAnException_WhenTrustDoesntExists( + CustomWebApplicationDbContextFactory factory, + ITrustsClient trustsClient) + { + // Arrange + factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Read")]; + + // Act & Assert + var exception = await Assert.ThrowsAsync(() => + trustsClient.GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync("65432567")); + + Assert.Contains("Trust not found.", exception.Message); + } + + [Theory] + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] + public async Task GetAllPersonsAssociatedWithTrustAsync_ShouldThrowAnException_WhenInvalidIdProvided( + CustomWebApplicationDbContextFactory factory, + ITrustsClient trustsClient) + { + // Arrange + factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Read")]; + + // Act & Assert + var exception = await Assert.ThrowsAsync(() => + trustsClient.GetAllPersonsAssociatedWithTrustByTrnOrUkPrnAsync("invalidId")); + + Assert.Contains("The identifier must be either a valid TRN (TR{5 digits}) or a valid UKPRN (8 digits).", exception.Message); + } + } +} \ No newline at end of file diff --git a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj index 03619e456..67411b091 100644 --- a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj +++ b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/Dfe.Academies.PersonsApi.Tests.Integration.csproj @@ -23,9 +23,8 @@ - - + diff --git a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs index 9703d3d26..cb339218f 100644 --- a/Tests/Dfe.Academies.PersonsApi.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs +++ b/Tests/Dfe.Academies.PersonsApi.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs @@ -1,8 +1,8 @@ -using Dfe.Academies.Testing.Common.Attributes; -using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Mocks; -using PersonsApi; +using PersonsApi; using System.Net; +using Dfe.Academies.Tests.Common.Attributes; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Mocks; namespace Dfe.Academies.PersonsApi.Tests.Integration.OpenApiTests; diff --git a/Tests/Dfe.Academies.Testing.Common/Attributes/CustomAutoDataAttribute.cs b/Tests/Dfe.Academies.Tests.Common/Attributes/CustomAutoDataAttribute.cs similarity index 79% rename from Tests/Dfe.Academies.Testing.Common/Attributes/CustomAutoDataAttribute.cs rename to Tests/Dfe.Academies.Tests.Common/Attributes/CustomAutoDataAttribute.cs index 219cd844c..9367bc15e 100644 --- a/Tests/Dfe.Academies.Testing.Common/Attributes/CustomAutoDataAttribute.cs +++ b/Tests/Dfe.Academies.Tests.Common/Attributes/CustomAutoDataAttribute.cs @@ -1,8 +1,9 @@ using AutoFixture.Xunit2; using Dfe.Academies.Testing.Common.Customizations; -using Dfe.Academies.Testing.Common.Helpers; +using Dfe.Academies.Tests.Common.Customizations; +using Dfe.Academies.Tests.Common.Helpers; -namespace Dfe.Academies.Testing.Common.Attributes +namespace Dfe.Academies.Tests.Common.Attributes { public class CustomAutoDataAttribute(params Type[] customizations) : AutoDataAttribute(() => FixtureFactoryHelper.ConfigureFixtureFactory(CombineCustomizations(customizations))) diff --git a/Tests/Dfe.Academies.Testing.Common/Attributes/InlineCustomAutoDataAttribute.cs b/Tests/Dfe.Academies.Tests.Common/Attributes/InlineCustomAutoDataAttribute.cs similarity index 81% rename from Tests/Dfe.Academies.Testing.Common/Attributes/InlineCustomAutoDataAttribute.cs rename to Tests/Dfe.Academies.Tests.Common/Attributes/InlineCustomAutoDataAttribute.cs index 6bb07a922..1ab3ff006 100644 --- a/Tests/Dfe.Academies.Testing.Common/Attributes/InlineCustomAutoDataAttribute.cs +++ b/Tests/Dfe.Academies.Tests.Common/Attributes/InlineCustomAutoDataAttribute.cs @@ -1,6 +1,6 @@ using AutoFixture.Xunit2; -namespace Dfe.Academies.Testing.Common.Attributes +namespace Dfe.Academies.Tests.Common.Attributes { public class InlineCustomAutoDataAttribute(object[] values, params Type[] customizations) : InlineAutoDataAttribute(new CustomAutoDataAttribute(customizations), values); diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/AutoMapperCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/AutoMapperCustomization.cs similarity index 92% rename from Tests/Dfe.Academies.Testing.Common/Customizations/AutoMapperCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/AutoMapperCustomization.cs index f77c52613..31c4d45cd 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/AutoMapperCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/AutoMapperCustomization.cs @@ -1,9 +1,8 @@ using AutoFixture; using AutoMapper; using Dfe.Academies.Application.MappingProfiles; -using System.Reflection; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class AutoMapperCustomization : ICustomization { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs similarity index 87% rename from Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs index ea5a213c7..f7527c4bf 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs @@ -1,14 +1,14 @@ using System.Security.Claims; using AutoFixture; -using Dfe.Academies.Testing.Common.Mocks; -using Microsoft.EntityFrameworkCore; +using Dfe.Academies.Tests.Common.Mocks; using Dfe.PersonsApi.Client; using Dfe.PersonsApi.Client.Contracts; using Dfe.PersonsApi.Client.Extensions; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class CustomWebApplicationDbContextFactoryCustomization : ICustomization where TProgram : class where TDbContext : DbContext @@ -33,7 +33,8 @@ public void Customize(IFixture fixture) services.AddSingleton(config); services.AddPersonsApiClient(config, client); services.AddPersonsApiClient(config, client); - + services.AddPersonsApiClient(config, client); + var serviceProvider = services.BuildServiceProvider(); fixture.Inject(factory); @@ -41,6 +42,8 @@ public void Customize(IFixture fixture) fixture.Inject(client); fixture.Inject(serviceProvider.GetRequiredService()); fixture.Inject(serviceProvider.GetRequiredService()); + fixture.Inject(serviceProvider.GetRequiredService()); + fixture.Inject(new List()); return factory; diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationFactoryCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationFactoryCustomization.cs similarity index 92% rename from Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationFactoryCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationFactoryCustomization.cs index 288c1a072..523d53ecc 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/CustomWebApplicationFactoryCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/CustomWebApplicationFactoryCustomization.cs @@ -1,13 +1,13 @@ -using AutoFixture; -using Dfe.Academies.Testing.Common.Mocks; +using System.Security.Claims; +using AutoFixture; +using Dfe.Academies.Tests.Common.Mocks; using Dfe.PersonsApi.Client; using Dfe.PersonsApi.Client.Contracts; using Dfe.PersonsApi.Client.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.Security.Claims; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class CustomWebApplicationFactoryCustomization : ICustomization where TProgram : class { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/DateOnlyCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/DateOnlyCustomization.cs similarity index 85% rename from Tests/Dfe.Academies.Testing.Common/Customizations/DateOnlyCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/DateOnlyCustomization.cs index 7729d5f9b..96b39fc98 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/DateOnlyCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/DateOnlyCustomization.cs @@ -1,6 +1,6 @@ using AutoFixture; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class DateOnlyCustomization : ICustomization { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/DbContextCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/DbContextCustomization.cs similarity index 87% rename from Tests/Dfe.Academies.Testing.Common/Customizations/DbContextCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/DbContextCustomization.cs index 656eb8bb9..0fb493287 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/DbContextCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/DbContextCustomization.cs @@ -1,9 +1,9 @@ using AutoFixture; -using Dfe.Academies.Testing.Common.Helpers; +using Dfe.Academies.Tests.Common.Helpers; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class DbContextCustomization : ICustomization where TContext : DbContext { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/Entities/ConstituencyCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Entities/ConstituencyCustomization.cs similarity index 94% rename from Tests/Dfe.Academies.Testing.Common/Customizations/Entities/ConstituencyCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/Entities/ConstituencyCustomization.cs index fa3d59cbe..29b4a4643 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/Entities/ConstituencyCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/Entities/ConstituencyCustomization.cs @@ -2,7 +2,7 @@ using Dfe.Academies.Domain.Constituencies; using Dfe.Academies.Domain.ValueObjects; -namespace Dfe.Academies.Testing.Common.Customizations.Entities +namespace Dfe.Academies.Tests.Common.Customizations.Entities { public class ConstituencyCustomization : ICustomization { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/Models/AcademyGovernanceCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/AcademyGovernanceCustomization.cs similarity index 100% rename from Tests/Dfe.Academies.Testing.Common/Customizations/Models/AcademyGovernanceCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/Models/AcademyGovernanceCustomization.cs diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs similarity index 94% rename from Tests/Dfe.Academies.Testing.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs index 861a451a4..82e048311 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/AcademyGovernanceQueryModelCustomization.cs @@ -1,6 +1,6 @@ using AutoFixture; +using Dfe.Academies.Application.Common.Models; using Dfe.Academies.Domain.Establishment; -using Dfe.Academies.Infrastructure.Models; namespace Dfe.Academies.Testing.Common.Customizations.Models { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/Models/MemberOfParliamentCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/MemberOfParliamentCustomization.cs similarity index 100% rename from Tests/Dfe.Academies.Testing.Common/Customizations/Models/MemberOfParliamentCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/Models/MemberOfParliamentCustomization.cs diff --git a/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceCustomization.cs new file mode 100644 index 000000000..05854d5dd --- /dev/null +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceCustomization.cs @@ -0,0 +1,28 @@ +using AutoFixture; +using Dfe.Academies.Application.Common.Models; + +namespace Dfe.Academies.Testing.Common.Customizations.Models +{ + public class TrustGovernanceCustomization : ICustomization + { + public string? FirstName { get; set; } = "John"; + public string? LastName { get; set; } = "Doe"; + public string? Email { get; set; } = "john.doe@example.com"; + public string? DisplayName { get; set; } = "John Doe"; + public string? DisplayNameWithTitle { get; set; } = "Mr. John Doe"; + public List Roles { get; set; } = ["MP"]; + public DateTime UpdatedAt { get; set; } = DateTime.Now; + + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(x => x.FirstName, FirstName) + .With(x => x.LastName, LastName) + .With(x => x.Email, Email) + .With(x => x.DisplayName, DisplayName) + .With(x => x.DisplayNameWithTitle, DisplayNameWithTitle) + .With(x => x.Roles, Roles) + .With(x => x.UpdatedAt, UpdatedAt)); + } + } +} diff --git a/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceQueryModelCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceQueryModelCustomization.cs new file mode 100644 index 000000000..0560747e2 --- /dev/null +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/Models/TrustGovernanceQueryModelCustomization.cs @@ -0,0 +1,24 @@ +using AutoFixture; +using Dfe.Academies.Application.Common.Models; +using Dfe.Academies.Domain.Establishment; +using Dfe.Academies.Domain.Trust; +using TrustGovernance = Dfe.Academies.Domain.Trust.TrustGovernance; + +namespace Dfe.Academies.Testing.Common.Customizations.Models +{ + public class TrustGovernanceQueryModelCustomization : ICustomization + { + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .FromFactory(() => + { + var trust = fixture.Create(); + var governanceRoleType = fixture.Create(); + var trustGovernance = fixture.Create(); + + return new TrustGovernanceQueryModel(trust, governanceRoleType, trustGovernance); + })); + } + } +} diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/NSubstituteCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/NSubstituteCustomization.cs similarity index 83% rename from Tests/Dfe.Academies.Testing.Common/Customizations/NSubstituteCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/NSubstituteCustomization.cs index 4d272eac2..bd8ccf227 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/NSubstituteCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/NSubstituteCustomization.cs @@ -1,7 +1,7 @@ using AutoFixture; using AutoFixture.AutoNSubstitute; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class NSubstituteCustomization : ICustomization { diff --git a/Tests/Dfe.Academies.Testing.Common/Customizations/OmitCircularReferenceCustomization.cs b/Tests/Dfe.Academies.Tests.Common/Customizations/OmitCircularReferenceCustomization.cs similarity index 87% rename from Tests/Dfe.Academies.Testing.Common/Customizations/OmitCircularReferenceCustomization.cs rename to Tests/Dfe.Academies.Tests.Common/Customizations/OmitCircularReferenceCustomization.cs index 40ff7b986..c38afcb76 100644 --- a/Tests/Dfe.Academies.Testing.Common/Customizations/OmitCircularReferenceCustomization.cs +++ b/Tests/Dfe.Academies.Tests.Common/Customizations/OmitCircularReferenceCustomization.cs @@ -1,6 +1,6 @@ using AutoFixture; -namespace Dfe.Academies.Testing.Common.Customizations +namespace Dfe.Academies.Tests.Common.Customizations { public class OmitCircularReferenceCustomization : ICustomization { diff --git a/Tests/Dfe.Academies.Testing.Common/Dfe.Academies.Testing.Common.csproj b/Tests/Dfe.Academies.Tests.Common/Dfe.Academies.Tests.Common.csproj similarity index 100% rename from Tests/Dfe.Academies.Testing.Common/Dfe.Academies.Testing.Common.csproj rename to Tests/Dfe.Academies.Tests.Common/Dfe.Academies.Tests.Common.csproj diff --git a/Tests/Dfe.Academies.Testing.Common/Helpers/DbContextHelper.cs b/Tests/Dfe.Academies.Tests.Common/Helpers/DbContextHelper.cs similarity index 63% rename from Tests/Dfe.Academies.Testing.Common/Helpers/DbContextHelper.cs rename to Tests/Dfe.Academies.Tests.Common/Helpers/DbContextHelper.cs index 4b8bee38e..b0a379c3c 100644 --- a/Tests/Dfe.Academies.Testing.Common/Helpers/DbContextHelper.cs +++ b/Tests/Dfe.Academies.Tests.Common/Helpers/DbContextHelper.cs @@ -1,4 +1,5 @@ -using Dfe.Academies.Domain.Constituencies; +using System.Data.Common; +using Dfe.Academies.Domain.Constituencies; using Dfe.Academies.Domain.Establishment; using Dfe.Academies.Domain.Trust; using Dfe.Academies.Domain.ValueObjects; @@ -6,9 +7,8 @@ using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using System.Data.Common; -namespace Dfe.Academies.Testing.Common.Helpers +namespace Dfe.Academies.Tests.Common.Helpers { public static class DbContextHelper where TContext : DbContext { @@ -38,10 +38,35 @@ private static void SeedTestData(TContext context) { if (context is MstrContext mstrContext) { - // Populate Trust - var trust1 = new Trust { SK = 1, Name = "Trust A", TrustTypeId = mstrContext.TrustTypes.FirstOrDefault()?.SK, GroupUID = "G1", Modified = DateTime.UtcNow, ModifiedBy = "System" }; - var trust2 = new Trust { SK = 2, Name = "Trust B", TrustTypeId = mstrContext.TrustTypes.FirstOrDefault()?.SK, GroupUID = "G2", Modified = DateTime.UtcNow, ModifiedBy = "System" }; - mstrContext.Trusts.AddRange(trust1, trust2); + if (!mstrContext.Trusts.Any() && !mstrContext.Establishments.Any() && + !mstrContext.EducationEstablishmentTrusts.Any() && !mstrContext.GovernanceRoleTypes.Any() && + !mstrContext.EducationEstablishmentGovernances.Any()) + { + + // Populate Trust + var trust1 = new Trust + { + SK = 1, + Name = "Trust A", + TrustTypeId = mstrContext.TrustTypes.FirstOrDefault()?.SK, + GroupUID = "G1", + Modified = DateTime.UtcNow, + ModifiedBy = "System", + UKPRN = "12345678", + GroupID = "TR00024" + }; + var trust2 = new Trust + { + SK = 2, + Name = "Trust B", + TrustTypeId = mstrContext.TrustTypes.FirstOrDefault()?.SK, + GroupUID = "G2", + Modified = DateTime.UtcNow, + ModifiedBy = "System", + UKPRN = "87654321", + GroupID = "TR00025" + }; + mstrContext.Trusts.AddRange(trust1, trust2); // Populate Establishment var establishment1 = new Establishment @@ -88,10 +113,14 @@ private static void SeedTestData(TContext context) }; mstrContext.EducationEstablishmentTrusts.AddRange(educationEstablishmentTrust1, educationEstablishmentTrust2); - // Populate GovernanceRoleType - var governanceRoleType1 = new GovernanceRoleType { SK = 1, Name = "Chair of Governors", Modified = DateTime.UtcNow, ModifiedBy = "System" }; - var governanceRoleType2 = new GovernanceRoleType { SK = 2, Name = "Vice Chair of Governors", Modified = DateTime.UtcNow, ModifiedBy = "System" }; - mstrContext.GovernanceRoleTypes.AddRange(governanceRoleType1, governanceRoleType2); + // Populate GovernanceRoleType + var governanceRoleType1 = new GovernanceRoleType + { SK = 1, Name = "Chair of Governors", Modified = DateTime.UtcNow, ModifiedBy = "System" }; + var governanceRoleType2 = new GovernanceRoleType + { SK = 2, Name = "Vice Chair of Governors", Modified = DateTime.UtcNow, ModifiedBy = "System" }; + var governanceRoleType3 = new GovernanceRoleType + { SK = 3, Name = "Trustee", Modified = DateTime.UtcNow, ModifiedBy = "System" }; + mstrContext.GovernanceRoleTypes.AddRange(governanceRoleType1, governanceRoleType2, governanceRoleType3); // Populate EducationEstablishmentGovernance var governance1 = new EducationEstablishmentGovernance @@ -122,8 +151,38 @@ private static void SeedTestData(TContext context) }; mstrContext.EducationEstablishmentGovernances.AddRange(governance1, governance3); - // Save changes - mstrContext.SaveChanges(); + // Populate TrustGovernance + var trustGovernance1 = new TrustGovernance + { + SK = 1, + TrustId = trust2.SK, + GovernanceRoleTypeId = governanceRoleType3.SK, + GID = "GID1", + Title = "Mr.", + Forename1 = "John", + Surname = "Wood", + Email = "johnWood@example.com", + Modified = DateTime.UtcNow, + ModifiedBy = "System" + }; + var trustGovernance2 = new TrustGovernance + { + SK = 2, + TrustId = trust2.SK, + GovernanceRoleTypeId = governanceRoleType3.SK, + GID = "GID1", + Title = "Mr.", + Forename1 = "Joe", + Surname = "Wood", + Email = "joeWood@example.com", + Modified = DateTime.UtcNow, + ModifiedBy = "System" + }; + mstrContext.TrustGovernances.AddRange(trustGovernance1, trustGovernance2); + + // Save changes + mstrContext.SaveChanges(); + } } if (context is MopContext mopContext) diff --git a/Tests/Dfe.Academies.Testing.Common/Helpers/FixtureFactoryHelper.cs b/Tests/Dfe.Academies.Tests.Common/Helpers/FixtureFactoryHelper.cs similarity index 91% rename from Tests/Dfe.Academies.Testing.Common/Helpers/FixtureFactoryHelper.cs rename to Tests/Dfe.Academies.Tests.Common/Helpers/FixtureFactoryHelper.cs index 165f282fa..f76995a8c 100644 --- a/Tests/Dfe.Academies.Testing.Common/Helpers/FixtureFactoryHelper.cs +++ b/Tests/Dfe.Academies.Tests.Common/Helpers/FixtureFactoryHelper.cs @@ -1,6 +1,6 @@ using AutoFixture; -namespace Dfe.Academies.Testing.Common.Helpers +namespace Dfe.Academies.Tests.Common.Helpers { public static class FixtureFactoryHelper { diff --git a/Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationDbContextFactory.cs b/Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationDbContextFactory.cs similarity index 94% rename from Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationDbContextFactory.cs rename to Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationDbContextFactory.cs index f747a13b2..f2384d58c 100644 --- a/Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationDbContextFactory.cs +++ b/Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationDbContextFactory.cs @@ -1,14 +1,14 @@ -using Dfe.Academies.Testing.Common.Helpers; +using System.Data.Common; +using System.Net.Http.Headers; +using System.Security.Claims; +using Dfe.Academies.Tests.Common.Helpers; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using System.Data.Common; -using System.Net.Http.Headers; -using System.Security.Claims; -namespace Dfe.Academies.Testing.Common.Mocks +namespace Dfe.Academies.Tests.Common.Mocks { public class CustomWebApplicationDbContextFactory : WebApplicationFactory where TProgram : class where TDbContext : DbContext diff --git a/Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationFactory.cs b/Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationFactory.cs similarity index 91% rename from Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationFactory.cs rename to Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationFactory.cs index 0e421c180..30d3a700e 100644 --- a/Tests/Dfe.Academies.Testing.Common/Mocks/CustomWebApplicationFactory.cs +++ b/Tests/Dfe.Academies.Tests.Common/Mocks/CustomWebApplicationFactory.cs @@ -1,11 +1,11 @@ -using Microsoft.AspNetCore.Authentication; +using System.Net.Http.Headers; +using System.Security.Claims; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; -using System.Net.Http.Headers; -using System.Security.Claims; -namespace Dfe.Academies.Testing.Common.Mocks +namespace Dfe.Academies.Tests.Common.Mocks { public class CustomWebApplicationFactory : WebApplicationFactory where TProgram : class diff --git a/Tests/Dfe.Academies.Testing.Common/Mocks/MockJwtBearerHandler.cs b/Tests/Dfe.Academies.Tests.Common/Mocks/MockJwtBearerHandler.cs similarity index 95% rename from Tests/Dfe.Academies.Testing.Common/Mocks/MockJwtBearerHandler.cs rename to Tests/Dfe.Academies.Tests.Common/Mocks/MockJwtBearerHandler.cs index a35137644..6e1ee6d09 100644 --- a/Tests/Dfe.Academies.Testing.Common/Mocks/MockJwtBearerHandler.cs +++ b/Tests/Dfe.Academies.Tests.Common/Mocks/MockJwtBearerHandler.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Dfe.Academies.Testing.Common.Mocks +namespace Dfe.Academies.Tests.Common.Mocks { #pragma warning disable CS0618 public class MockJwtBearerHandler( diff --git a/TramsDataApi.sln b/TramsDataApi.sln index d894b89e9..e35c7937c 100644 --- a/TramsDataApi.sln +++ b/TramsDataApi.sln @@ -28,9 +28,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.Application.T EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.PersonsApi.Tests.Integration", "Tests\Dfe.Academies.PersonsApi.Tests.Integration\Dfe.Academies.PersonsApi.Tests.Integration.csproj", "{EB0D20C1-4818-44DF-97A7-276C9F96CC64}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.Testing.Common", "Tests\Dfe.Academies.Testing.Common\Dfe.Academies.Testing.Common.csproj", "{777C300F-FBB1-402A-A850-1D26417FA412}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.Domain.Tests", "Tests\Dfe.Academies.Domain.Tests\Dfe.Academies.Domain.Tests.csproj", "{82966208-86EA-4459-8D99-FED765D07D82}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dfe.Academies.Domain.Tests", "Tests\Dfe.Academies.Domain.Tests\Dfe.Academies.Domain.Tests.csproj", "{82966208-86EA-4459-8D99-FED765D07D82}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dfe.Academies.Tests.Common", "Tests\Dfe.Academies.Tests.Common\Dfe.Academies.Tests.Common.csproj", "{AB1B6456-E7B3-46DF-823D-8A49C7D3B13F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -74,14 +74,14 @@ Global {EB0D20C1-4818-44DF-97A7-276C9F96CC64}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB0D20C1-4818-44DF-97A7-276C9F96CC64}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB0D20C1-4818-44DF-97A7-276C9F96CC64}.Release|Any CPU.Build.0 = Release|Any CPU - {777C300F-FBB1-402A-A850-1D26417FA412}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {777C300F-FBB1-402A-A850-1D26417FA412}.Debug|Any CPU.Build.0 = Debug|Any CPU - {777C300F-FBB1-402A-A850-1D26417FA412}.Release|Any CPU.ActiveCfg = Release|Any CPU - {777C300F-FBB1-402A-A850-1D26417FA412}.Release|Any CPU.Build.0 = Release|Any CPU {82966208-86EA-4459-8D99-FED765D07D82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {82966208-86EA-4459-8D99-FED765D07D82}.Debug|Any CPU.Build.0 = Debug|Any CPU {82966208-86EA-4459-8D99-FED765D07D82}.Release|Any CPU.ActiveCfg = Release|Any CPU {82966208-86EA-4459-8D99-FED765D07D82}.Release|Any CPU.Build.0 = Release|Any CPU + {AB1B6456-E7B3-46DF-823D-8A49C7D3B13F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB1B6456-E7B3-46DF-823D-8A49C7D3B13F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB1B6456-E7B3-46DF-823D-8A49C7D3B13F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB1B6456-E7B3-46DF-823D-8A49C7D3B13F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -89,8 +89,8 @@ Global GlobalSection(NestedProjects) = preSolution {9FDF7C25-083F-4404-BE64-82EF26EED072} = {08B2EC51-B7D3-4D6E-BF99-C2ADC957F387} {EB0D20C1-4818-44DF-97A7-276C9F96CC64} = {08B2EC51-B7D3-4D6E-BF99-C2ADC957F387} - {777C300F-FBB1-402A-A850-1D26417FA412} = {08B2EC51-B7D3-4D6E-BF99-C2ADC957F387} {82966208-86EA-4459-8D99-FED765D07D82} = {08B2EC51-B7D3-4D6E-BF99-C2ADC957F387} + {AB1B6456-E7B3-46DF-823D-8A49C7D3B13F} = {08B2EC51-B7D3-4D6E-BF99-C2ADC957F387} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F0704299-A9C2-448A-B816-E5BCCB345AF8}