Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dp 1049 endpoint get latest mou #1117

Merged
merged 11 commits into from
Jan 10, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using AutoMapper;
using CO.CDP.Organisation.WebApi.Model;
using CO.CDP.Organisation.WebApi.Tests.AutoMapper;
using CO.CDP.Organisation.WebApi.UseCase;
using CO.CDP.OrganisationInformation;
using CO.CDP.OrganisationInformation.Persistence;
using FluentAssertions;
using Moq;
using Persistence = CO.CDP.OrganisationInformation.Persistence;
using Person = CO.CDP.OrganisationInformation.Persistence.Person;

namespace CO.CDP.Organisation.WebApi.Tests.UseCase;

public class GetLatestMouUseCaseTest(AutoMapperFixture mapperFixture)
: IClassFixture<AutoMapperFixture>
{
private readonly Mock<IOrganisationRepository> _organisationRepository = new();
private GetLatestMouUseCase _useCase => new GetLatestMouUseCase(_organisationRepository.Object, mapperFixture.Mapper);

[Fact]
public async Task Execute_ShouldReturnMappedMou_WhenLatestMouExists()
{
var latestMouEntity = new Persistence.Mou
{
Id = 1,
Guid = Guid.NewGuid(),
FilePath = "/path/to/mou.pdf",
CreatedOn = DateTimeOffset.UtcNow,
UpdatedOn = DateTimeOffset.UtcNow
};

var mappedMou = new CO.CDP.Organisation.WebApi.Model.Mou
{
Id = latestMouEntity.Guid,
FilePath = latestMouEntity.FilePath,
CreatedOn = latestMouEntity.CreatedOn
};

_organisationRepository
.Setup(repo => repo.GetLatestMou())
.ReturnsAsync(latestMouEntity);

var result = await _useCase.Execute();

result.Should().BeEquivalentTo(mappedMou);

_organisationRepository.Verify(repo => repo.GetLatestMou(), Times.Once);
}

[Fact]
public async Task Execute_ShouldThrowUnknownMouException_WhenLatestMouIsNull()
{
_organisationRepository
.Setup(repo => repo.GetLatestMou())
.ReturnsAsync((Persistence.Mou)null!);

Func<Task> act = async () => await _useCase.Execute();

await act.Should().ThrowAsync<UnknownMouException>()
.WithMessage("No MOU found.");

_organisationRepository.Verify(repo => repo.GetLatestMou(), Times.Once);
}

public static Person FakePerson(
Guid? guid = null,
string? userUrn = null,
string firstname = "Jon",
string lastname = "doe",
string? email = null,
string phone = "07925123123",
List<string>? scopes = null,
Tenant? tenant = null,
List<(Persistence.Organisation, List<string>)>? organisationsWithScope = null
)
{
scopes = scopes ?? [];
var personGuid = guid ?? Guid.NewGuid();
var person = new Person
{
Guid = personGuid,
UserUrn = userUrn ?? $"urn:fdc:gov.uk:2022:{Guid.NewGuid()}",
FirstName = firstname,
LastName = lastname,
Email = email ?? $"jon{personGuid}@example.com",
Phone = phone,
Scopes = scopes
};
if (tenant != null)
{
person.Tenants.Add(tenant);
}

foreach (var organisationWithScope in organisationsWithScope ?? [])
{
person.PersonOrganisations.Add(
new OrganisationPerson
{
Person = person,
Organisation = organisationWithScope.Item1,
Scopes = organisationWithScope.Item2
}
);
}

return person;
}

private static Persistence.Organisation FakeOrganisation(bool? withBuyerInfo = true)
{
Persistence.Organisation org = new()
{
Id = 1,
Guid = Guid.NewGuid(),
Name = "FakeOrg",
Tenant = new Tenant
{
Guid = Guid.NewGuid(),
Name = "Tenant 101"
},
ContactPoints =
[
new Persistence.Organisation.ContactPoint
{
Email = "contact@test.org"
}
],
Type = OrganisationType.Organisation
};

if (withBuyerInfo == true)
{
var devolvedRegulations = new List<DevolvedRegulation>();
devolvedRegulations.Add(DevolvedRegulation.NorthernIreland);

org.BuyerInfo = new Persistence.Organisation.BuyerInformation
{
BuyerType = "FakeBuyerType",
DevolvedRegulations = devolvedRegulations,
};
}

return org;
}
}
147 changes: 147 additions & 0 deletions Services/CO.CDP.Organisation.WebApi.Tests/UseCase/GetMouUseCaseTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using AutoMapper;
using CO.CDP.Organisation.WebApi.Model;
using CO.CDP.Organisation.WebApi.Tests.AutoMapper;
using CO.CDP.Organisation.WebApi.UseCase;
using CO.CDP.OrganisationInformation;
using CO.CDP.OrganisationInformation.Persistence;
using FluentAssertions;
using Moq;
using Persistence = CO.CDP.OrganisationInformation.Persistence;
using Person = CO.CDP.OrganisationInformation.Persistence.Person;

