Skip to content

Commit

Permalink
Merge pull request #175 from DFE-Digital/academies-db-connection/trus…
Browse files Browse the repository at this point in the history
…t-provider

Set `TrustProvider` to use academies db instead of academies api
  • Loading branch information
dynamictulip authored Oct 16, 2023
2 parents a894104 + 3c803f7 commit be71dee
Show file tree
Hide file tree
Showing 20 changed files with 290 additions and 344 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;

public class TrustProvider : ITrustProvider
{
private readonly IAcademiesDbContext _academiesDbContext;

[ExcludeFromCodeCoverage]
public TrustProvider(AcademiesDbContext academiesDbContext) : this((IAcademiesDbContext)academiesDbContext)
{
}

public TrustProvider(IAcademiesDbContext academiesDbContext)
{
_academiesDbContext = academiesDbContext;
}

public async Task<Trust?> GetTrustByUidAsync(string uid)
{
Trust? trust = null;

var group = await _academiesDbContext.Groups.SingleOrDefaultAsync(g => g.GroupUid == uid);
if (group is not null)
{
trust = new Trust(
group.GroupUid!,
group.GroupName ?? string.Empty,
group.Ukprn,
group.GroupType ?? string.Empty
);
}

return trust;
}
}
16 changes: 6 additions & 10 deletions DfE.FindInformationAcademiesTrusts.Data.AcademiesDb/TrustSearch.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;

Expand All @@ -19,16 +20,14 @@ public TrustSearch(IAcademiesDbContext academiesDbContext, ITrustHelper trustHel
_academiesDbContext = academiesDbContext;
}

public Task<IEnumerable<TrustSearchEntry>> SearchAsync(string searchTerm)
public async Task<TrustSearchEntry[]> SearchAsync(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
{
return Task.FromResult(
Array.Empty<TrustSearchEntry>().AsEnumerable()
);
return Array.Empty<TrustSearchEntry>();
}

var trustSearchEntries = _academiesDbContext.Groups
var trustSearchEntries = await _academiesDbContext.Groups
.Where(g =>
g.GroupUid != null &&
g.GroupId != null &&
Expand All @@ -42,11 +41,8 @@ public Task<IEnumerable<TrustSearchEntry>> SearchAsync(string searchTerm)
.Take(20)
.Select(g =>
new TrustSearchEntry(g.GroupName!, _trustHelper.BuildAddressString(g), g.GroupUid!, g.GroupId!))
.AsEnumerable();
.ToArrayAsync();


return Task.FromResult(
trustSearchEntries
);
return trustSearchEntries;
}
}
6 changes: 6 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data/ITrustProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DfE.FindInformationAcademiesTrusts.Data;

public interface ITrustProvider
{
public Task<Trust?> GetTrustByUidAsync(string uid);
}
2 changes: 1 addition & 1 deletion DfE.FindInformationAcademiesTrusts.Data/ITrustSearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace DfE.FindInformationAcademiesTrusts.Data;

public interface ITrustSearch
{
public Task<IEnumerable<TrustSearchEntry>> SearchAsync(string searchTerm);
public Task<TrustSearchEntry[]> SearchAsync(string searchTerm);
}
2 changes: 1 addition & 1 deletion DfE.FindInformationAcademiesTrusts.Data/Trust.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
namespace DfE.FindInformationAcademiesTrusts.Data;

public record Trust(string Name, string? Ukprn, string Type);
public record Trust(string Uid, string Name, string? Ukprn, string Type);
8 changes: 4 additions & 4 deletions DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ public SearchModel(ITrustProvider trustProvider, ITrustSearch trustSearch)

public string InputId => "search";
[BindProperty(SupportsGet = true)] public string KeyWords { get; set; } = string.Empty;
[BindProperty(SupportsGet = true)] public string TrustId { get; set; } = string.Empty;
[BindProperty(SupportsGet = true)] public string Uid { get; set; } = string.Empty;
public IEnumerable<TrustSearchEntry> Trusts { get; set; } = Array.Empty<TrustSearchEntry>();

