diff --git a/docs/api/issuer-service.yaml b/docs/api/issuer-service.yaml index d0c9460b..964921eb 100644 --- a/docs/api/issuer-service.yaml +++ b/docs/api/issuer-service.yaml @@ -8,7 +8,12 @@ paths: tags: - 'Org.Eclipse.TractusX.SsiCredentialIssuer.Service, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null' summary: Gets all use case frameworks and the participation status of the acting company - description: 'Example: GET: api/issuer/useCaseParticipation' + description: 'Example: GET: api/issuer/useCaseParticipation

Available values:

All, Active, Expired' + parameters: + - name: status + in: query + schema: + type: string responses: '200': description: OK @@ -30,6 +35,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '409': description: Conflict content: diff --git a/src/database/SsiCredentialIssuer.DbAccess/Models/StatusType.cs b/src/database/SsiCredentialIssuer.DbAccess/Models/StatusType.cs new file mode 100644 index 00000000..39de1391 --- /dev/null +++ b/src/database/SsiCredentialIssuer.DbAccess/Models/StatusType.cs @@ -0,0 +1,8 @@ +namespace Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; + +public enum StatusType +{ + Active, + Expired, + All +} diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs index 220585f8..5cf0aff1 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/CompanySsiDetailsRepository.cs @@ -30,7 +30,7 @@ public class CompanySsiDetailsRepository(IssuerDbContext context) : ICompanySsiDetailsRepository { /// - public IAsyncEnumerable GetUseCaseParticipationForCompany(string bpnl, DateTimeOffset minExpiry) => + public IAsyncEnumerable GetUseCaseParticipationForCompany(string bpnl, DateTimeOffset minExpiry, StatusType? statusType) => context.VerifiedCredentialTypes .Where(t => t.VerifiedCredentialTypeAssignedKind!.VerifiedCredentialTypeKindId == VerifiedCredentialTypeKindId.FRAMEWORK) .Select(t => new @@ -38,6 +38,11 @@ public IAsyncEnumerable GetUseCaseParticipationForComp t.VerifiedCredentialTypeAssignedUseCase!.UseCase, TypeId = t.Id, ExternalTypeDetails = t.VerifiedCredentialTypeAssignedExternalType!.VerifiedCredentialExternalType!.VerifiedCredentialExternalTypeDetailVersions + .Where(x => !statusType.HasValue || statusType == StatusType.All + || (statusType == StatusType.Active + && (x.Expiry > minExpiry)) + || (statusType == StatusType.Expired && x.Expiry <= DateTimeOffset.UtcNow) + ) }) .Select(x => new UseCaseParticipationData( x.UseCase!.Name, diff --git a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs index 6291c544..0be2af40 100644 --- a/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs +++ b/src/database/SsiCredentialIssuer.DbAccess/Repositories/ICompanySsiDetailsRepository.cs @@ -31,8 +31,9 @@ public interface ICompanySsiDetailsRepository /// /// Bpnl of the company /// The minimum datetime the expiry date should have + /// the status type on how the credentials should be filtered /// AsyncEnumerable of UseCaseParticipation - IAsyncEnumerable GetUseCaseParticipationForCompany(string bpnl, DateTimeOffset minExpiry); + IAsyncEnumerable GetUseCaseParticipationForCompany(string bpnl, DateTimeOffset minExpiry, StatusType? statusType); /// /// Gets the company credential details for the given company id diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs index f3721e57..40d79778 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IIssuerBusinessLogic.cs @@ -26,7 +26,7 @@ namespace Org.Eclipse.TractusX.SsiCredentialIssuer.Service.BusinessLogic; public interface IIssuerBusinessLogic { - IAsyncEnumerable GetUseCaseParticipationAsync(); + IAsyncEnumerable GetUseCaseParticipationAsync(string? status); IAsyncEnumerable GetSsiCertificatesAsync(); diff --git a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs index d9a03a3a..76b76636 100644 --- a/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs +++ b/src/issuer/SsiCredentialIssuer.Service/BusinessLogic/IssuerBusinessLogic.cs @@ -85,10 +85,22 @@ public IssuerBusinessLogic( } /// - public IAsyncEnumerable GetUseCaseParticipationAsync() => - _repositories + public IAsyncEnumerable GetUseCaseParticipationAsync(string? status) + { + StatusType? statusTypeResult = null; + if (!string.IsNullOrEmpty(status)) + { + if (!(Enum.TryParse(status, ignoreCase: true, out var statusType) || !Enum.IsDefined(typeof(StatusType), statusType))) + { + throw new ArgumentException($"Status value {status} is not valid; please use Active, Expired or All"); + } + statusTypeResult = statusType; + } + + return _repositories .GetInstance() - .GetUseCaseParticipationForCompany(_identity.Bpnl, _dateTimeProvider.OffsetNow); + .GetUseCaseParticipationForCompany(_identity.Bpnl, _dateTimeProvider.OffsetNow, statusTypeResult); + } /// public IAsyncEnumerable GetSsiCertificatesAsync() => diff --git a/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs b/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs index 55aec7e5..1d12b849 100644 --- a/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs +++ b/src/issuer/SsiCredentialIssuer.Service/Controllers/IssuerController.cs @@ -42,9 +42,10 @@ public static RouteGroupBuilder MapIssuerApi(this RouteGroupBuilder group) { var issuer = group.MapGroup("/issuer"); - issuer.MapGet("useCaseParticipation", (IIssuerBusinessLogic logic) => logic.GetUseCaseParticipationAsync()) + issuer.MapGet("useCaseParticipation", (IIssuerBusinessLogic logic, + [FromQuery(Name = "status")] string? status) => logic.GetUseCaseParticipationAsync(status)) .WithSwaggerDescription("Gets all use case frameworks and the participation status of the acting company", - "Example: GET: api/issuer/useCaseParticipation") + "Example: GET: api/issuer/useCaseParticipation