namespace CO.CDP.Organisation.WebApi.Tests.UseCase;

public class GetMouUseCaseTest(AutoMapperFixture mapperFixture)
: IClassFixture<AutoMapperFixture>
{
private readonly Mock<IOrganisationRepository> _organisationRepository = new();
private GetMouUseCase _useCase => new GetMouUseCase(_organisationRepository.Object, mapperFixture.Mapper);

[Fact]
public async Task Execute_ShouldReturnMappedMou_WhenLatestMouExists()
{
var mouId = Guid.NewGuid();
var mouEntity = new Persistence.Mou
{
Id = 1,
Guid = mouId,
FilePath = "/path/to/mou.pdf",
CreatedOn = DateTimeOffset.UtcNow,
UpdatedOn = DateTimeOffset.UtcNow
};

var mappedMou = new Model.Mou
{
Id = mouId,
FilePath = mouEntity.FilePath,
CreatedOn = mouEntity.CreatedOn
};

_organisationRepository
.Setup(repo => repo.GetMou(mouId))
.ReturnsAsync(mouEntity);

var result = await _useCase.Execute(mouId);

result.Should().BeEquivalentTo(mappedMou);

_organisationRepository.Verify(repo => repo.GetMou(mouId), Times.Once);
}

[Fact]
public async Task Execute_ShouldThrowUnknownMouException_WhenLatestMouIsNull()
{
var mouId = Guid.NewGuid();
_organisationRepository
.Setup(repo => repo.GetMou(mouId))
.ReturnsAsync((Persistence.Mou)null!);

Func<Task> act = async () => await _useCase.Execute(mouId);

await act.Should().ThrowAsync<UnknownMouException>()
.WithMessage("No MOU found.");

_organisationRepository.Verify(repo => repo.GetMou(mouId), Times.Once);
}

public static Person FakePerson(
Guid? guid = null,
string? userUrn = null,
string firstname = "Jon",
string lastname = "doe",
string? email = null,
string phone = "07925123123",
List<string>? scopes = null,
Tenant? tenant = null,
List<(Persistence.Organisation, List<string>)>? organisationsWithScope = null
)
{
scopes = scopes ?? [];
var personGuid = guid ?? Guid.NewGuid();
var person = new Person
{
Guid = personGuid,
UserUrn = userUrn ?? $"urn:fdc:gov.uk:2022:{Guid.NewGuid()}",
FirstName = firstname,
LastName = lastname,
Email = email ?? $"jon{personGuid}@example.com",
Phone = phone,
Scopes = scopes
};
if (tenant != null)
{
person.Tenants.Add(tenant);
}

foreach (var organisationWithScope in organisationsWithScope ?? [])
{
person.PersonOrganisations.Add(
new OrganisationPerson
{
Person = person,
Organisation = organisationWithScope.Item1,
Scopes = organisationWithScope.Item2
}
);
}

return person;
}

private static Persistence.Organisation FakeOrganisation(bool? withBuyerInfo = true)
{
Persistence.Organisation org = new()
{
Id = 1,
Guid = Guid.NewGuid(),
Name = "FakeOrg",
Tenant = new Tenant
{
Guid = Guid.NewGuid(),
Name = "Tenant 101"
},
ContactPoints =
[
new Persistence.Organisation.ContactPoint
{
Email = "contact@test.org"
}
],
Type = OrganisationType.Organisation
};

if (withBuyerInfo == true)
{
var devolvedRegulations = new List<DevolvedRegulation>();
devolvedRegulations.Add(DevolvedRegulation.NorthernIreland);

org.BuyerInfo = new Persistence.Organisation.BuyerInformation
{
BuyerType = "FakeBuyerType",
DevolvedRegulations = devolvedRegulations,
};
}

return org;
}
}
63 changes: 61 additions & 2 deletions Services/CO.CDP.Organisation.WebApi/Api/Organisation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ await useCase.Execute((organisationId, keyName))
return app;
}