public async Task<IActionResult> OnGetAsync()
{
if (!string.IsNullOrWhiteSpace(TrustId))
if (!string.IsNullOrWhiteSpace(Uid))
{
var trust = await _trustProvider.GetTrustByUkprnAsync(TrustId);
var trust = await _trustProvider.GetTrustByUidAsync(Uid);
if (trust != null && string.Equals(trust.Name, KeyWords, StringComparison.CurrentCultureIgnoreCase))
{
return RedirectToPage("/Trusts/Details", new { Uid = TrustId });
return RedirectToPage("/Trusts/Details", new { Uid });
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public ContactsModel(ITrustProvider trustProvider)

public async Task<IActionResult> OnGetAsync()
{
var trust = await _trustProvider.GetTrustByUkprnAsync(Uid);
var trust = await _trustProvider.GetTrustByUidAsync(Uid);

if (trust == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public DetailsModel(ITrustProvider trustProvider)

public async Task<IActionResult> OnGetAsync()
{
var trust = await _trustProvider.GetTrustByUkprnAsync(Uid);
var trust = await _trustProvider.GetTrustByUidAsync(Uid);

if (trust == null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
<div class="govuk-caption-m app-side-navigation__title">@ViewConstants.AboutTheTrustSectionName</div>
<ul class="ds_side-navigation__list app-side-navigation__list">
<li class="ds_side-navigation__item app-side-navigation-highlight-item">
<a asp-page="./Details" asp-route-ukprn="@Model.Trust.Ukprn" class="ds_side-navigation__link govuk-body govuk-link govuk-link--no-visited-state @GetCurrentLinkClassIf("Details")">
<a asp-page="./Details" asp-route-uid="@Model.Trust.Uid" class="ds_side-navigation__link govuk-body govuk-link govuk-link--no-visited-state @GetCurrentLinkClassIf("Details")">
<span class="visually-hidden">Trust</span>Details
</a>
</li>
<li class="ds_side-navigation__item app-side-navigation-highlight-item">
<a asp-page="./Contacts" asp-route-ukprn="@Model.Trust.Ukprn" class="ds_side-navigation__link govuk-body govuk-link govuk-link--no-visited-state @GetCurrentLinkClassIf("Contacts")">
<a asp-page="./Contacts" asp-route-uid="@Model.Trust.Uid" class="ds_side-navigation__link govuk-body govuk-link govuk-link--no-visited-state @GetCurrentLinkClassIf("Contacts")">
<span class="visually-hidden">Trust</span>Contacts
</a>
</li>
Expand Down
66 changes: 0 additions & 66 deletions DfE.FindInformationAcademiesTrusts/TrustProvider.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.Models;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.UnitTests.Mocks;

public class MockAcademiesDbContext : Mock<IAcademiesDbContext>
{
public List<Group> SetupMockDbContextGroups(int numMatches)
{
var groups = new List<Group>();
for (var i = 0; i < numMatches; i++)
{
groups.Add(new Group
{
GroupName = $"trust {i}", GroupUid = $"{i}", GroupId = $"TR0{i}", GroupType = "Multi-academy trust"
});
}

Setup(academiesDbContext => academiesDbContext.Groups)
.Returns(new MockDbSet<Group>(groups).Object);

return groups;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.UnitTests.Mocks;

/// <summary>
/// Use this to create a mock DbSet from a collection of items. The mock DbSet points to the collection so changes to
/// the original collection will be reflected in the mock DbSet and vice versa
/// Adapted from https://jason-ge.medium.com/mock-async-data-repository-in-asp-net-core-3-1-634cb19a3013
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class MockDbSet<TEntity> : Mock<DbSet<TEntity>> where TEntity : class
{
public MockDbSet(IEnumerable<TEntity> items)
{
var itemsAsQueryable = items.AsQueryable();

As<IAsyncEnumerable<TEntity>>()
.Setup(x => x.GetAsyncEnumerator(default))
.Returns(new TestAsyncEnumerator<TEntity>(itemsAsQueryable.GetEnumerator()));
As<IQueryable<TEntity>>()
.Setup(m => m.Provider)
.Returns(new TestAsyncQueryProvider<TEntity>(itemsAsQueryable.Provider));
As<IQueryable<TEntity>>()
.Setup(m => m.Expression).Returns(itemsAsQueryable.Expression);
As<IQueryable<TEntity>>()
.Setup(m => m.ElementType).Returns(itemsAsQueryable.ElementType);
As<IQueryable<TEntity>>()
.Setup(m => m.GetEnumerator()).Returns(itemsAsQueryable.GetEnumerator());
}

public sealed override Mock<TInterface> As<TInterface>()
{
return base.As<TInterface>();
}

private class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _enumerator;

public TestAsyncEnumerator(IEnumerator<T> enumerator)
{
_enumerator = enumerator;
}

public T Current => _enumerator.Current;

public ValueTask DisposeAsync()
{
return new ValueTask(Task.Run(() => _enumerator.Dispose()));
}

public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_enumerator.MoveNext());
}
}

private class TestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public TestAsyncEnumerable(Expression expression)
: base(expression)
{
}

IQueryProvider IQueryable.Provider => new TestAsyncQueryProvider<T>(this);

public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token)
{
return new TestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
}

private class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
private readonly IQueryProvider _innerQueryProvider;

internal TestAsyncQueryProvider(IQueryProvider innerQueryProvider)
{
_innerQueryProvider = innerQueryProvider;
}

public IQueryable CreateQuery(Expression expression)
{
return new TestAsyncEnumerable<T>(expression);
}

public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(expression);
}

public object? Execute(Expression expression)
{
return _innerQueryProvider.Execute(expression);
}

public TResult Execute<TResult>(Expression expression)
{
return _innerQueryProvider.Execute<TResult>(expression);
}

public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = new())
{
var expectedResultType = typeof(TResult).GetGenericArguments()[0];
var executionResult = Execute(expression);

return (TResult)(typeof(Task).GetMethod(nameof(Task.FromResult))!
.MakeGenericMethod(expectedResultType)
.Invoke(null, new[] { executionResult }) ?? throw new Exception());
}
}
}
Loading

0 comments on commit be71dee

Please sign in to comment.