Skip to content

Commit

Permalink
Merge pull request #4308 from dfe-analytical-services/EES-4531-3
Browse files Browse the repository at this point in the history
EES-4531 Only allow published methodologies to be adopted
  • Loading branch information
benoutram authored Sep 18, 2023
2 parents 31e3cbd + 96ec9e1 commit d4b1487
Show file tree
Hide file tree
Showing 12 changed files with 228 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ public async Task AdoptMethodology()
// Setup methodology owned by a different publication
var methodology = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
{
Publication = new Publication(),
Owner = true
}
}
},
};

var contentDbContextId = Guid.NewGuid().ToString();
Expand Down Expand Up @@ -100,6 +101,45 @@ public async Task AdoptMethodology()
}
}

[Fact]
public async Task AdoptMethodology_CannotAdoptUnpublishedMethodology()
{
var publication = new Publication();

// Setup methodology owned by a different publication
var methodology = new Methodology
{
LatestPublishedVersionId = null, // methodology is unpublished
Publications = new List<PublicationMethodology>
{
new()
{
Publication = new Publication(),
Owner = true
}
},
};

var contentDbContextId = Guid.NewGuid().ToString();

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
{
await context.Publications.AddAsync(publication);
await context.Methodologies.AddAsync(methodology);
await context.SaveChangesAsync();
}

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
{
var service = SetupMethodologyService(
contentDbContext: context);

var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
service.AdoptMethodology(publication.Id, methodology.Id));
Assert.Equal("Cannot adopt an unpublished methodology", exception.Message);
}
}

[Fact]
public async Task AdoptMethodology_AlreadyAdoptedByPublicationFails()
{
Expand All @@ -108,6 +148,7 @@ public async Task AdoptMethodology_AlreadyAdoptedByPublicationFails()
// Setup methodology adopted by this publication
var methodology = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
Expand Down Expand Up @@ -167,14 +208,15 @@ public async Task AdoptMethodology_AdoptingOwnedMethodologyFails()
// Setup methodology owned by this publication
var methodology = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
{
Publication = publication,
Owner = true
}
}
},
};

var contentDbContextId = Guid.NewGuid().ToString();
Expand Down Expand Up @@ -527,7 +569,8 @@ public async Task GetAdoptableMethodologies()
{
var methodology = new Methodology
{
Slug = "test-publication"
LatestPublishedVersionId = Guid.NewGuid(),
Slug = "test-publication",
};

var publication = new Publication
Expand Down Expand Up @@ -569,10 +612,10 @@ public async Task GetAdoptableMethodologies()
var methodologyVersionRepository = new Mock<IMethodologyVersionRepository>(Strict);

methodologyRepository.Setup(mock =>
mock.GetUnrelatedToPublication(adoptingPublication.Id))
mock.GetPublishedMethodologiesUnrelatedToPublication(adoptingPublication.Id))
.ReturnsAsync(ListOf(methodology));

methodologyVersionRepository.Setup(mock => mock.GetLatestVersion(methodology.Id))
methodologyVersionRepository.Setup(mock => mock.GetLatestPublishedVersion(methodology.Id))
.ReturnsAsync(methodologyVersion);

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
Expand Down Expand Up @@ -606,6 +649,70 @@ public async Task GetAdoptableMethodologies()
}
}

[Fact]
public async Task GetAdoptableMethodologies_NoUnpublishedMethodologies()
{
var methodology = new Methodology
{
LatestPublishedVersionId = null, // methodology is unpublished
Slug = "test-publication",
Versions = new List<MethodologyVersion>
{
new()
{
InternalReleaseNote = "Test approval",
Published = null,
PublishingStrategy = Immediately,
Status = Draft,
AlternativeTitle = "Alternative title"
},
},
};

var publication = new Publication
{
Title = "Owning publication",
Methodologies = new List<PublicationMethodology>
{
new()
{
Methodology = methodology,
Owner = true
}
}
};

var adoptingPublication = new Publication();

var contentDbContextId = Guid.NewGuid().ToString();

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
{
await context.Publications.AddRangeAsync(publication, adoptingPublication);
await context.Methodologies.AddAsync(methodology);
await context.SaveChangesAsync();
}

var methodologyRepository = new Mock<IMethodologyRepository>(Strict);
methodologyRepository.Setup(mock =>
mock.GetPublishedMethodologiesUnrelatedToPublication(adoptingPublication.Id))
.ReturnsAsync(new List<Methodology>());

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
{
var service = SetupMethodologyService(
contentDbContext: context,
methodologyRepository: methodologyRepository.Object);

var result = await service.GetAdoptableMethodologies(adoptingPublication.Id);
var adoptableMethodologyList = result.AssertRight();

VerifyAllMocks(methodologyRepository);

Assert.Empty(adoptableMethodologyList);
}
}