public static RouteGroupBuilder UseMouEndpoints(this RouteGroupBuilder app)
public static RouteGroupBuilder UseOrganisationMouEndpoints(this RouteGroupBuilder app)
{
app.MapGet("/{organisationId}/mou",
[OrganisationAuthorize(
Expand Down Expand Up @@ -987,7 +987,7 @@ await useCase.Execute((organisationId, mouSignatureId))
[AuthenticationChannel.OneLogin],
[Constants.OrganisationPersonScope.Admin],
OrganisationIdLocation.Path)]
async (Guid organisationId, IUseCase<Guid, MouSignatureLatest> useCase) =>
async (Guid organisationId, IUseCase<Guid, MouSignatureLatest> useCase) =>
await useCase.Execute(organisationId)
.AndThen(mouSignatureLatest => mouSignatureLatest != null ? Results.Ok(mouSignatureLatest) : Results.NotFound()))
.Produces<Model.MouSignatureLatest>(StatusCodes.Status200OK, "application/json")
Expand Down Expand Up @@ -1040,6 +1040,65 @@ await useCase.Execute((organisationId, signMou))
return app;
}

public static RouteGroupBuilder UseMouEndpoints(this RouteGroupBuilder app)
{
app.MapGet("/latest",
[OrganisationAuthorize(
[AuthenticationChannel.OneLogin]
, [Constants.OrganisationPersonScope.Admin],
OrganisationIdLocation.Path
)]
async (IUseCase<Mou> useCase) =>
await useCase.Execute()
.AndThen(mouLatest => mouLatest != null ? Results.Ok(mouLatest) : Results.NotFound()))
.Produces<Model.MouSignatureLatest>(StatusCodes.Status200OK, "application/json")
.Produces<ProblemDetails>(StatusCodes.Status401Unauthorized)
.Produces<ProblemDetails>(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status422UnprocessableEntity)
.Produces<ProblemDetails>(StatusCodes.Status500InternalServerError)
.WithOpenApi(operation =>
{
operation.OperationId = "GetLatestMou";
operation.Description = "Get Latest MOU.";
operation.Summary = "Get Latest MOU to sign.";
operation.Responses["200"].Description = "Latest MOU.";
operation.Responses["401"].Description = "Valid authentication credentials are missing in the request.";
operation.Responses["404"].Description = "Latest Mou information not found.";
operation.Responses["422"].Description = "Unprocessable entity.";
operation.Responses["500"].Description = "Internal server error.";
return operation;
});

app.MapGet("/{mouId}",
[OrganisationAuthorize(
[AuthenticationChannel.OneLogin]
, [Constants.OrganisationPersonScope.Admin],
OrganisationIdLocation.Path
)]
async (Guid mouId, IUseCase<Guid, Mou> useCase) =>
await useCase.Execute(mouId)
.AndThen(mou => mou != null ? Results.Ok(mou) : Results.NotFound()))
.Produces<Model.MouSignatureLatest>(StatusCodes.Status200OK, "application/json")
.Produces<ProblemDetails>(StatusCodes.Status401Unauthorized)
.Produces<ProblemDetails>(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status422UnprocessableEntity)
.Produces<ProblemDetails>(StatusCodes.Status500InternalServerError)
.WithOpenApi(operation =>
{
operation.OperationId = "GetMou";
operation.Description = "Get MOU byId.";
operation.Summary = "Get MOU by ID.";
operation.Responses["200"].Description = "MOU by Id.";
operation.Responses["401"].Description = "Valid authentication credentials are missing in the request.";
operation.Responses["404"].Description = "Mou information not found.";
operation.Responses["422"].Description = "Unprocessable entity.";
operation.Responses["500"].Description = "Internal server error.";
return operation;
});

return app;
}

public static RouteGroupBuilder UseOrganisationPartiesEndpoints(this RouteGroupBuilder app)
{
app.MapGet("/{organisationId}/parties",
Expand Down
Loading
Loading