diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppChangeBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/AppChangeBusinessLogic.cs index 3c492d56d9..5672ff9ef1 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/AppChangeBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/AppChangeBusinessLogic.cs @@ -283,4 +283,17 @@ private async Task UpdateTenantUrlAsyncInternal(Guid offerId, Guid subscriptionI await _portalRepositories.SaveAsync().ConfigureAwait(false); } + + /// + public async Task GetActiveAppDocumentTypeDataAsync(Guid appId, Guid companyId) + { + var documentData = new Dictionary>(); + var results = await _portalRepositories.GetInstance().GetActiveOfferDocumentTypeDataAsync(appId, companyId, OfferTypeId.APP, _settings.ActiveAppDocumentTypeIds).ConfigureAwait(false); + var appDocTypeData = results!.GroupBy(d => d.DocumentTypeId).ToDictionary(g => g.Key, g => g.Select(d => new DocumentData(d.DocumentId, d.DocumentName))); + foreach (var doctype in _settings.ActiveAppDocumentTypeIds) + { + documentData.Add(doctype, appDocTypeData.Where(x => x.Key == doctype).SelectMany(x => x.Value)); + } + return new ActiveAppDocumentData(documentData); + } } diff --git a/src/marketplace/Apps.Service/BusinessLogic/AppsSettings.cs b/src/marketplace/Apps.Service/BusinessLogic/AppsSettings.cs index 2ecde22d7b..69650d8897 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/AppsSettings.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/AppsSettings.cs @@ -183,6 +183,14 @@ public class AppsSettings [Required] [DistinctValues("x => x.ClientId")] public IEnumerable SubscriptionManagerRoles { get; set; } = null!; + + /// + /// Active Document Types + /// + [Required] + [EnumEnumeration] + [DistinctValues] + public IEnumerable ActiveAppDocumentTypeIds { get; set; } = null!; } /// diff --git a/src/marketplace/Apps.Service/BusinessLogic/IAppChangeBusinessLogic.cs b/src/marketplace/Apps.Service/BusinessLogic/IAppChangeBusinessLogic.cs index 9a3cf710db..72e375a80f 100644 --- a/src/marketplace/Apps.Service/BusinessLogic/IAppChangeBusinessLogic.cs +++ b/src/marketplace/Apps.Service/BusinessLogic/IAppChangeBusinessLogic.cs @@ -76,4 +76,11 @@ public interface IAppChangeBusinessLogic /// the data to update the url /// Task UpdateTenantUrlAsync(Guid offerId, Guid subscriptionId, UpdateTenantData data, Guid companyId); + + /// + /// Gets the Active App Documents + /// + /// Id of the offer + /// + Task GetActiveAppDocumentTypeDataAsync(Guid appId, Guid companyId); } diff --git a/src/marketplace/Apps.Service/Controllers/AppChangeController.cs b/src/marketplace/Apps.Service/Controllers/AppChangeController.cs index cc72f0d7b6..fb04d8283d 100644 --- a/src/marketplace/Apps.Service/Controllers/AppChangeController.cs +++ b/src/marketplace/Apps.Service/Controllers/AppChangeController.cs @@ -186,4 +186,19 @@ public async Task UpdateTenantUrl([FromRoute] Guid appId, [From await this.WithCompanyId(companyId => _businessLogic.UpdateTenantUrlAsync(appId, subscriptionId, data, companyId)).ConfigureAwait(false); return NoContent(); } + + /// + /// Returns the Active App Documents + /// + /// Id of the app. + /// Example: GET /apps/appchange/{appId}/documents + /// Gets the Active Apps documents + /// If App does not exists + [HttpGet] + [Route("{appId}/documents")] + [Authorize(Roles = "edit_apps")] + [ProducesResponseType(typeof(ActiveAppDocumentData), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)] + public async Task GetActiveAppDocuments([FromRoute] Guid appId) => + await this.WithCompanyId(companyId => _businessLogic.GetActiveAppDocumentTypeDataAsync(appId, companyId)).ConfigureAwait(false); } diff --git a/src/marketplace/Apps.Service/ViewModels/AppData.cs b/src/marketplace/Apps.Service/ViewModels/AppData.cs index 7f03d03e9f..c923c85e1c 100644 --- a/src/marketplace/Apps.Service/ViewModels/AppData.cs +++ b/src/marketplace/Apps.Service/ViewModels/AppData.cs @@ -18,6 +18,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; namespace Org.Eclipse.TractusX.Portal.Backend.Apps.Service.ViewModels; @@ -43,3 +44,9 @@ public record AppData( string Price, Guid LeadPictureId, IEnumerable UseCases); + +/// +/// View model of an application's base data. +/// +/// Id of the App. +public record ActiveAppDocumentData(IDictionary> Documents); diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferRepository.cs index 0ebf3efd4f..85d36a2a5c 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IOfferRepository.cs @@ -487,4 +487,13 @@ public interface IOfferRepository /// /// Task<(bool IsSingleInstance, IEnumerable> ServiceAccountProfiles, string? OfferName)> GetServiceAccountProfileDataForSubscription(Guid subscriptionId); + + /// + /// Gets the Active Offer DocumentType Data + /// + /// + /// + /// + /// + Task?> GetActiveOfferDocumentTypeDataAsync(Guid offerId, Guid userCompanyId, OfferTypeId offerTypeId, IEnumerable documentTypeIds); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferRepository.cs index fd451d0852..6404d394bc 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/OfferRepository.cs @@ -822,4 +822,19 @@ public void AttachAndModifyAppInstanceSetup(Guid appInstanceSetupId, Guid offerI o.Offer.Name )) .SingleOrDefaultAsync(); + + /// + public Task?> GetActiveOfferDocumentTypeDataAsync(Guid offerId, Guid userCompanyId, OfferTypeId offerTypeId, IEnumerable documentTypeIds) => + _context.Offers + .Where(o => o.Id == offerId && + o.OfferStatusId == OfferStatusId.ACTIVE && + o.OfferTypeId == offerTypeId && + o.ProviderCompanyId == userCompanyId) + .Select(o => o.Documents.Where(doc => documentTypeIds.Contains(doc.DocumentTypeId)) + .Select(doc => new DocumentTypeData( + doc.DocumentTypeId, + doc.Id, + doc.DocumentName))) + .SingleOrDefaultAsync(); + } diff --git a/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppChangeBusinessLogicTest.cs b/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppChangeBusinessLogicTest.cs index 77fdd705ba..28f32cfaff 100644 --- a/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppChangeBusinessLogicTest.cs +++ b/tests/marketplace/Apps.Service.Tests/BusinessLogic/AppChangeBusinessLogicTest.cs @@ -22,7 +22,10 @@ using AutoFixture.AutoFakeItEasy; using FakeItEasy; using FluentAssertions; +using Flurl.Util; using Microsoft.Extensions.Options; +using MimeKit.Encodings; +using Org.BouncyCastle.Utilities.Collections; using Org.Eclipse.TractusX.Portal.Backend.Apps.Service.ViewModels; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; @@ -92,6 +95,13 @@ public AppChangeBusinessLogicTest() CompanyAdminRoles = new[] { new UserRoleConfig(ClientId, new [] { "Company Admin" }) + }, + ActiveAppDocumentTypeIds = new[] + { + DocumentTypeId.APP_IMAGE, + DocumentTypeId.APP_TECHNICAL_INFORMATION, + DocumentTypeId.APP_CONTRACT, + DocumentTypeId.ADDITIONAL_DETAILS } }; A.CallTo(() => _portalRepositories.GetInstance()).Returns(_notificationRepository); @@ -941,4 +951,35 @@ public async Task UpdateTenantUrlAsync_WithoutSubscriptionDetails_ThrowsConflict } #endregion + + #region GetActiveAppDocumentTypeDataAsync + + [Fact] + public async Task GetActiveAppDocumentTypeDataAsync_ReturnsExpected() + { + // Arrange + var appId = _fixture.Create(); + var documentId1 = _fixture.Create(); + var documenntData = new[] { + new DocumentTypeData(DocumentTypeId.APP_IMAGE, documentId1, "TestDoc1") + }; + + A.CallTo(() => _offerRepository.GetActiveOfferDocumentTypeDataAsync(A._, A._, OfferTypeId.APP, A>._)) + .Returns(documenntData); + + // Act + var result = await _sut.GetActiveAppDocumentTypeDataAsync(appId, _identity.CompanyId).ConfigureAwait(false); + + // Assert + A.CallTo(() => _offerRepository.GetActiveOfferDocumentTypeDataAsync(A._, A._, OfferTypeId.APP, A>._)).MustHaveHappened(); + result.Documents.Should().NotBeNull().And.HaveCount(4).And.Satisfy( + x => x.Key == DocumentTypeId.APP_IMAGE && x.Value.Any(y => y!.DocumentId == documentId1 && y.DocumentName == "TestDoc1"), + x => x.Key == DocumentTypeId.APP_TECHNICAL_INFORMATION && !x.Value.Any(), + x => x.Key == DocumentTypeId.APP_CONTRACT && !x.Value.Any(), + x => x.Key == DocumentTypeId.ADDITIONAL_DETAILS && !x.Value.Any() + ); + } + + #endregion + } diff --git a/tests/marketplace/Apps.Service.Tests/Controllers/AppChangeControllerTest.cs b/tests/marketplace/Apps.Service.Tests/Controllers/AppChangeControllerTest.cs index 4ac2a93396..9b76076546 100644 --- a/tests/marketplace/Apps.Service.Tests/Controllers/AppChangeControllerTest.cs +++ b/tests/marketplace/Apps.Service.Tests/Controllers/AppChangeControllerTest.cs @@ -147,4 +147,18 @@ public async Task UpdateTenantUrl_ReturnsExpected() A.CallTo(() => _logic.UpdateTenantUrlAsync(appId, subscriptionId, data, _identity.CompanyId)).MustHaveHappened(); result.Should().BeOfType(); } + + [Fact] + public async Task GetActiveAppDocuments_ReturnsExpected() + { + //Arrange + var appId = _fixture.Create(); + + //Act + await this._controller.GetActiveAppDocuments(appId).ConfigureAwait(false); + + //Assert + A.CallTo(() => _logic.GetActiveAppDocumentTypeDataAsync(appId, _identity.CompanyId)).MustHaveHappened(); + + } } diff --git a/tests/marketplace/Apps.Service.Tests/appsettings.IntegrationTests.json b/tests/marketplace/Apps.Service.Tests/appsettings.IntegrationTests.json index 643dcfd6a3..8e4a9b34f3 100644 --- a/tests/marketplace/Apps.Service.Tests/appsettings.IntegrationTests.json +++ b/tests/marketplace/Apps.Service.Tests/appsettings.IntegrationTests.json @@ -111,6 +111,12 @@ "APP_TECHNICAL_INFORMATION", "CONFORMITY_APPROVAL_BUSINESS_APPS" ], + "ActiveAppDocumentTypeIds": [ + "APP_IMAGE", + "APP_TECHNICAL_INFORMATION", + "APP_CONTRACT", + "ADDITIONAL_DETAILS" + ], "ITAdminRoles": [ { "ClientId": "Cl2-CX-Portal", diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferRepositoryTests.cs index 10d41d9697..68ebba0675 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/OfferRepositoryTests.cs @@ -1377,6 +1377,31 @@ public async Task GetCompanyProvidedServiceStatusDataAsync_ReturnsExpectedResult #endregion + #region GetActiveOfferDocumentTypeData + + [Fact] + public async Task GetActiveOfferDocumentTypeDataAsync_ReturnsExpectedResult() + { + // Arrange + var activeDocumentType = new[]{ + DocumentTypeId.APP_IMAGE, + DocumentTypeId.APP_TECHNICAL_INFORMATION, + DocumentTypeId.APP_CONTRACT, + DocumentTypeId.ADDITIONAL_DETAILS + }; + var sut = await CreateSut().ConfigureAwait(false); + + // Act + var result = await sut.GetActiveOfferDocumentTypeDataAsync(new("ac1cf001-7fbc-1f2f-817f-bce0572c0007"), new("2dc4249f-b5ca-4d42-bef1-7a7a950a4f87"), OfferTypeId.APP, activeDocumentType).ConfigureAwait(false); + + // Assert + result.Should().NotBeNull().And.HaveCount(1).And.Satisfy( + x => x.DocumentId == new Guid("e020787d-1e04-4c0b-9c06-bd1cd44724b2") && x.DocumentName == "Default_App_Image.png" && x.DocumentTypeId == DocumentTypeId.APP_IMAGE + ); + } + + #endregion + #region Setup private async Task CreateSut()