Available values:

All, Active, Expired") .RequireAuthorization(r => { r.RequireRole("view_use_case_participation"); @@ -52,6 +53,7 @@ public static RouteGroupBuilder MapIssuerApi(this RouteGroupBuilder group) }) .WithDefaultResponses() .Produces(StatusCodes.Status200OK, typeof(IEnumerable), Constants.JsonContentType) + .Produces(StatusCodes.Status400BadRequest, typeof(ErrorResponse), Constants.JsonContentType) .Produces(StatusCodes.Status409Conflict, typeof(ErrorResponse), Constants.JsonContentType); issuer.MapGet("certificates", (IIssuerBusinessLogic logic) => logic.GetSsiCertificatesAsync()) diff --git a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs index c9ed176a..cc12f4dd 100644 --- a/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs +++ b/tests/database/SsiCredentialIssuer.DbAccess.Tests/CompanySsiDetailsRepositoryTests.cs @@ -22,6 +22,7 @@ using FluentAssertions; using Microsoft.EntityFrameworkCore; using Org.Eclipse.TractusX.SsiCredentialIssuer.DbAccess.Tests.Setup; +using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Models; using Org.Eclipse.TractusX.SsiCredentialIssuer.DBAccess.Repositories; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities; using Org.Eclipse.TractusX.SsiCredentialIssuer.Entities.Entities; @@ -49,14 +50,16 @@ public CompanySsiDetailsRepositoryTests(TestDbFixture testDbFixture) #region GetDetailsForCompany - [Fact] - public async Task GetDetailsForCompany_WithValidData_ReturnsExpected() + [Theory] + [InlineData(null)] + [InlineData(StatusType.All)] + public async Task GetDetailsForCompany_WithValidData_And_StatusType_ReturnsExpected(StatusType? statusType) { // Arrange var sut = await CreateSut(); // Act - var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, DateTimeOffset.MinValue).ToListAsync(); + var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, DateTimeOffset.MinValue, statusType).ToListAsync(); // Assert result.Should().HaveCount(10); @@ -84,7 +87,7 @@ public async Task GetDetailsForCompany_WithExpiryFilter_ReturnsExpected() var sut = await CreateSut(); // Act - var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, dt).ToListAsync(); + var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, dt, null).ToListAsync(); // Assert result.Should().HaveCount(10); @@ -104,6 +107,56 @@ public async Task GetDetailsForCompany_WithExpiryFilter_ReturnsExpected() x => x.ExternalDetailData.Version == "3.0" && !x.SsiDetailData.Any()); } + [Fact] + public async Task GetAllExternalTypeDetailDataWithValidData_WithValidData_and_StatusType_Active_ReturnsExpected() + { + // Arrange + var (sut, context) = await CreateSutWithContext(); + + //Act + + var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, DateTimeOffset.UtcNow, StatusType.Active).ToListAsync(); + + // Assert + result.Should().HaveCount(10); + result.SelectMany(x => x.VerifiedCredentials) + .Select(x => x.ExternalDetailData) + .Should().HaveCount(1); + } + + [Fact] + public async Task GetAllExternalTypeDetailDataWithValidData_and_StatusType_All_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + //Act + + var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, DateTimeOffset.UtcNow, StatusType.All).ToListAsync(); + + // Assert + result.Should().HaveCount(10); + result.SelectMany(x => x.VerifiedCredentials) + .Select(x => x.ExternalDetailData) + .Should().HaveCount(11); + } + + [Fact] + public async Task GetAllExternalTypeDetailDataWithValidData_WithValidData_and_StatusType_Expired_ReturnsExpected() + { + // Arrange + var (sut, context) = await CreateSutWithContext(); + //Act + + var result = await sut.GetUseCaseParticipationForCompany(ValidBpnl, DateTimeOffset.UtcNow, StatusType.Expired).ToListAsync(); + + // Assert + result.Should().HaveCount(10); + result.SelectMany(x => x.VerifiedCredentials) + .Select(x => x.ExternalDetailData) + .Should().HaveCount(10); + } + #endregion #region GetAllCredentialDetails diff --git a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs index f159cc07..78d26411 100644 --- a/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs +++ b/tests/issuer/SsiCredentialIssuer.Service.Tests/BusinessLogic/IssuerBusinessLogicTests.cs @@ -123,7 +123,7 @@ public async Task GetUseCaseParticipationAsync_ReturnsExpected() Setup_GetUseCaseParticipationAsync(); // Act - var result = await _sut.GetUseCaseParticipationAsync().ToListAsync(); + var result = await _sut.GetUseCaseParticipationAsync(null).ToListAsync(); // Assert result.Should().HaveCount(5); @@ -163,6 +163,93 @@ public async Task GetCredentialsForBpn_ReturnsExpected() result.Should().HaveCount(5); } + [Fact] + public async Task GetUseCaseParticipationAsync_WithValidStatus_ReturnsExpectedResults() + { + // Arrange + var status = "Active"; + var now = DateTimeOffset.Now; + var verifiedCredentials = _fixture.Build() + .With(x => x.SsiDetailData, _fixture.CreateMany(1)) + .CreateMany(5); + var expectedData = new List + { + new UseCaseParticipationData("Test", "Test", VerifiedCredentialTypeId.TRACEABILITY_FRAMEWORK, verifiedCredentials) + }.ToAsyncEnumerable(); + + A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(now); + A.CallTo(() => _companySsiDetailsRepository.GetUseCaseParticipationForCompany(_identity.Bpnl, now, StatusType.Active)) + .Returns(expectedData); + + // Act + var result = await _sut.GetUseCaseParticipationAsync(status).ToListAsync(); + + // Assert + result.Should().BeEquivalentTo(expectedData.ToEnumerable()); + } + + [Fact] + public async Task GetUseCaseParticipationAsync_WithInvalidStatus_ThrowsArgumentException() + { + // Arrange + var status = "InvalidStatus"; + + // Act + Func act = async () => await _sut.GetUseCaseParticipationAsync(status).ToListAsync(); + + // Assert + await act.Should().ThrowAsync() + .WithMessage("Status value InvalidStatus is not valid; please use Active, Expired or All"); + } + + [Fact] + public async Task GetUseCaseParticipationAsync_WithNullStatus_ReturnsExpectedResults() + { + // Arrange + var now = DateTimeOffset.Now; + var verifiedCredentials = _fixture.Build() + .With(x => x.SsiDetailData, _fixture.CreateMany(1)) + .CreateMany(5); + var expectedData = new List + { + new UseCaseParticipationData("Test", "Test", VerifiedCredentialTypeId.TRACEABILITY_FRAMEWORK, verifiedCredentials) + }.ToAsyncEnumerable(); + + A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(now); + A.CallTo(() => _companySsiDetailsRepository.GetUseCaseParticipationForCompany(_identity.Bpnl, now, null)) + .Returns(expectedData); + + // Act + var result = await _sut.GetUseCaseParticipationAsync(null).ToListAsync(); + + // Assert + result.Should().BeEquivalentTo(expectedData.ToEnumerable()); + } + + [Fact] + public async Task GetUseCaseParticipationAsync_WithAllStatus_ReturnsExpectedResults() + { + // Arrange + var now = DateTimeOffset.Now; + var verifiedCredentials = _fixture.Build() + .With(x => x.SsiDetailData, _fixture.CreateMany(1)) + .CreateMany(5); + var expectedData = new List + { + new UseCaseParticipationData("Test", "Test", VerifiedCredentialTypeId.TRACEABILITY_FRAMEWORK, verifiedCredentials) + }.ToAsyncEnumerable(); + + A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(now); + A.CallTo(() => _companySsiDetailsRepository.GetUseCaseParticipationForCompany(_identity.Bpnl, now, StatusType.All)) + .Returns(expectedData); + + // Act + var result = await _sut.GetUseCaseParticipationAsync("All").ToListAsync(); + + // Assert + result.Should().BeEquivalentTo(expectedData.ToEnumerable()); + } + #endregion #region ApproveCredential @@ -1081,7 +1168,7 @@ private void Setup_GetUseCaseParticipationAsync() var verifiedCredentials = _fixture.Build() .With(x => x.SsiDetailData, _fixture.CreateMany(1)) .CreateMany(5); - A.CallTo(() => _companySsiDetailsRepository.GetUseCaseParticipationForCompany(Bpnl, A._)) + A.CallTo(() => _companySsiDetailsRepository.GetUseCaseParticipationForCompany(Bpnl, A._, null)) .Returns(_fixture.Build().With(x => x.VerifiedCredentials, verifiedCredentials).CreateMany(5).ToAsyncEnumerable()); }