[Fact]
public async Task GetAdoptableMethodologies_NoUnrelatedMethodologies()
{
Expand All @@ -622,7 +729,7 @@ public async Task GetAdoptableMethodologies_NoUnrelatedMethodologies()
var methodologyRepository = new Mock<IMethodologyRepository>(Strict);

methodologyRepository.Setup(mock =>
mock.GetUnrelatedToPublication(publication.Id))
mock.GetPublishedMethodologiesUnrelatedToPublication(publication.Id))
.ReturnsAsync(new List<Methodology>());

await using (var context = InMemoryApplicationDbContext(contentDbContextId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using GovUk.Education.ExploreEducationStatistics.Content.Model.Database;
using GovUk.Education.ExploreEducationStatistics.Content.Model.Repository.Interfaces;
using GovUk.Education.ExploreEducationStatistics.Content.Services.Interfaces.Cache;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand All @@ -23,13 +24,16 @@ public class MethodologyMigrationController : ControllerBase
{
private readonly ContentDbContext _context;
private readonly IMethodologyVersionRepository _methodologyVersionRepository;
private readonly IMethodologyCacheService _methodologyCacheService;

public MethodologyMigrationController(
ContentDbContext context,
IMethodologyVersionRepository methodologyVersionRepository)
IMethodologyVersionRepository methodologyVersionRepository,
IMethodologyCacheService methodologyCacheService)
{
_context = context;
_methodologyVersionRepository = methodologyVersionRepository;
_methodologyCacheService = methodologyCacheService;
}

public class MethodologyMigrationResult
Expand Down Expand Up @@ -68,6 +72,7 @@ public async Task<ActionResult<List<MethodologyMigrationResult>>> MigrateMethodo
if (!dryRun)
{
await _context.SaveChangesAsync();
await _methodologyCacheService.UpdateSummariesTree();
}

return results;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,16 @@ public async Task<Either<ActionResult, Unit>> AdoptMethodology(Guid publicationI
.CheckEntityExists<Publication>(publicationId, q =>
q.Include(p => p.Methodologies))
.OnSuccess(_userService.CheckCanAdoptMethodologyForPublication)
.OnSuccessDo(_ => _persistenceHelper.CheckEntityExists<Methodology>(methodologyId))
.OnSuccess<ActionResult, Publication, Unit>(async publication =>
.OnSuccessCombineWith(_ => _persistenceHelper.CheckEntityExists<Methodology>(methodologyId))
.OnSuccess<ActionResult, Tuple<Publication, Methodology>, Unit>(async tuple =>
{
var (publication, methodology) = tuple;

if (methodology.LatestPublishedVersionId == null)
{
throw new ArgumentException("Cannot adopt an unpublished methodology");
}

if (publication.Methodologies.Any(pm => pm.MethodologyId == methodologyId))
{
return ValidationActionResult(CannotAdoptMethodologyAlreadyLinkedToPublication);
Expand Down Expand Up @@ -123,10 +130,16 @@ public async Task<Either<ActionResult, List<MethodologyVersionViewModel>>> GetAd
.OnSuccess(_userService.CheckCanAdoptMethodologyForPublication)
.OnSuccess(async publication =>
{
var methodologies = await _methodologyRepository.GetUnrelatedToPublication(publication.Id);
var latestVersions = await methodologies.SelectAsync(methodology =>
_methodologyVersionRepository.GetLatestVersion(methodology.Id));
return (await latestVersions.SelectAsync(BuildMethodologyVersionViewModel)).ToList();
var publishedMethodologies = await _methodologyRepository
.GetPublishedMethodologiesUnrelatedToPublication(publication.Id);
var latestPublishedVersions = publishedMethodologies
.ToAsyncEnumerable()
.SelectAwait(async methodology =>
await _methodologyVersionRepository.GetLatestPublishedVersion(methodology.Id))
.WhereNotNull();
return await latestPublishedVersions
.SelectAwait(async version => await BuildMethodologyVersionViewModel(version))
.ToListAsync();
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source)
return source.Where(item => item is not null)!;
}

public static IAsyncEnumerable<T> WhereNotNull<T>(this IAsyncEnumerable<T?> source)
where T: class
{
return source.Where(item => item is not null)!;
}

public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self) =>
self.Select((item, index) => (item, index));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,13 @@ public async Task GetOwningPublication()
}

[Fact]
public async Task GetUnrelatedToPublication()
public async Task GetPublishedMethodologiesUnrelatedToPublication()
{
var publication = new Publication();

var methodologyOwnedByThisPublication = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
Expand All @@ -193,6 +194,7 @@ public async Task GetUnrelatedToPublication()

var methodologyAdoptedByThisPublication = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
Expand All @@ -208,8 +210,27 @@ public async Task GetUnrelatedToPublication()
}
};

var unpublishedMethodologyUnrelatedToThisPublication = new Methodology
{
LatestPublishedVersionId = null,
Publications = new List<PublicationMethodology>
{
new()
{
Publication = new Publication(),
Owner = true
},
new()
{
Publication = new Publication(),
Owner = false
}
}
};

var methodologyUnrelatedToThisPublication = new Methodology
{
LatestPublishedVersionId = Guid.NewGuid(),
Publications = new List<PublicationMethodology>
{
new()
Expand All @@ -233,6 +254,7 @@ public async Task GetUnrelatedToPublication()
await contentDbContext.Methodologies.AddRangeAsync(
methodologyOwnedByThisPublication,
methodologyAdoptedByThisPublication,
unpublishedMethodologyUnrelatedToThisPublication,
methodologyUnrelatedToThisPublication
);

Expand All @@ -243,7 +265,7 @@ await contentDbContext.Methodologies.AddRangeAsync(
{
var service = BuildMethodologyRepository(contentDbContext);

var result = await service.GetUnrelatedToPublication(publication.Id);
var result = await service.GetPublishedMethodologiesUnrelatedToPublication(publication.Id);

Assert.Single(result);
Assert.Equal(methodologyUnrelatedToThisPublication.Id, result[0].Id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public interface IMethodologyRepository

Task<List<Guid>> GetAllPublicationIds(Guid methodologyId);

Task<List<Methodology>> GetUnrelatedToPublication(Guid publicationId);
Task<List<Methodology>> GetPublishedMethodologiesUnrelatedToPublication(Guid publicationId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@ public Task<List<Guid>> GetAllPublicationIds(Guid methodologyId)
.ToListAsync();
}

public async Task<List<Methodology>> GetUnrelatedToPublication(Guid publicationId)
public async Task<List<Methodology>> GetPublishedMethodologiesUnrelatedToPublication(Guid publicationId)
{
return await _contentDbContext.Methodologies
.Include(m => m.Publications)
.Where(m => m.Publications.All(pm => pm.PublicationId != publicationId))
.Where(m =>
m.Publications.All(pm => pm.PublicationId != publicationId)
&& m.LatestPublishedVersionId != null)
.ToListAsync();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,18 @@ const AdoptMethodologyForm = ({ methodologies, onCancel, onSubmit }: Props) => {
initialValues={{ methodologyId: '' }}
onSubmit={handleSubmit}
validationSchema={Yup.object<FormValues>({
methodologyId: Yup.string().required('Select a methodology to adopt'),
methodologyId: Yup.string().required(
'Select a published methodology to adopt',
),
})}
>
<Form id="adoptMethodologyForm">
<FormFieldRadioSearchGroup
id="selectMethodology"
legend="Select a methodology"
legend="Select a published methodology"
name="methodologyId"
options={radioOptions}
searchLabel="Search for a methodology"
searchLabel="Search for a published methodology"
/>
<ButtonGroup>
<Button type="submit">Save</Button>
Expand Down
Loading

0 comments on commit d4b1487

Please sign in to comment.