diff --git a/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs index 1a7b3780f7..4f1598213d 100644 --- a/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/CompanyDataBusinessLogic.cs @@ -645,4 +645,23 @@ public async Task DeleteCompanyCertificateAsync(Guid documentId) return await _portalRepositories.SaveAsync().ConfigureAwait(false); } + + /// + public async Task<(string FileName, byte[] Content, string MediaType)> GetCompanyCertificateDocumentAsync(Guid documentId) + { + var documentDetails = await _portalRepositories.GetInstance() + .GetCompanyCertificateDocumentDataAsync(documentId, DocumentTypeId.COMPANY_CERTIFICATE) + .ConfigureAwait(false); + + if (!documentDetails.Exists) + { + throw new NotFoundException($"Company certificate document {documentId} does not exist"); + } + if (!documentDetails.IsStatusLocked) + { + throw new ForbiddenException($"Document {documentId} status is not locked"); + } + + return (documentDetails.FileName, documentDetails.Content, documentDetails.MediaTypeId.MapToMediaType()); + } } diff --git a/src/administration/Administration.Service/BusinessLogic/ICompanyDataBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ICompanyDataBusinessLogic.cs index 98ad3f08dd..a3abfa2241 100644 --- a/src/administration/Administration.Service/BusinessLogic/ICompanyDataBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ICompanyDataBusinessLogic.cs @@ -58,6 +58,8 @@ public interface ICompanyDataBusinessLogic Task CreateCompanyCertificate(CompanyCertificateCreationData data, CancellationToken cancellationToken); + Task<(string FileName, byte[] Content, string MediaType)> GetCompanyCertificateDocumentAsync(Guid documentId); + Task DeleteCompanyCertificateAsync(Guid documentId); Task> GetAllCompanyCertificatesAsync(int page, int size, CertificateSorting? sorting, CompanyCertificateStatusId? certificateStatus, CompanyCertificateTypeId? certificateType); diff --git a/src/administration/Administration.Service/Controllers/CompanyDataController.cs b/src/administration/Administration.Service/Controllers/CompanyDataController.cs index fa91be2f69..ff9dec84f3 100644 --- a/src/administration/Administration.Service/Controllers/CompanyDataController.cs +++ b/src/administration/Administration.Service/Controllers/CompanyDataController.cs @@ -331,6 +331,31 @@ public IAsyncEnumerable GetCompanyCertificatesByBpn(s public Task> GetAllCompanyCertificatesAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] CertificateSorting? sorting = null, [FromQuery] CompanyCertificateStatusId? certificateStatus = null, [FromQuery] CompanyCertificateTypeId? certificateType = null) => _logic.GetAllCompanyCertificatesAsync(page, size, sorting, certificateStatus, certificateType); + /// + /// Retrieves a specific company certificate document for the given id. + /// + /// Id of the document to get. + /// Returns the file. + /// Example: GET /api/administration/companydata/companyCertificates/documents/4ad087bb-80a1-49d3-9ba9-da0b175cd4e3 + /// Returns the file. + /// The document which is not in status "ACTIVE". + /// The document was not found. + /// document Content is null. + [HttpGet] + [Route("companyCertificates/documents/{documentId}")] + [Authorize(Roles = "view_certificates")] + [Authorize(Policy = PolicyTypes.ValidCompany)] + [Authorize(Policy = PolicyTypes.CompanyUser)] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status403Forbidden)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status503ServiceUnavailable)] + public async Task GetCompanyCertificateDocumentContentFileAsync([FromRoute] Guid documentId) + { + var (fileName, content, mediaType) = await _logic.GetCompanyCertificateDocumentAsync(documentId).ConfigureAwait(false); + return File(content, mediaType, fileName); + } + /// /// Deletes the company certificate with the given id /// diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyCertificateRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyCertificateRepository.cs index 16f8e9c799..080a38cade 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyCertificateRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/CompanyCertificateRepository.cs @@ -100,6 +100,13 @@ public IAsyncEnumerable GetCompanyCertificateData(Gui )) .SingleOrDefaultAsync(); + public Task<(byte[] Content, string FileName, MediaTypeId MediaTypeId, bool Exists, bool IsStatusLocked)> GetCompanyCertificateDocumentDataAsync(Guid documentId, DocumentTypeId documentTypeId) => + _context.Documents + .Where(x => x.Id == documentId && + x.DocumentTypeId == documentTypeId) + .Select(x => new ValueTuple(x.DocumentContent, x.DocumentName, x.MediaTypeId, true, x.DocumentStatusId == DocumentStatusId.LOCKED)) + .SingleOrDefaultAsync(); + public Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, IEnumerable CompanyCertificateId, bool IsSameCompany)> GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(Guid documentId, Guid companyId) => _context.Documents .AsNoTracking() diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyCertificateRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyCertificateRepository.cs index 7166701505..3bb40d1092 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyCertificateRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ICompanyCertificateRepository.cs @@ -19,7 +19,6 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; @@ -65,6 +64,13 @@ public interface ICompanyCertificateRepository /// Returns an Pagination Func?>> GetActiveCompanyCertificatePaginationSource(CertificateSorting? sorting, CompanyCertificateStatusId? certificateStatus, CompanyCertificateTypeId? certificateType, Guid companyId); + /// + /// Get the company certificate document data + /// + /// id of the document + /// Returns the document data + Task<(byte[] Content, string FileName, MediaTypeId MediaTypeId, bool Exists, bool IsStatusLocked)> GetCompanyCertificateDocumentDataAsync(Guid documentId, DocumentTypeId documentTypeId); + Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, IEnumerable CompanyCertificateId, bool IsSameCompany)> GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(Guid documentId, Guid companyId); void AttachAndModifyCompanyCertificateDetails(Guid id, Action? initialize, Action updateFields); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs index 9b8ff1a0ec..02283cafe4 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/CompanyDataBusinessLogicTests.cs @@ -42,6 +42,7 @@ public class CompanyDataBusinessLogicTests private readonly IIdentityData _identity; private readonly Guid _traceabilityExternalTypeDetailId = Guid.NewGuid(); private readonly Guid _validCredentialId = Guid.NewGuid(); + private static readonly Guid _validDocumentId = Guid.NewGuid(); private readonly IFixture _fixture; private readonly IPortalRepositories _portalRepositories; private readonly IConsentRepository _consentRepository; @@ -55,7 +56,6 @@ public class CompanyDataBusinessLogicTests private readonly IMailingService _mailingService; private readonly ICustodianService _custodianService; private readonly IDateTimeProvider _dateTimeProvider; - private readonly CompanyDataBusinessLogic _sut; private readonly IIdentityService _identityService; @@ -1670,6 +1670,55 @@ public async Task GetAllCompanyCertificatesAsync_WithSmallSize_GetsExpectedEntri #endregion + #region GetCompanyCertificateDocuments + + [Fact] + public async Task GetCompanyCertificateDocumentAsync_WithValidData_ReturnsExpected() + { + // Arrange + SetupFakesForGetDocument(); + + // Act + var result = await _sut.GetCompanyCertificateDocumentAsync(_validDocumentId).ConfigureAwait(false); + + // Assert + result.Should().NotBeNull(); + result.FileName.Should().Be("test.pdf"); + result.MediaType.Should().Be("application/pdf"); + } + + [Fact] + public async Task GetCompanyCertificateDocumentAsync_WithNotExistingDocument_ThrowsNotFoundException() + { + // Arrange + var documentId = Guid.NewGuid(); + SetupFakesForGetDocument(); + + // Act + async Task Act() => await _sut.GetCompanyCertificateDocumentAsync(documentId).ConfigureAwait(false); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be($"Company certificate document {documentId} does not exist"); + } + + [Fact] + public async Task GetCompanyCertificateDocumentAsync_WithDocumentStatusIsNotLocked_ThrowsNotFoundException() + { + // Arrange + var documentId = new Guid("aaf53459-c36b-408e-a805-0b406ce9751d"); + SetupFakesForGetDocument(); + + // Act + async Task Act() => await _sut.GetCompanyCertificateDocumentAsync(documentId).ConfigureAwait(false); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be($"Document {documentId} status is not locked"); + } + + #endregion + #region DeleteCompanyCertificates [Fact] @@ -1777,5 +1826,13 @@ private void SetupPagination(int count = 5) A.CallTo(() => _portalRepositories.GetInstance()).Returns(_companyCertificateRepository); } + private void SetupFakesForGetDocument() + { + var content = new byte[7]; + A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDataAsync(_validDocumentId, DocumentTypeId.COMPANY_CERTIFICATE)) + .ReturnsLazily(() => new ValueTuple(content, "test.pdf", MediaTypeId.PDF, true, true)); + A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDataAsync(new Guid("aaf53459-c36b-408e-a805-0b406ce9751d"), DocumentTypeId.COMPANY_CERTIFICATE)) + .ReturnsLazily(() => new ValueTuple(content, "test1.pdf", MediaTypeId.PDF, true, false)); + } #endregion } diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyCertificateRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyCertificateRepositoryTests.cs index 4af8442d76..bb447638cb 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyCertificateRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/CompanyCertificateRepositoryTests.cs @@ -183,6 +183,38 @@ public async Task GetCompanyCertificateData_NoResults_ReturnsExpected() #endregion + #region GetCompanyCertificateDocumentContentFile + + [Fact] + public async Task GetCompanyCertificateDocumentContentFile_WithValidData_ReturnsExpectedDocument() + { + // Arrange + var sut = await CreateSut().ConfigureAwait(false); + + // Act + var result = await sut.GetCompanyCertificateDocumentDataAsync(new Guid("aaf53459-c36b-408e-a805-0b406ce9751f"), DocumentTypeId.COMPANY_CERTIFICATE).ConfigureAwait(false); + + // Assert + result.Should().NotBe(default); + result.FileName.Should().Be("AdditionalServiceDetails2.pdf"); + result.MediaTypeId.Should().Be(MediaTypeId.PDF); + } + + [Fact] + public async Task GetCompanyCertificateDocumentContentFile_WithNotExistingDocument_ReturnsDefault() + { + // Arrange + var sut = await CreateSut().ConfigureAwait(false); + + // Act + var result = await sut.GetCompanyCertificateDocumentDataAsync(Guid.NewGuid(), DocumentTypeId.COMPANY_CERTIFICATE).ConfigureAwait(false); + + // Assert + result.Should().Be(default); + } + + #endregion + #region DeleteCertificate [Fact]