From 03f9dcbcf8d7760e5d5e8d13b51eb17b72c09027 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 25 Jun 2024 12:36:42 +0200 Subject: [PATCH 1/7] feat(clearinghouse): add feature toggle for sd connectivity (#793) Refs: #792 Reviewed-By: Evelyn Gurschler --- .../BusinessLogic/ConnectorsBusinessLogic.cs | 99 +++++++++---------- .../BusinessLogic/ConnectorsSettings.cs | 5 + .../BusinessLogic/SdFactoryBusinessLogic.cs | 48 +++++---- .../SdFactory.Library/SdFactoryService.cs | 22 +---- .../SdFactory.Library/SdFactorySettings.cs | 5 + .../ConnectorsBusinessLogicTests.cs | 76 +++++++++++--- .../SdFactoryBusinessLogicTests.cs | 34 +++++-- .../SdFactoryServiceTests.cs | 2 +- 8 files changed, 173 insertions(+), 118 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs index 045184886d..f69e9f7f89 100644 --- a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs @@ -39,31 +39,17 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.BusinessLog /// /// Implementation of making use of to retrieve data. /// -public class ConnectorsBusinessLogic : IConnectorsBusinessLogic +public class ConnectorsBusinessLogic( + IPortalRepositories portalRepositories, + IOptions options, + ISdFactoryBusinessLogic sdFactoryBusinessLogic, + IIdentityService identityService, + ILogger logger) + : IConnectorsBusinessLogic { - private readonly IPortalRepositories _portalRepositories; - private readonly ISdFactoryBusinessLogic _sdFactoryBusinessLogic; - private readonly IIdentityData _identityData; - private readonly ILogger _logger; - private readonly ConnectorsSettings _settings; - private static readonly Regex bpnRegex = new(@"(\w|\d){16}", RegexOptions.None, TimeSpan.FromSeconds(1)); - - /// - /// Constructor. - /// - /// Access to the needed repositories - /// The options - /// Access to the connectorsSdFactory - /// Access to the current logged in user - /// Access to the logger - public ConnectorsBusinessLogic(IPortalRepositories portalRepositories, IOptions options, ISdFactoryBusinessLogic sdFactoryBusinessLogic, IIdentityService identityService, ILogger logger) - { - _portalRepositories = portalRepositories; - _settings = options.Value; - _sdFactoryBusinessLogic = sdFactoryBusinessLogic; - _identityData = identityService.IdentityData; - _logger = logger; - } + private static readonly Regex BpnRegex = new(@"(\w|\d){16}", RegexOptions.None, TimeSpan.FromSeconds(1)); + private readonly IIdentityData _identityData = identityService.IdentityData; + private readonly ConnectorsSettings _settings = options.Value; /// public Task> GetAllCompanyConnectorDatas(int page, int size) => @@ -71,7 +57,7 @@ public ConnectorsBusinessLogic(IPortalRepositories portalRepositories, IOptions< page, size, _settings.MaxPageSize, - _portalRepositories.GetInstance().GetAllCompanyConnectorsForCompanyId(_identityData.CompanyId)); + portalRepositories.GetInstance().GetAllCompanyConnectorsForCompanyId(_identityData.CompanyId)); /// public Task> GetManagedConnectorForCompany(int page, int size) => @@ -79,12 +65,12 @@ public ConnectorsBusinessLogic(IPortalRepositories portalRepositories, IOptions< page, size, _settings.MaxPageSize, - _portalRepositories.GetInstance().GetManagedConnectorsForCompany(_identityData.CompanyId)); + portalRepositories.GetInstance().GetManagedConnectorsForCompany(_identityData.CompanyId)); public async Task GetCompanyConnectorData(Guid connectorId) { var companyId = _identityData.CompanyId; - var result = await _portalRepositories.GetInstance().GetConnectorByIdForCompany(connectorId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetConnectorByIdForCompany(connectorId, companyId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) { throw NotFoundException.Create(AdministrationConnectorErrors.CONNECTOR_NOT_FOUND, new ErrorParameter[] { new("connectorId", connectorId.ToString()) }); @@ -109,7 +95,7 @@ private async Task CreateConnectorInternalAsync(ConnectorInputModel connec var (name, connectorUrl, location, technicalUserId) = connectorInputModel; await CheckLocationExists(location); - var result = await _portalRepositories + var result = await portalRepositories .GetInstance() .GetCompanyBpnAndSelfDescriptionDocumentByIdAsync(companyId) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -140,7 +126,7 @@ private async Task CreateManagedConnectorInternalAsync(ManagedConnectorInp var (name, connectorUrl, location, subscriptionId, technicalUserId) = connectorInputModel; await CheckLocationExists(location).ConfigureAwait(ConfigureAwaitOptions.None); - var result = await _portalRepositories.GetInstance() + var result = await portalRepositories.GetInstance() .CheckOfferSubscriptionWithOfferProvider(subscriptionId, companyId) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -188,7 +174,7 @@ private async Task CreateManagedConnectorInternalAsync(ManagedConnectorInp private async Task CheckLocationExists(string location) { - if (!await _portalRepositories.GetInstance() + if (!await portalRepositories.GetInstance() .CheckCountryExistsByAlpha2CodeAsync(location.ToUpper()).ConfigureAwait(ConfigureAwaitOptions.None)) { throw ControllerArgumentException.Create(AdministrationConnectorErrors.CONNECTOR_ARGUMENT_LOCATION_NOT_EXIST, new ErrorParameter[] { new("location", location) }); @@ -202,7 +188,7 @@ private async Task ValidateTechnicalUser(Guid? technicalUserId, Guid companyId) return; } - if (!await _portalRepositories.GetInstance() + if (!await portalRepositories.GetInstance() .CheckActiveServiceAccountExistsForCompanyAsync(technicalUserId.Value, companyId).ConfigureAwait(ConfigureAwaitOptions.None)) { throw ControllerArgumentException.Create(AdministrationConnectorErrors.CONNECTOR_ARGUMENT_TECH_USER_NOT_ACTIVE, new ErrorParameter[] { new("technicalUserId", technicalUserId.Value.ToString()), new("companyId", companyId.ToString()) }); @@ -218,7 +204,7 @@ private async Task CreateAndRegisterConnectorAsync( { var (name, connectorUrl, type, location, provider, host, technicalUserId) = connectorInputModel; - var connectorsRepository = _portalRepositories.GetInstance(); + var connectorsRepository = portalRepositories.GetInstance(); var createdConnector = connectorsRepository.CreateConnector( name, location.ToUpper(), @@ -229,7 +215,7 @@ private async Task CreateAndRegisterConnectorAsync( connector.HostId = host; connector.TypeId = type; connector.DateLastChanged = DateTimeOffset.UtcNow; - connector.StatusId = ConnectorStatusId.PENDING; + connector.StatusId = _settings.ClearinghouseConnectDisabled ? ConnectorStatusId.ACTIVE : ConnectorStatusId.PENDING; if (technicalUserId != null) { connector.CompanyServiceAccountId = technicalUserId; @@ -241,12 +227,15 @@ private async Task CreateAndRegisterConnectorAsync( connectorsRepository.CreateConnectorAssignedSubscriptions(createdConnector.Id, subscriptionId.Value); } - var selfDescriptionDocumentUrl = $"{_settings.SelfDescriptionDocumentUrl}/{selfDescriptionDocumentId}"; - await _sdFactoryBusinessLogic - .RegisterConnectorAsync(createdConnector.Id, selfDescriptionDocumentUrl, businessPartnerNumber, cancellationToken) - .ConfigureAwait(ConfigureAwaitOptions.None); + if (!_settings.ClearinghouseConnectDisabled) + { + var selfDescriptionDocumentUrl = $"{_settings.SelfDescriptionDocumentUrl}/{selfDescriptionDocumentId}"; + await sdFactoryBusinessLogic + .RegisterConnectorAsync(createdConnector.Id, selfDescriptionDocumentUrl, businessPartnerNumber, cancellationToken) + .ConfigureAwait(ConfigureAwaitOptions.None); + } - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return createdConnector.Id; } @@ -254,7 +243,7 @@ await _sdFactoryBusinessLogic public async Task DeleteConnectorAsync(Guid connectorId) { var companyId = _identityData.CompanyId; - var connectorsRepository = _portalRepositories.GetInstance(); + var connectorsRepository = portalRepositories.GetInstance(); var result = await connectorsRepository.GetConnectorDeleteDataAsync(connectorId, companyId).ConfigureAwait(ConfigureAwaitOptions.None) ?? throw NotFoundException.Create(AdministrationConnectorErrors.CONNECTOR_NOT_FOUND, new ErrorParameter[] { new("connectorId", connectorId.ToString()) }); if (!result.IsProvidingOrHostCompany) { @@ -262,7 +251,7 @@ public async Task DeleteConnectorAsync(Guid connectorId) } if (result.ServiceAccountId.HasValue && result.UserStatusId != UserStatusId.INACTIVE) { - _portalRepositories.GetInstance().AttachAndModifyIdentity(result.ServiceAccountId.Value, null, i => + portalRepositories.GetInstance().AttachAndModifyIdentity(result.ServiceAccountId.Value, null, i => { i.UserStatusId = UserStatusId.INACTIVE; }); @@ -286,7 +275,7 @@ public async Task DeleteConnectorAsync(Guid connectorId) private async Task DeleteConnector(Guid connectorId, IEnumerable connectorOfferSubscriptions, Guid selfDescriptionDocumentId, DocumentStatusId documentStatus, IConnectorsRepository connectorsRepository) { - _portalRepositories.GetInstance().AttachAndModifyDocument( + portalRepositories.GetInstance().AttachAndModifyDocument( selfDescriptionDocumentId, a => { a.DocumentStatusId = documentStatus; }, a => { a.DocumentStatusId = DocumentStatusId.INACTIVE; }); @@ -301,22 +290,22 @@ private async Task DeleteUpdateConnectorDetail(Guid connectorId, IConnectorsRepo con.StatusId = ConnectorStatusId.INACTIVE; con.DateLastChanged = DateTimeOffset.UtcNow; }); - await _portalRepositories.SaveAsync(); + await portalRepositories.SaveAsync(); } private async Task DeleteConnectorWithDocuments(Guid connectorId, Guid selfDescriptionDocumentId, IEnumerable connectorOfferSubscriptions, IConnectorsRepository connectorsRepository) { - _portalRepositories.GetInstance().RemoveDocument(selfDescriptionDocumentId); + portalRepositories.GetInstance().RemoveDocument(selfDescriptionDocumentId); RemoveConnectorAssignedOfferSubscriptions(connectorId, connectorOfferSubscriptions, connectorsRepository); connectorsRepository.DeleteConnector(connectorId); - await _portalRepositories.SaveAsync(); + await portalRepositories.SaveAsync(); } private async Task DeleteConnectorWithoutDocuments(Guid connectorId, IEnumerable connectorOfferSubscriptions, IConnectorsRepository connectorsRepository) { RemoveConnectorAssignedOfferSubscriptions(connectorId, connectorOfferSubscriptions, connectorsRepository); connectorsRepository.DeleteConnector(connectorId); - await _portalRepositories.SaveAsync(); + await portalRepositories.SaveAsync(); } private static void RemoveConnectorAssignedOfferSubscriptions(Guid connectorId, IEnumerable connectorOfferSubscriptions, IConnectorsRepository connectorsRepository) @@ -339,10 +328,10 @@ public IAsyncEnumerable GetCompanyConnectorEndPointAsync( { bpns ??= Enumerable.Empty(); - bpns.Where(bpn => !bpnRegex.IsMatch(bpn)).IfAny(invalid => + bpns.Where(bpn => !BpnRegex.IsMatch(bpn)).IfAny(invalid => throw ControllerArgumentException.Create(AdministrationConnectorErrors.CONNECTOR_ARGUMENT_INCORRECT_BPN, new ErrorParameter[] { new("bpns", string.Join(", ", invalid)) })); - return _portalRepositories.GetInstance() + return portalRepositories.GetInstance() .GetConnectorEndPointDataAsync(bpns.Select(x => x.ToUpper())) .PreSortedGroupBy(data => data.BusinessPartnerNumber) .Select(group => @@ -354,9 +343,9 @@ public IAsyncEnumerable GetCompanyConnectorEndPointAsync( /// public async Task ProcessClearinghouseSelfDescription(SelfDescriptionResponseData data, CancellationToken cancellationToken) { - _logger.LogInformation("Process SelfDescription called with the following data {Data}", data); + logger.LogInformation("Process SelfDescription called with the following data {Data}", data.ToString().Replace(Environment.NewLine, string.Empty)); - var result = await _portalRepositories.GetInstance() + var result = await portalRepositories.GetInstance() .GetConnectorDataById(data.ExternalId) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -370,8 +359,8 @@ public async Task ProcessClearinghouseSelfDescription(SelfDescriptionResponseDat throw ConflictException.Create(AdministrationConnectorErrors.CONNECTOR_CONFLICT_ALREADY_ASSIGNED, new ErrorParameter[] { new("externalId", data.ExternalId.ToString()) }); } - await _sdFactoryBusinessLogic.ProcessFinishSelfDescriptionLpForConnector(data, _identityData.IdentityId, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await sdFactoryBusinessLogic.ProcessFinishSelfDescriptionLpForConnector(data, _identityData.IdentityId, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } /// @@ -383,7 +372,7 @@ public Task UpdateConnectorUrl(Guid connectorId, ConnectorUpdateRequest data) private async Task UpdateConnectorUrlInternal(Guid connectorId, ConnectorUpdateRequest data) { - var connectorsRepository = _portalRepositories + var connectorsRepository = portalRepositories .GetInstance(); var connector = await connectorsRepository .GetConnectorUpdateInformation(connectorId, _identityData.CompanyId) @@ -411,7 +400,7 @@ private async Task UpdateConnectorUrlInternal(Guid connectorId, ConnectorUpdateR var bpn = connector.Type == ConnectorTypeId.CONNECTOR_AS_A_SERVICE ? connector.Bpn - : await _portalRepositories.GetInstance() + : await portalRepositories.GetInstance() .GetCompanyBpnForIamUserAsync(_identityData.IdentityId) .ConfigureAwait(ConfigureAwaitOptions.None); if (string.IsNullOrWhiteSpace(bpn)) @@ -424,11 +413,11 @@ private async Task UpdateConnectorUrlInternal(Guid connectorId, ConnectorUpdateR con.ConnectorUrl = data.ConnectorUrl; }); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } /// public IAsyncEnumerable GetConnectorOfferSubscriptionData(bool? connectorIdSet) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .GetConnectorOfferSubscriptionData(connectorIdSet, _identityData.CompanyId); } diff --git a/src/administration/Administration.Service/BusinessLogic/ConnectorsSettings.cs b/src/administration/Administration.Service/BusinessLogic/ConnectorsSettings.cs index f19a5d244a..a3890d054d 100644 --- a/src/administration/Administration.Service/BusinessLogic/ConnectorsSettings.cs +++ b/src/administration/Administration.Service/BusinessLogic/ConnectorsSettings.cs @@ -46,6 +46,11 @@ public class ConnectorsSettings /// [Required(AllowEmptyStrings = false)] public string SelfDescriptionDocumentUrl { get; set; } = null!; + + /// + /// If true all sd factory calls are disabled and won't be called. The respective process steps will be skipped. + /// + public bool ClearinghouseConnectDisabled { get; set; } } public static class ConnectorsSettingsExtensions diff --git a/src/externalsystems/SdFactory.Library/BusinessLogic/SdFactoryBusinessLogic.cs b/src/externalsystems/SdFactory.Library/BusinessLogic/SdFactoryBusinessLogic.cs index 91049cbd89..1cf3479903 100644 --- a/src/externalsystems/SdFactory.Library/BusinessLogic/SdFactoryBusinessLogic.cs +++ b/src/externalsystems/SdFactory.Library/BusinessLogic/SdFactoryBusinessLogic.cs @@ -17,6 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -28,19 +29,14 @@ namespace Org.Eclipse.TractusX.Portal.Backend.SdFactory.Library.BusinessLogic; -public class SdFactoryBusinessLogic : ISdFactoryBusinessLogic +public class SdFactoryBusinessLogic( + ISdFactoryService sdFactoryService, + IPortalRepositories portalRepositories, + IApplicationChecklistService checklistService, + IOptions options) + : ISdFactoryBusinessLogic { - private readonly ISdFactoryService _sdFactoryService; - private readonly IPortalRepositories _portalRepositories; - private readonly IApplicationChecklistService _checklistService; - - public SdFactoryBusinessLogic(ISdFactoryService sdFactoryService, IPortalRepositories portalRepositories, - IApplicationChecklistService checklistService) - { - _sdFactoryService = sdFactoryService; - _portalRepositories = portalRepositories; - _checklistService = checklistService; - } + private readonly SdFactorySettings _settings = options.Value; /// public Task RegisterConnectorAsync( @@ -48,11 +44,23 @@ public Task RegisterConnectorAsync( string selfDescriptionDocumentUrl, string businessPartnerNumber, CancellationToken cancellationToken) => - _sdFactoryService.RegisterConnectorAsync(connectorId, selfDescriptionDocumentUrl, businessPartnerNumber, cancellationToken); + sdFactoryService.RegisterConnectorAsync(connectorId, selfDescriptionDocumentUrl, businessPartnerNumber, cancellationToken); /// public async Task StartSelfDescriptionRegistration(IApplicationChecklistService.WorkerChecklistProcessStepData context, CancellationToken cancellationToken) { + if (_settings.ClearinghouseConnectDisabled) + { + return new IApplicationChecklistService.WorkerChecklistProcessStepExecutionResult( + ProcessStepStatusId.DONE, + entry => entry.ApplicationChecklistEntryStatusId = ApplicationChecklistEntryStatusId.DONE, + new[] { ProcessStepTypeId.ACTIVATE_APPLICATION }, + null, + true, + null + ); + } + await RegisterSelfDescriptionInternalAsync(context.ApplicationId, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -70,7 +78,7 @@ private async Task RegisterSelfDescriptionInternalAsync( Guid applicationId, CancellationToken cancellationToken) { - var result = await _portalRepositories.GetInstance() + var result = await portalRepositories.GetInstance() .GetCompanyAndApplicationDetailsWithUniqueIdentifiersAsync(applicationId) .ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) @@ -86,7 +94,7 @@ private async Task RegisterSelfDescriptionInternalAsync( $"BusinessPartnerNumber (bpn) for CompanyApplications {applicationId} company {companyId} is empty"); } - await _sdFactoryService + await sdFactoryService .RegisterSelfDescriptionAsync(applicationId, uniqueIdentifiers, countryCode, businessPartnerNumber, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None); } @@ -94,7 +102,7 @@ await _sdFactoryService public async Task ProcessFinishSelfDescriptionLpForApplication(SelfDescriptionResponseData data, Guid companyId, CancellationToken cancellationToken) { var confirm = ValidateData(data); - var context = await _checklistService + var context = await checklistService .VerifyChecklistEntryAndProcessSteps( data.ExternalId, ApplicationChecklistEntryTypeId.SELF_DESCRIPTION_LP, @@ -106,11 +114,11 @@ public async Task ProcessFinishSelfDescriptionLpForApplication(SelfDescriptionRe if (confirm) { var documentId = await ProcessDocument(SdFactoryResponseModelTitle.LegalPerson, data, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); - _portalRepositories.GetInstance().AttachAndModifyCompany(companyId, null, + portalRepositories.GetInstance().AttachAndModifyCompany(companyId, null, c => { c.SelfDescriptionDocumentId = documentId; }); } - _checklistService.FinalizeChecklistEntryAndProcessSteps( + checklistService.FinalizeChecklistEntryAndProcessSteps( context, null, item => @@ -133,7 +141,7 @@ public async Task ProcessFinishSelfDescriptionLpForConnector(SelfDescriptionResp { documentId = await ProcessDocument(SdFactoryResponseModelTitle.Connector, data, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); } - _portalRepositories.GetInstance().AttachAndModifyConnector(data.ExternalId, null, con => + portalRepositories.GetInstance().AttachAndModifyConnector(data.ExternalId, null, con => { if (documentId != null) { @@ -175,7 +183,7 @@ private async Task ProcessDocument(SdFactoryResponseModelTitle title, Self var documentContent = ms.ToArray(); var hash = SHA512.HashData(documentContent); - var document = _portalRepositories.GetInstance().CreateDocument( + var document = portalRepositories.GetInstance().CreateDocument( $"SelfDescription_{title}.json", documentContent, hash, diff --git a/src/externalsystems/SdFactory.Library/SdFactoryService.cs b/src/externalsystems/SdFactory.Library/SdFactoryService.cs index 10dfb07e41..0a8a4356a5 100644 --- a/src/externalsystems/SdFactory.Library/SdFactoryService.cs +++ b/src/externalsystems/SdFactory.Library/SdFactoryService.cs @@ -30,28 +30,14 @@ namespace Org.Eclipse.TractusX.Portal.Backend.SdFactory.Library; /// /// Service to handle communication with the connectors sd factory /// -public class SdFactoryService : ISdFactoryService +public class SdFactoryService(ITokenService tokenService, IOptions options) : ISdFactoryService { - private readonly ITokenService _tokenService; - private readonly SdFactorySettings _settings; - - /// - /// Creates a new instance of - /// - /// Access to the token service - /// The options - public SdFactoryService( - ITokenService tokenService, - IOptions options) - { - _settings = options.Value; - _tokenService = tokenService; - } + private readonly SdFactorySettings _settings = options.Value; /// public async Task RegisterConnectorAsync(Guid connectorId, string selfDescriptionDocumentUrl, string businessPartnerNumber, CancellationToken cancellationToken) { - var httpClient = await _tokenService.GetAuthorizedClient(_settings, cancellationToken) + var httpClient = await tokenService.GetAuthorizedClient(_settings, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None); var requestModel = new ConnectorSdFactoryRequestModel( connectorId.ToString(), @@ -70,7 +56,7 @@ await httpClient.PostAsJsonAsync(default(string?), requestModel, cancellationTok /// public async Task RegisterSelfDescriptionAsync(Guid applicationId, IEnumerable<(UniqueIdentifierId Id, string Value)> uniqueIdentifiers, string countryCode, string businessPartnerNumber, CancellationToken cancellationToken) { - var httpClient = await _tokenService.GetAuthorizedClient(_settings, cancellationToken) + var httpClient = await tokenService.GetAuthorizedClient(_settings, cancellationToken) .ConfigureAwait(ConfigureAwaitOptions.None); var requestModel = new SdFactoryRequestModel( applicationId.ToString(), diff --git a/src/externalsystems/SdFactory.Library/SdFactorySettings.cs b/src/externalsystems/SdFactory.Library/SdFactorySettings.cs index 30775eca3a..a7b397de94 100644 --- a/src/externalsystems/SdFactory.Library/SdFactorySettings.cs +++ b/src/externalsystems/SdFactory.Library/SdFactorySettings.cs @@ -28,6 +28,11 @@ namespace Org.Eclipse.TractusX.Portal.Backend.SdFactory.Library; /// public class SdFactorySettings : KeyVaultAuthSettings { + /// + /// If true all sd factory calls are disabled and won't be called. The respective process steps will be skipped. + /// + public bool ClearinghouseConnectDisabled { get; set; } + /// /// SD Factory endpoint for registering connectors. /// diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs index b5af392383..4f96886675 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs @@ -61,6 +61,8 @@ public class ConnectorsBusinessLogicTests private readonly ConnectorsBusinessLogic _logic; private readonly IDocumentRepository _documentRepository; private readonly IServiceAccountRepository _serviceAccountRepository; + private readonly IOptions _options; + private readonly IIdentityService _identityService; public ConnectorsBusinessLogicTests() { @@ -75,10 +77,10 @@ public ConnectorsBusinessLogicTests() _sdFactoryBusinessLogic = A.Fake(); _serviceAccountRepository = A.Fake(); _offerSubscriptionRepository = A.Fake(); - var identityService = A.Fake(); + _identityService = A.Fake(); _identity = A.Fake(); _connectors = new List(); - var options = A.Fake>(); + _options = A.Fake>(); var settings = new ConnectorsSettings { MaxPageSize = 15, @@ -92,13 +94,12 @@ public ConnectorsBusinessLogicTests() _documentRepository = A.Fake(); SetupRepositoryMethods(); - A.CallTo(() => options.Value).Returns(settings); - A.CallTo(() => identityService.IdentityData).Returns(_identity); - var logger = A.Fake>(); + A.CallTo(() => _options.Value).Returns(settings); + A.CallTo(() => _identityService.IdentityData).Returns(_identity); SetupIdentity(); - _logic = new ConnectorsBusinessLogic(_portalRepositories, options, _sdFactoryBusinessLogic, identityService, logger); + _logic = new ConnectorsBusinessLogic(_portalRepositories, _options, _sdFactoryBusinessLogic, _identityService, A.Fake>()); } #region GetAllCompanyConnectorDatas @@ -133,19 +134,34 @@ public async Task GetAllCompanyConnectorDatas_WithValidData_ReturnsExpected(int #region Create Connector - [Fact] - public async Task CreateConnectorAsync_WithValidInput_ReturnsCreatedConnectorData() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task CreateConnectorAsync_WithValidInput_ReturnsCreatedConnectorData(bool clearingHouseDisabled) { // Arrange + var sut = new ConnectorsBusinessLogic(_portalRepositories, Options.Create(new ConnectorsSettings + { + MaxPageSize = 15, + ValidCertificationContentTypes = new[] + { + "application/x-pem-file", + "application/x-x509-ca-cert", + "application/pkix-cert" + }, + ClearinghouseConnectDisabled = clearingHouseDisabled + }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + var connectorInput = new ConnectorInputModel("connectorName", "https://test.de", "de", ServiceAccountUserId); // Act - var result = await _logic.CreateConnectorAsync(connectorInput, CancellationToken.None); + var result = await sut.CreateConnectorAsync(connectorInput, CancellationToken.None); // Assert result.Should().NotBeEmpty(); _connectors.Should().HaveCount(1); A.CallTo(() => _connectorsRepository.CreateConnectorAssignedSubscriptions(A._, A._)).MustNotHaveHappened(); + A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A._, A._, A._, A._)).MustHaveHappened(clearingHouseDisabled ? 0 : 1, Times.Exactly); } [Fact] @@ -248,36 +264,66 @@ public async Task CreateConnectorAsync_WithFailingDapsService_ReturnsCreatedConn #region CreateManagedConnectorAsync - [Fact] - public async Task CreateManagedConnectorAsync_WithValidInput_ReturnsCreatedConnectorData() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task CreateManagedConnectorAsync_WithValidInput_ReturnsCreatedConnectorData(bool clearingHouseDisabled) { // Arrange + var sut = new ConnectorsBusinessLogic(_portalRepositories, Options.Create(new ConnectorsSettings + { + MaxPageSize = 15, + ValidCertificationContentTypes = new[] + { + "application/x-pem-file", + "application/x-x509-ca-cert", + "application/pkix-cert" + }, + ClearinghouseConnectDisabled = clearingHouseDisabled + }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + var connectorInput = new ManagedConnectorInputModel("connectorName", "https://test.de", "de", _validOfferSubscriptionId, ServiceAccountUserId); // Act - var result = await _logic.CreateManagedConnectorAsync(connectorInput, CancellationToken.None); + var result = await sut.CreateManagedConnectorAsync(connectorInput, CancellationToken.None); // Assert result.Should().NotBeEmpty(); _connectors.Should().HaveCount(1); A.CallTo(() => _connectorsRepository.CreateConnectorAssignedSubscriptions(A._, _validOfferSubscriptionId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A._, A._, A._, A._)).MustHaveHappened(clearingHouseDisabled ? 0 : 1, Times.Exactly); } - [Fact] - public async Task CreateManagedConnectorAsync_WithTechnicalUser_ReturnsCreatedConnectorData() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task CreateManagedConnectorAsync_WithTechnicalUser_ReturnsCreatedConnectorData(bool clearingHouseDisabled) { // Arrange + var sut = new ConnectorsBusinessLogic(_portalRepositories, Options.Create(new ConnectorsSettings + { + MaxPageSize = 15, + ValidCertificationContentTypes = new[] + { + "application/x-pem-file", + "application/x-x509-ca-cert", + "application/pkix-cert" + }, + ClearinghouseConnectDisabled = clearingHouseDisabled + }), _sdFactoryBusinessLogic, _identityService, A.Fake>()); + var connectorInput = new ManagedConnectorInputModel("connectorName", "https://test.de", "de", _validOfferSubscriptionId, null); SetupTechnicalIdentity(); // Act - var result = await _logic.CreateManagedConnectorAsync(connectorInput, CancellationToken.None); + var result = await sut.CreateManagedConnectorAsync(connectorInput, CancellationToken.None); // Assert result.Should().NotBeEmpty(); _connectors.Should().HaveCount(1); A.CallTo(() => _connectorsRepository.CreateConnectorAssignedSubscriptions(A._, _validOfferSubscriptionId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A._, A._, A._, A._)).MustHaveHappened(clearingHouseDisabled ? 0 : 1, Times.Exactly); } [Fact] diff --git a/tests/externalsystems/SdFactory.Library.Tests/SdFactoryBusinessLogicTests.cs b/tests/externalsystems/SdFactory.Library.Tests/SdFactoryBusinessLogicTests.cs index 67c721ea77..7901edcb0b 100644 --- a/tests/externalsystems/SdFactory.Library.Tests/SdFactoryBusinessLogicTests.cs +++ b/tests/externalsystems/SdFactory.Library.Tests/SdFactoryBusinessLogicTests.cs @@ -17,6 +17,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -41,22 +42,24 @@ public class SdFactoryBusinessLogicTests private static readonly Guid CompanyUserId = new("ac1cf001-7fbc-1f2f-817f-bce058020002"); private readonly Process _process; private static readonly Guid CompanyId = new("b4697623-dd87-410d-abb8-6d4f4d87ab58"); + private static readonly IEnumerable<(UniqueIdentifierId Id, string Value)> UniqueIdentifiers = new List<(UniqueIdentifierId Id, string Value)> { - new (UniqueIdentifierId.VAT_ID, "JUSTATEST") + new(UniqueIdentifierId.VAT_ID, "JUSTATEST") }; private readonly IApplicationRepository _applicationRepository; private readonly ICompanyRepository _companyRepository; private readonly IDocumentRepository _documentRepository; private readonly IConnectorsRepository _connectorsRepository; - private readonly IPortalRepositories _portalRepositories; private readonly ISdFactoryService _service; private readonly ICollection _documents; private readonly SdFactoryBusinessLogic _sut; private readonly IFixture _fixture; private readonly IApplicationChecklistService _checklistService; + private readonly IOptions _options; + private readonly IPortalRepositories _portalRepositories; public SdFactoryBusinessLogicTests() { @@ -79,7 +82,12 @@ public SdFactoryBusinessLogicTests() A.CallTo(() => _portalRepositories.GetInstance()).Returns(_connectorsRepository); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_documentRepository); - _sut = new SdFactoryBusinessLogic(_service, _portalRepositories, _checklistService); + _options = Options.Create(new SdFactorySettings + { + SdFactoryUrl = "https://www.api.sdfactory.com", + SdFactoryIssuerBpn = "BPNL00000003CRHK" + }); + _sut = new SdFactoryBusinessLogic(_service, _portalRepositories, _checklistService, _options); } #endregion @@ -105,8 +113,10 @@ public async Task RegisterConnectorAsync_ExpectedServiceCallIsMade() #region StartSelfDescriptionRegistration - [Fact] - public async Task StartSelfDescriptionRegistration_WithValidData_CompanyIsUpdated() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task StartSelfDescriptionRegistration_WithValidData_CompanyIsUpdated(bool clearinghouseConnectDisabled) { // Arrange var checklist = new Dictionary @@ -120,18 +130,24 @@ public async Task StartSelfDescriptionRegistration_WithValidData_CompanyIsUpdate var entry = new ApplicationChecklistEntry(Guid.NewGuid(), ApplicationChecklistEntryTypeId.SELF_DESCRIPTION_LP, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); A.CallTo(() => _applicationRepository.GetCompanyAndApplicationDetailsWithUniqueIdentifiersAsync(ApplicationId)) .Returns((CompanyId, Bpn, CountryCode, UniqueIdentifiers)); + var sut = new SdFactoryBusinessLogic(_service, _portalRepositories, _checklistService, Options.Create(new SdFactorySettings + { + SdFactoryUrl = "https://www.api.sdfactory.com", + SdFactoryIssuerBpn = "BPNL00000003CRHK", + ClearinghouseConnectDisabled = clearinghouseConnectDisabled + })); // Act - var result = await _sut.StartSelfDescriptionRegistration(context, CancellationToken.None); + var result = await sut.StartSelfDescriptionRegistration(context, CancellationToken.None); // Assert A.CallTo(() => _service.RegisterSelfDescriptionAsync(ApplicationId, UniqueIdentifiers, CountryCode, Bpn, A._)) - .MustHaveHappenedOnceExactly(); + .MustHaveHappened(clearinghouseConnectDisabled ? 0 : 1, Times.Exactly); result.Should().NotBeNull(); result.ModifyChecklistEntry.Should().NotBeNull(); result.ModifyChecklistEntry!.Invoke(entry); - entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.IN_PROGRESS); - result.ScheduleStepTypeIds.Should().ContainSingle().And.Match(x => x.Single() == ProcessStepTypeId.FINISH_SELF_DESCRIPTION_LP); + entry.ApplicationChecklistEntryStatusId.Should().Be(clearinghouseConnectDisabled ? ApplicationChecklistEntryStatusId.DONE : ApplicationChecklistEntryStatusId.IN_PROGRESS); + result.ScheduleStepTypeIds.Should().ContainSingle().And.Match(x => clearinghouseConnectDisabled ? x.Single() == ProcessStepTypeId.ACTIVATE_APPLICATION : x.Single() == ProcessStepTypeId.FINISH_SELF_DESCRIPTION_LP); result.SkipStepTypeIds.Should().BeNull(); result.Modified.Should().BeTrue(); } diff --git a/tests/externalsystems/SdFactory.Library.Tests/SdFactoryServiceTests.cs b/tests/externalsystems/SdFactory.Library.Tests/SdFactoryServiceTests.cs index c679cd9e18..2174e7ae4f 100644 --- a/tests/externalsystems/SdFactory.Library.Tests/SdFactoryServiceTests.cs +++ b/tests/externalsystems/SdFactory.Library.Tests/SdFactoryServiceTests.cs @@ -35,7 +35,7 @@ public class SdFactoryServiceTests private static readonly IEnumerable<(UniqueIdentifierId Id, string Value)> UniqueIdentifiers = new List<(UniqueIdentifierId Id, string Value)> { - new (UniqueIdentifierId.VAT_ID, "JUSTATEST") + new(UniqueIdentifierId.VAT_ID, "JUSTATEST") }; private readonly IPortalRepositories _portalRepositories; From e49fa8f59a48b2c0a65c049d681c281790b52690 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Thu, 27 Jun 2024 14:59:16 +0200 Subject: [PATCH 2/7] fix(decline): remove mail sending when user declines registration (#806) Refs: #797 Reviewed-By: Norbert Truchsess --- .../RegistrationBusinessLogic.cs | 225 ++++++++---------- .../Controllers/RegistrationController.cs | 2 +- .../RegistrationBusinessLogicTest.cs | 22 +- 3 files changed, 96 insertions(+), 153 deletions(-) diff --git a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs index fb6ecb0002..5566c0c8f6 100644 --- a/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/registration/Registration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -44,44 +44,24 @@ namespace Org.Eclipse.TractusX.Portal.Backend.Registration.Service.BusinessLogic; -public class RegistrationBusinessLogic : IRegistrationBusinessLogic +public class RegistrationBusinessLogic( + IOptions settings, + IBpnAccess bpnAccess, + IUserProvisioningService userProvisioningService, + ILogger logger, + IPortalRepositories portalRepositories, + IApplicationChecklistCreationService checklistService, + IIdentityService identityService, + IDateTimeProvider dateTimeProvider, + IMailingProcessCreation mailingProcessCreation) : IRegistrationBusinessLogic { - private readonly RegistrationSettings _settings; - private readonly IBpnAccess _bpnAccess; - private readonly IUserProvisioningService _userProvisioningService; - private readonly IPortalRepositories _portalRepositories; - private readonly ILogger _logger; - private readonly IApplicationChecklistCreationService _checklistService; - private readonly IIdentityData _identityData; - private readonly IDateTimeProvider _dateTimeProvider; - private readonly IMailingProcessCreation _mailingProcessCreation; + private readonly IIdentityData _identityData = identityService.IdentityData; + private readonly RegistrationSettings _settings = settings.Value; private static readonly Regex bpnRegex = new(@"(\w|\d){16}", RegexOptions.None, TimeSpan.FromSeconds(1)); - public RegistrationBusinessLogic( - IOptions settings, - IBpnAccess bpnAccess, - IUserProvisioningService userProvisioningService, - ILogger logger, - IPortalRepositories portalRepositories, - IApplicationChecklistCreationService checklistService, - IIdentityService identityService, - IDateTimeProvider dateTimeProvider, - IMailingProcessCreation mailingProcessCreation) - { - _settings = settings.Value; - _bpnAccess = bpnAccess; - _userProvisioningService = userProvisioningService; - _logger = logger; - _portalRepositories = portalRepositories; - _checklistService = checklistService; - _identityData = identityService.IdentityData; - _dateTimeProvider = dateTimeProvider; - _mailingProcessCreation = mailingProcessCreation; - } - public IAsyncEnumerable GetClientRolesCompositeAsync() => - _portalRepositories.GetInstance().GetClientRolesCompositeAsync(_settings.KeycloakClientID); + portalRepositories.GetInstance().GetClientRolesCompositeAsync(_settings.KeycloakClientID); public Task GetCompanyBpdmDetailDataByBusinessPartnerNumber(string businessPartnerNumber, string token, CancellationToken cancellationToken) { @@ -94,7 +74,7 @@ public Task GetCompanyBpdmDetailDataByBusinessPartnerNumb private async Task GetCompanyBpdmDetailDataByBusinessPartnerNumberInternal(string businessPartnerNumber, string token, CancellationToken cancellationToken) { - var legalEntity = await _bpnAccess.FetchLegalEntityByBpn(businessPartnerNumber, token, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + var legalEntity = await bpnAccess.FetchLegalEntityByBpn(businessPartnerNumber, token, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); if (!businessPartnerNumber.Equals(legalEntity.Bpn, StringComparison.OrdinalIgnoreCase)) { throw new ConflictException("Bpdm did return incorrect bpn legal-entity-data"); @@ -104,7 +84,7 @@ private async Task GetCompanyBpdmDetailDataByBusinessPart throw new ConflictException("Legal-entity-data did not contain a valid country identifier"); var bpdmIdentifiers = ParseBpdmIdentifierDtos(legalEntity.Identifiers).ToList(); - var assignedIdentifiersResult = await _portalRepositories.GetInstance() + var assignedIdentifiersResult = await portalRepositories.GetInstance() .GetCountryAssignedIdentifiers(bpdmIdentifiers.Select(x => x.BpdmIdentifierId), country).ConfigureAwait(ConfigureAwaitOptions.None); if (!assignedIdentifiersResult.IsValidCountry) @@ -158,30 +138,30 @@ public async Task UploadDocumentAsync(Guid applicationId, IFormFile document, Do throw new ControllerArgumentException($"documentType must be either: {string.Join(",", _settings.DocumentTypeIds)}"); } - var validApplicationForCompany = await _portalRepositories.GetInstance().IsValidApplicationForCompany(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); + var validApplicationForCompany = await portalRepositories.GetInstance().IsValidApplicationForCompany(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!validApplicationForCompany) { throw new ForbiddenException($"The users company is not assigned with application {applicationId}"); } - _portalRepositories.GetInstance().AttachAndModifyCompanyApplication(applicationId, application => + portalRepositories.GetInstance().AttachAndModifyCompanyApplication(applicationId, application => { - application.DateLastChanged = _dateTimeProvider.OffsetNow; + application.DateLastChanged = dateTimeProvider.OffsetNow; }); var mediaTypeId = document.ContentType.ParseMediaTypeId(); var (content, hash) = await document.GetContentAndHash(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); - _portalRepositories.GetInstance().CreateDocument(document.FileName, content, hash, mediaTypeId, documentTypeId, doc => + portalRepositories.GetInstance().CreateDocument(document.FileName, content, hash, mediaTypeId, documentTypeId, doc => { doc.CompanyUserId = _identityData.IdentityId; }); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } public async Task<(string FileName, byte[] Content, string MediaType)> GetDocumentContentAsync(Guid documentId) { - var documentRepository = _portalRepositories.GetInstance(); + var documentRepository = portalRepositories.GetInstance(); var documentDetails = await documentRepository.GetDocumentIdWithCompanyUserCheckAsync(documentId, _identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); if (documentDetails.DocumentId == Guid.Empty) { @@ -202,11 +182,11 @@ public async Task UploadDocumentAsync(Guid applicationId, IFormFile document, Do } public IAsyncEnumerable GetAllApplicationsForUserWithStatus() => - _portalRepositories.GetInstance().GetApplicationsWithStatusUntrackedAsync(_identityData.CompanyId); + portalRepositories.GetInstance().GetApplicationsWithStatusUntrackedAsync(_identityData.CompanyId); public async Task> GetApplicationsDeclineData() { - var data = await _portalRepositories.GetInstance().GetCompanyApplicationsDeclineData(_identityData.IdentityId, _settings.ApplicationDeclineStatusIds).ConfigureAwait(ConfigureAwaitOptions.None); + var data = await portalRepositories.GetInstance().GetCompanyApplicationsDeclineData(_identityData.IdentityId, _settings.ApplicationDeclineStatusIds).ConfigureAwait(ConfigureAwaitOptions.None); var user = NameHelper.CreateNameString(data.FirstName, data.LastName, data.Email, "unknown user"); return data.Applications.Select(application => @@ -221,7 +201,7 @@ public async Task> GetApplicationsDec public async Task GetCompanyDetailData(Guid applicationId) { - var result = await _portalRepositories.GetInstance().GetCompanyApplicationDetailDataAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetCompanyApplicationDetailDataAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == null) { throw new NotFoundException($"CompanyApplication {applicationId} not found"); @@ -255,18 +235,18 @@ public Task SetCompanyDetailDataAsync(Guid applicationId, CompanyDetailData comp private async Task SetCompanyDetailDataInternal(Guid applicationId, CompanyDetailData companyDetails) { await companyDetails.ValidateDatabaseData( - bpn => _portalRepositories.GetInstance().CheckBpnExists(bpn), - alpha2Code => _portalRepositories.GetInstance() + bpn => portalRepositories.GetInstance().CheckBpnExists(bpn), + alpha2Code => portalRepositories.GetInstance() .CheckCountryExistsByAlpha2CodeAsync(alpha2Code), (countryAlpha2Code, uniqueIdentifierIds) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .GetCountryAssignedIdentifiers( countryAlpha2Code, uniqueIdentifierIds), false).ConfigureAwait(ConfigureAwaitOptions.None); - var applicationRepository = _portalRepositories.GetInstance(); - var companyRepository = _portalRepositories.GetInstance(); + var applicationRepository = portalRepositories.GetInstance(); + var companyRepository = portalRepositories.GetInstance(); var companyApplicationData = await GetAndValidateApplicationData(applicationId, companyDetails, applicationRepository).ConfigureAwait(ConfigureAwaitOptions.None); @@ -276,9 +256,9 @@ await companyDetails.ValidateDatabaseData( companyRepository.CreateUpdateDeleteIdentifiers(companyDetails.CompanyId, companyApplicationData.UniqueIds, companyDetails.UniqueIds.Select(x => (x.UniqueIdentifierId, x.Value))); - UpdateApplicationStatus(applicationId, companyApplicationData.ApplicationStatusId, UpdateApplicationSteps.CompanyWithAddress, applicationRepository, _dateTimeProvider); + UpdateApplicationStatus(applicationId, companyApplicationData.ApplicationStatusId, UpdateApplicationSteps.CompanyWithAddress, applicationRepository, dateTimeProvider); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } private async Task GetAndValidateApplicationData(Guid applicationId, CompanyDetailData companyDetails, IApplicationRepository applicationRepository) @@ -377,19 +357,19 @@ public Task InviteNewUserAsync(Guid applicationId, UserCreationInfoWithMess private async Task InviteNewUserInternalAsync(Guid applicationId, UserCreationInfoWithMessage userCreationInfo) { - if (await _portalRepositories.GetInstance().IsOwnCompanyUserWithEmailExisting(userCreationInfo.eMail, _identityData.CompanyId)) + if (await portalRepositories.GetInstance().IsOwnCompanyUserWithEmailExisting(userCreationInfo.eMail, _identityData.CompanyId)) { throw new ControllerArgumentException($"user with email {userCreationInfo.eMail} does already exist"); } - var (companyNameIdpAliasData, createdByName) = await _userProvisioningService.GetCompanyNameSharedIdpAliasData(_identityData.IdentityId, applicationId).ConfigureAwait(ConfigureAwaitOptions.None); + var (companyNameIdpAliasData, createdByName) = await userProvisioningService.GetCompanyNameSharedIdpAliasData(_identityData.IdentityId, applicationId).ConfigureAwait(ConfigureAwaitOptions.None); IEnumerable? userRoleDatas = null; if (userCreationInfo.Roles.Any()) { var clientRoles = new[] { new UserRoleConfig(_settings.KeycloakClientID, userCreationInfo.Roles) }; - userRoleDatas = await _userProvisioningService.GetRoleDatas(clientRoles).ToListAsync().ConfigureAwait(false); + userRoleDatas = await userProvisioningService.GetRoleDatas(clientRoles).ToListAsync().ConfigureAwait(false); } var userCreationInfoIdps = new[] { new UserCreationRoleDataIdpInfo( @@ -403,17 +383,17 @@ private async Task InviteNewUserInternalAsync(Guid applicationId, UserCreat true )}.ToAsyncEnumerable(); - var (newCompanyUserId, _, password, error) = await _userProvisioningService.CreateOwnCompanyIdpUsersAsync(companyNameIdpAliasData, userCreationInfoIdps).SingleAsync().ConfigureAwait(false); + var (newCompanyUserId, _, password, error) = await userProvisioningService.CreateOwnCompanyIdpUsersAsync(companyNameIdpAliasData, userCreationInfoIdps).SingleAsync().ConfigureAwait(false); if (error != null) { throw error; } - _portalRepositories.GetInstance().CreateInvitation(applicationId, newCompanyUserId); - _portalRepositories.GetInstance().AttachAndModifyCompanyApplication(applicationId, application => + portalRepositories.GetInstance().CreateInvitation(applicationId, newCompanyUserId); + portalRepositories.GetInstance().AttachAndModifyCompanyApplication(applicationId, application => { - application.DateLastChanged = _dateTimeProvider.OffsetNow; + application.DateLastChanged = dateTimeProvider.OffsetNow; }); var inviteTemplateName = "invite"; @@ -422,9 +402,9 @@ private async Task InviteNewUserInternalAsync(Guid applicationId, UserCreat inviteTemplateName = "inviteWithMessage"; } - var modified = await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + var modified = await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); - var companyDisplayName = await _userProvisioningService.GetIdentityProviderDisplayName(companyNameIdpAliasData.IdpAlias).ConfigureAwait(ConfigureAwaitOptions.None) ?? companyNameIdpAliasData.IdpAlias; + var companyDisplayName = await userProvisioningService.GetIdentityProviderDisplayName(companyNameIdpAliasData.IdpAlias).ConfigureAwait(ConfigureAwaitOptions.None) ?? companyNameIdpAliasData.IdpAlias; var mailParameters = ImmutableDictionary.CreateRange(new[] { KeyValuePair.Create("password", password), @@ -436,9 +416,9 @@ private async Task InviteNewUserInternalAsync(Guid applicationId, UserCreat KeyValuePair.Create("username", userCreationInfo.eMail), }); - _mailingProcessCreation.CreateMailProcess(userCreationInfo.eMail, inviteTemplateName, mailParameters); - _mailingProcessCreation.CreateMailProcess(userCreationInfo.eMail, "password", mailParameters); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + mailingProcessCreation.CreateMailProcess(userCreationInfo.eMail, inviteTemplateName, mailParameters); + mailingProcessCreation.CreateMailProcess(userCreationInfo.eMail, "password", mailParameters); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return modified; } @@ -449,7 +429,7 @@ public async Task SetOwnCompanyApplicationStatusAsync(Guid applicationId, C throw new ControllerArgumentException("status must not be null"); } - var applicationRepository = _portalRepositories.GetInstance(); + var applicationRepository = portalRepositories.GetInstance(); var applicationUserData = await applicationRepository.GetOwnCompanyApplicationUserDataAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!applicationUserData.Exists) { @@ -458,8 +438,8 @@ public async Task SetOwnCompanyApplicationStatusAsync(Guid applicationId, C if (applicationUserData.StatusId != status) { - ValidateCompanyApplicationStatus(applicationId, status, applicationUserData, applicationRepository, _dateTimeProvider); - return await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + ValidateCompanyApplicationStatus(applicationId, status, applicationUserData, applicationRepository, dateTimeProvider); + return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } return 0; @@ -467,7 +447,7 @@ public async Task SetOwnCompanyApplicationStatusAsync(Guid applicationId, C public async Task GetOwnCompanyApplicationStatusAsync(Guid applicationId) { - var result = await _portalRepositories.GetInstance().GetOwnCompanyApplicationStatusUserDataUntrackedAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetOwnCompanyApplicationStatusUserDataUntrackedAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); if (!result.Exists) { throw new NotFoundException($"CompanyApplication {applicationId} not found"); @@ -484,8 +464,8 @@ public async Task SubmitRoleConsentAsync(Guid applicationId, CompanyRoleAgr var companyRoleIdsToSet = roleAgreementConsentStatuses.CompanyRoleIds; var agreementConsentsToSet = roleAgreementConsentStatuses.AgreementConsentStatuses; - var companyRolesRepository = _portalRepositories.GetInstance(); - var consentRepository = _portalRepositories.GetInstance(); + var companyRolesRepository = portalRepositories.GetInstance(); + var consentRepository = portalRepositories.GetInstance(); var companyRoleAgreementConsentData = await companyRolesRepository.GetCompanyRoleAgreementConsentDataAsync(applicationId).ConfigureAwait(ConfigureAwaitOptions.None); @@ -536,14 +516,14 @@ public async Task SubmitRoleConsentAsync(Guid applicationId, CompanyRoleAgr } HandleConsent(consents, agreementConsentsToSet.ExceptBy(companyRoleAssignedAgreements.SelectMany(x => x.Value).Where(x => x.AgreementStatusId == AgreementStatusId.INACTIVE).Select(x => x.AgreementId), x => x.AgreementId), consentRepository, companyId, userId); - UpdateApplicationStatus(applicationId, applicationStatusId, UpdateApplicationSteps.CompanyRoleAgreementConsents, _portalRepositories.GetInstance(), _dateTimeProvider); + UpdateApplicationStatus(applicationId, applicationStatusId, UpdateApplicationSteps.CompanyRoleAgreementConsents, portalRepositories.GetInstance(), dateTimeProvider); - return await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } public async Task GetRoleAgreementConsentsAsync(Guid applicationId) { - var result = await _portalRepositories.GetInstance().GetCompanyRoleAgreementConsentStatusUntrackedAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetCompanyRoleAgreementConsentStatusUntrackedAsync(applicationId, _identityData.CompanyId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == null) { throw new ForbiddenException($"user is not assigned with CompanyApplication {applicationId}"); @@ -553,8 +533,8 @@ public async Task GetRoleAgreementConsentsAsync(Gu public async Task GetCompanyRoleAgreementDataAsync() => new( - (await _portalRepositories.GetInstance().GetCompanyRoleAgreementsUntrackedAsync().ToListAsync().ConfigureAwait(false)).AsEnumerable(), - (await _portalRepositories.GetInstance().GetAgreementsForCompanyRolesUntrackedAsync().ToListAsync().ConfigureAwait(false)).AsEnumerable() + (await portalRepositories.GetInstance().GetCompanyRoleAgreementsUntrackedAsync().ToListAsync().ConfigureAwait(false)).AsEnumerable(), + (await portalRepositories.GetInstance().GetAgreementsForCompanyRolesUntrackedAsync().ToListAsync().ConfigureAwait(false)).AsEnumerable() ); public async Task SubmitRegistrationAsync(Guid applicationId) @@ -566,30 +546,30 @@ public async Task SubmitRegistrationAsync(Guid applicationId) throw new UnexpectedConditionException("updateStatus should allways be SUBMITTED here"); } - _portalRepositories.GetInstance().AttachAndModifyDocuments( + portalRepositories.GetInstance().AttachAndModifyDocuments( applicationUserData.DocumentDatas.Select(x => new ValueTuple?, Action>( x.DocumentId, doc => doc.DocumentStatusId = x.StatusId, doc => doc.DocumentStatusId = DocumentStatusId.LOCKED))); - var entries = await _checklistService.CreateInitialChecklistAsync(applicationId); + var entries = await checklistService.CreateInitialChecklistAsync(applicationId); - var process = _portalRepositories.GetInstance().CreateProcess(ProcessTypeId.APPLICATION_CHECKLIST); + var process = portalRepositories.GetInstance().CreateProcess(ProcessTypeId.APPLICATION_CHECKLIST); - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .CreateProcessStepRange( - _checklistService + checklistService .GetInitialProcessStepTypeIds(entries) .Select(processStepTypeId => (processStepTypeId, ProcessStepStatusId.TODO, process.Id))); - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyCompanyApplication( applicationId, application => { application.ApplicationStatusId = CompanyApplicationStatusId.SUBMITTED; application.ChecklistProcessId = process.Id; - application.DateLastChanged = _dateTimeProvider.OffsetNow; + application.DateLastChanged = dateTimeProvider.OffsetNow; }); var mailParameters = ImmutableDictionary.CreateRange(new[] @@ -599,14 +579,14 @@ public async Task SubmitRegistrationAsync(Guid applicationId) if (applicationUserData.Email != null) { - _mailingProcessCreation.CreateMailProcess(applicationUserData.Email, "SubmitRegistrationTemplate", mailParameters); + mailingProcessCreation.CreateMailProcess(applicationUserData.Email, "SubmitRegistrationTemplate", mailParameters); } else { - _logger.LogInformation("user {userId} has no email-address", _identityData.IdentityId); + logger.LogInformation("user {userId} has no email-address", _identityData.IdentityId); } - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return true; } @@ -614,7 +594,7 @@ public async Task SubmitRegistrationAsync(Guid applicationId) private async ValueTask GetAndValidateCompanyDataDetails(Guid applicationId, IEnumerable docTypeIds) { var userId = _identityData.IdentityId; - var applicationUserData = await _portalRepositories.GetInstance() + var applicationUserData = await portalRepositories.GetInstance() .GetOwnCompanyApplicationUserEmailDataAsync(applicationId, userId, docTypeIds).ConfigureAwait(ConfigureAwaitOptions.None); if (applicationUserData == null) @@ -665,7 +645,7 @@ private async ValueTask GetAndValidateCompanyDa } public IAsyncEnumerable GetInvitedUsersAsync(Guid applicationId) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .GetInvitedUserDetailsUntrackedAsync(applicationId) .Select(x => new InvitedUser( @@ -675,7 +655,7 @@ public IAsyncEnumerable GetInvitedUsersAsync(Guid applicationId) => public async Task> GetUploadedDocumentsAsync(Guid applicationId, DocumentTypeId documentTypeId) { - var result = await _portalRepositories.GetInstance().GetUploadedDocumentsAsync(applicationId, documentTypeId, _identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); + var result = await portalRepositories.GetInstance().GetUploadedDocumentsAsync(applicationId, documentTypeId, _identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) { throw new NotFoundException($"application {applicationId} not found"); @@ -689,7 +669,7 @@ public async Task> GetUploadedDocumentsAsync(Guid a public async Task SetInvitationStatusAsync() { - var invitationData = await _portalRepositories.GetInstance().GetInvitationStatusAsync(_identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); + var invitationData = await portalRepositories.GetInstance().GetInvitationStatusAsync(_identityData.IdentityId).ConfigureAwait(ConfigureAwaitOptions.None); if (invitationData == null) { @@ -700,16 +680,16 @@ public async Task SetInvitationStatusAsync() { invitationData.InvitationStatusId = InvitationStatusId.ACCEPTED; } - _portalRepositories.GetInstance().AttachAndModifyCompanyApplication(invitationData.CompanyApplicationId, application => + portalRepositories.GetInstance().AttachAndModifyCompanyApplication(invitationData.CompanyApplicationId, application => { - application.DateLastChanged = _dateTimeProvider.OffsetNow; + application.DateLastChanged = dateTimeProvider.OffsetNow; }); - return await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + return await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } public async Task GetRegistrationDataAsync(Guid applicationId) { - var (isValidApplicationId, isValidCompany, data) = await _portalRepositories.GetInstance().GetRegistrationDataUntrackedAsync(applicationId, _identityData.CompanyId, _settings.DocumentTypeIds).ConfigureAwait(ConfigureAwaitOptions.None); + var (isValidApplicationId, isValidCompany, data) = await portalRepositories.GetInstance().GetRegistrationDataUntrackedAsync(applicationId, _identityData.CompanyId, _settings.DocumentTypeIds).ConfigureAwait(ConfigureAwaitOptions.None); if (!isValidApplicationId) { throw new NotFoundException($"application {applicationId} does not exist"); @@ -742,7 +722,7 @@ public async Task GetRegistrationDataAsync(Guid applica } public IAsyncEnumerable GetCompanyRoles(string? languageShortName = null) => - _portalRepositories.GetInstance().GetCompanyRolesAsync(languageShortName); + portalRepositories.GetInstance().GetCompanyRolesAsync(languageShortName); private static void HandleConsent(IEnumerable consents, IEnumerable agreementConsentsToSet, IConsentRepository consentRepository, Guid companyId, Guid userId) @@ -860,7 +840,7 @@ public async Task DeleteRegistrationDocumentAsync(Guid documentId) { throw new ControllerArgumentException($"documentId must not be empty"); } - var documentRepository = _portalRepositories.GetInstance(); + var documentRepository = portalRepositories.GetInstance(); var details = await documentRepository.GetDocumentDetailsForApplicationUntrackedAsync(documentId, _identityData.CompanyId, _settings.ApplicationStatusIds).ConfigureAwait(ConfigureAwaitOptions.None); if (details == default) { @@ -885,19 +865,19 @@ public async Task DeleteRegistrationDocumentAsync(Guid documentId) documentRepository.RemoveDocument(details.DocumentId); - _portalRepositories.GetInstance().AttachAndModifyCompanyApplications( + portalRepositories.GetInstance().AttachAndModifyCompanyApplications( details.applicationId.Select(applicationId => new ValueTuple?, Action>( applicationId, null, - application => application.DateLastChanged = _dateTimeProvider.OffsetNow))); + application => application.DateLastChanged = dateTimeProvider.OffsetNow))); - await _portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); return true; } public async Task> GetCompanyIdentifiers(string alpha2Code) { - var uniqueIdentifierData = await _portalRepositories.GetInstance().GetCompanyIdentifiers(alpha2Code).ConfigureAwait(ConfigureAwaitOptions.None); + var uniqueIdentifierData = await portalRepositories.GetInstance().GetCompanyIdentifiers(alpha2Code).ConfigureAwait(ConfigureAwaitOptions.None); if (!uniqueIdentifierData.IsValidCountryCode) { @@ -908,7 +888,7 @@ public async Task> GetCompanyIdentifiers(strin public async Task<(string fileName, byte[] content, string mediaType)> GetRegistrationDocumentAsync(Guid documentId) { - var documentRepository = _portalRepositories.GetInstance(); + var documentRepository = portalRepositories.GetInstance(); var documentDetails = await documentRepository.GetDocumentAsync(documentId, _settings.RegistrationDocumentTypeIds).ConfigureAwait(ConfigureAwaitOptions.None); if (documentDetails == default) @@ -922,9 +902,10 @@ public async Task> GetCompanyIdentifiers(strin return (documentDetails.FileName, documentDetails.Content, documentDetails.MediaTypeId.MapToMediaType()); } + public async Task DeclineApplicationRegistrationAsync(Guid applicationId) { - var (isValidApplicationId, isValidCompany, declineData) = await _portalRepositories.GetInstance() + var (isValidApplicationId, isValidCompany, declineData) = await portalRepositories.GetInstance() .GetDeclineApplicationDataForApplicationId(applicationId, _identityData.CompanyId, _settings.ApplicationDeclineStatusIds) .ConfigureAwait(ConfigureAwaitOptions.None); @@ -935,7 +916,7 @@ public async Task DeclineApplicationRegistrationAsync(Guid applicationId) if (!isValidCompany) { - throw new ForbiddenException($"User is not allowed to decline this application"); + throw new ForbiddenException("User is not allowed to decline this application"); } if (declineData == null) @@ -949,12 +930,11 @@ public async Task DeclineApplicationRegistrationAsync(Guid applicationId) DeactivateDocuments(declineData.DocumentStatusDatas); ScheduleDeleteIdentityProviders(_identityData.CompanyId, declineData.IdentityProviderStatusDatas); ScheduleDeleteCompanyUsers(declineData.CompanyUserStatusDatas); - CreateDeclineApplicationEmailProcesses(declineData.CompanyUserStatusDatas.Select(x => (x.FirstName, x.LastName, x.Email)), declineData.CompanyName); - await _portalRepositories.SaveAsync().ConfigureAwait(false); + await portalRepositories.SaveAsync().ConfigureAwait(false); } private void DeclineApplication(Guid applicationId) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyCompanyApplication(applicationId, application => { application.ApplicationStatusId = CompanyApplicationStatusId.DECLINED; @@ -962,14 +942,14 @@ private void DeclineApplication(Guid applicationId) => }); private void DeleteCompany(Guid companyId) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyCompany( companyId, null, company => company.CompanyStatusId = CompanyStatusId.DELETED); private void DeclineInvitations(IEnumerable invitationsStatusDatas) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyInvitations( invitationsStatusDatas.Select(data => new ValueTuple?, Action>( data.InvitationId, @@ -977,7 +957,7 @@ private void DeclineInvitations(IEnumerable invitationsSt invitation => invitation.InvitationStatusId = InvitationStatusId.DECLINED))); private void DeactivateDocuments(IEnumerable documentStatusDatas) => - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyDocuments( documentStatusDatas.Select(data => new ValueTuple?, Action>( data.DocumentId, @@ -986,8 +966,8 @@ private void DeactivateDocuments(IEnumerable documentStatusD private void ScheduleDeleteIdentityProviders(Guid companyId, IEnumerable identityProviderStatusDatas) { - var identityProviderRepository = _portalRepositories.GetInstance(); - var processStepRepository = _portalRepositories.GetInstance(); + var identityProviderRepository = portalRepositories.GetInstance(); + var processStepRepository = portalRepositories.GetInstance(); identityProviderStatusDatas .Where(data => data.IdentityProviderTypeId == IdentityProviderTypeId.MANAGED) @@ -1029,7 +1009,7 @@ private void ScheduleDeleteIdentityProviders(Guid companyId, IEnumerable companyUserDatas) { - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .AttachAndModifyIdentities( companyUserDatas .Select(data => new ValueTuple?, Action>( @@ -1040,38 +1020,19 @@ private void ScheduleDeleteCompanyUsers(IEnumerable compa identity.UserStatusId = UserStatusId.DELETED; }))); - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .DeleteCompanyUserAssignedRoles( companyUserDatas.SelectMany(data => data.IdentityAssignedRoleIds.Select(roleId => (data.CompanyUserId, roleId)))); - var processStepRepository = _portalRepositories.GetInstance(); + var processStepRepository = portalRepositories.GetInstance(); var processIds = processStepRepository .CreateProcessRange(Enumerable.Repeat(ProcessTypeId.USER_PROVISIONING, companyUserDatas.Count())) .Select(x => x.Id) .ToImmutableList(); processStepRepository.CreateProcessStepRange( processIds.Select(processId => (ProcessStepTypeId.DELETE_CENTRAL_USER, ProcessStepStatusId.TODO, processId))); - _portalRepositories.GetInstance() + portalRepositories.GetInstance() .CreateCompanyUserAssignedProcessRange( companyUserDatas.Select(x => x.CompanyUserId).Zip(processIds)); } - - private void CreateDeclineApplicationEmailProcesses(IEnumerable<(string? FirstName, string? LastName, string? Email)> emailData, string companyName) - { - foreach (var (FirstName, LastName, Email) in emailData) - { - var userName = string.Join(" ", new[] { FirstName, LastName }.Where(item => !string.IsNullOrWhiteSpace(item))); - - if (string.IsNullOrWhiteSpace(Email)) - { - throw new ConflictException($"user {userName} has no assigned email"); - } - var mailParameters = ImmutableDictionary.CreateRange(new[] - { - KeyValuePair.Create("userName", !string.IsNullOrWhiteSpace(userName) ? userName : Email), - KeyValuePair.Create("companyName", companyName) - }); - _mailingProcessCreation.CreateMailProcess(Email, "EmailRegistrationDeclineTemplate", mailParameters); - } - } } diff --git a/src/registration/Registration.Service/Controllers/RegistrationController.cs b/src/registration/Registration.Service/Controllers/RegistrationController.cs index 4b2211779a..79e30e4cd1 100644 --- a/src/registration/Registration.Service/Controllers/RegistrationController.cs +++ b/src/registration/Registration.Service/Controllers/RegistrationController.cs @@ -279,7 +279,7 @@ public Task SetCompanyDetailDataAsync([FromRoute] Guid applicationId, [FromBody] /// Internal Server Error /// Service Unavailable. [HttpPost] - // [Authorize(Roles = "invite_user")] + [Authorize(Roles = "invite_user")] [Authorize(Policy = PolicyTypes.ValidCompany)] [Authorize(Policy = PolicyTypes.ValidIdentity)] [Route("application/{applicationId}/inviteNewUser")] diff --git a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 166b663bb5..f21f2f66c2 100644 --- a/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/registration/Registration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -3591,28 +3591,10 @@ public async Task DeclineApplicationRegistrationAsync_CallsExpected() .MustHaveHappenedOnceExactly(); // CreateDeclineApplicationEmailProcesses - - A.CallTo(() => _mailingProcessCreation.CreateMailProcess("email1", "EmailRegistrationDeclineTemplate", A>.That.Matches(x => x.OrderBy(y => y.Key).SequenceEqual(new KeyValuePair[] - { - KeyValuePair.Create("companyName", "TestCompany"), - KeyValuePair.Create("userName", "email1") - })))) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess("email2", "EmailRegistrationDeclineTemplate", A>.That.Matches(x => x.OrderBy(y => y.Key).SequenceEqual(new KeyValuePair[] - { - KeyValuePair.Create("companyName", "TestCompany"), - KeyValuePair.Create("userName", "First Last") - })))) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _mailingProcessCreation.CreateMailProcess("email3", "EmailRegistrationDeclineTemplate", A>.That.Matches(x => x.OrderBy(y => y.Key).SequenceEqual(new KeyValuePair[] - { - KeyValuePair.Create("companyName", "TestCompany"), - KeyValuePair.Create("userName", "Other") - })))) - .MustHaveHappenedOnceExactly(); + A.CallTo(() => _mailingProcessCreation.CreateMailProcess(A._, A._, A>._)) + .MustNotHaveHappened(); // final save - A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); } From e1e97809b48b199a72a7a0fa43511d3d5e8d2ee1 Mon Sep 17 00:00:00 2001 From: "Dhirender Singh (Cofinity-X)" <144212607+dhiren-singh-007@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:08:05 +0200 Subject: [PATCH 3/7] fix(decline): moved email validation in decline application process api (#822) Refs: #820 Reviewed-By: Phil Schneider --- .../BusinessLogic/RegistrationBusinessLogic.cs | 10 +++++----- .../RegistrationBusinessLogicTest.cs | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index 4b453d2f21..36dcfeb58f 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -463,6 +463,11 @@ public async Task ApproveRegistrationVerification(Guid applicationId) public async Task DeclineRegistrationVerification(Guid applicationId, string comment, CancellationToken cancellationToken) { + if (string.IsNullOrWhiteSpace(comment)) + { + throw new ConflictException("No comment set."); + } + var result = await _portalRepositories.GetInstance().GetCompanyIdNameForSubmittedApplication(applicationId).ConfigureAwait(ConfigureAwaitOptions.None); if (result == default) { @@ -546,11 +551,6 @@ public async Task DeclineRegistrationVerification(Guid applicationId, string com private void PostRegistrationCancelEmailAsync(ICollection emailData, string companyName, string comment) { - if (string.IsNullOrWhiteSpace(comment)) - { - throw new ConflictException("No comment set."); - } - foreach (var user in emailData) { var userName = string.Join(" ", new[] { user.FirstName, user.LastName }.Where(item => !string.IsNullOrWhiteSpace(item))); diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index 4959f09092..9cd3381d83 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -529,6 +529,22 @@ public async Task DeclineRegistrationVerification_WithApplicationNotFound_Throws ex.ParamName.Should().Be("applicationId"); } + [Fact] + public async Task DeclineRegistrationVerification_WithNoComment_ThrowsConflictException() + { + // Arrange + var applicationId = Guid.NewGuid(); + A.CallTo(() => _applicationRepository.GetCompanyIdNameForSubmittedApplication(applicationId)) + .Returns<(Guid, string, Guid?, IEnumerable<(Guid, string, IdentityProviderTypeId, IEnumerable)>, IEnumerable)>(default); + Task Act() => _logic.DeclineRegistrationVerification(applicationId, "", CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("No comment set."); + } + [Fact] public async Task DeclineRegistrationVerification_WithMultipleIdps_CallsExpected() { From 7e1447a89f17161d288a769c989e735e67b61abb Mon Sep 17 00:00:00 2001 From: Tayyab Fayyaz Janjua Date: Wed, 10 Jul 2024 14:53:59 +0200 Subject: [PATCH 4/7] fix(getAppRoles): change permissions (#827) permission 'view_client_roles' has been changed for the following APIs GET /api/apps/AppChange/{appId}/roles => new permission validation as 'edit_apps' GET /api/apps/AppReleaseProcess/{appId}/roles => new permission validation as 'add_apps' [Refs: 826](https://github.com/eclipse-tractusx/portal-backend/issues/826) --- src/marketplace/Apps.Service/Controllers/AppChangeController.cs | 2 +- .../Apps.Service/Controllers/AppReleaseProcessController.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/marketplace/Apps.Service/Controllers/AppChangeController.cs b/src/marketplace/Apps.Service/Controllers/AppChangeController.cs index 18deb79670..83bae8be28 100644 --- a/src/marketplace/Apps.Service/Controllers/AppChangeController.cs +++ b/src/marketplace/Apps.Service/Controllers/AppChangeController.cs @@ -268,7 +268,7 @@ public async Task CreateActiveAppDocumentAsync([FromRoute] Guid /// The language does not exist. /// The app was not found. [HttpGet] - [Authorize(Roles = "view_client_roles")] + [Authorize(Roles = "edit_apps")] [Authorize(Policy = PolicyTypes.ValidCompany)] [Route("{appId}/roles")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] diff --git a/src/marketplace/Apps.Service/Controllers/AppReleaseProcessController.cs b/src/marketplace/Apps.Service/Controllers/AppReleaseProcessController.cs index 2f2b333f18..dba63d921a 100644 --- a/src/marketplace/Apps.Service/Controllers/AppReleaseProcessController.cs +++ b/src/marketplace/Apps.Service/Controllers/AppReleaseProcessController.cs @@ -505,7 +505,7 @@ public async Task CreateAndUpdateTechnicalUserProfiles([FromRou /// The app was not found. /// The app is not the provider of the company [HttpGet] - [Authorize(Roles = "view_client_roles")] + [Authorize(Roles = "add_apps")] [Authorize(Policy = PolicyTypes.ValidCompany)] [Route("{appId}/roles")] [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] From 158f664d0d078261dd9af544d03a53af231ba0bb Mon Sep 17 00:00:00 2001 From: Evelyn Gurschler Date: Thu, 11 Jul 2024 16:43:26 +0200 Subject: [PATCH 5/7] chore(2.1.0-rc1): rename migration (#829) https://github.com/eclipse-tractusx/portal/issues/350 --- ...tails.Designer.cs => 20240527114938_2.1.0-rc1.Designer.cs} | 4 ++-- ...38_538-RemoveSsiDetails.cs => 20240527114938_2.1.0-rc1.cs} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/portalbackend/PortalBackend.Migrations/Migrations/{20240527114938_538-RemoveSsiDetails.Designer.cs => 20240527114938_2.1.0-rc1.Designer.cs} (99%) rename src/portalbackend/PortalBackend.Migrations/Migrations/{20240527114938_538-RemoveSsiDetails.cs => 20240527114938_2.1.0-rc1.cs} (99%) diff --git a/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.Designer.cs b/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.Designer.cs similarity index 99% rename from src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.Designer.cs rename to src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.Designer.cs index 2a8ab1aced..9f753849f4 100644 --- a/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.Designer.cs +++ b/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.Designer.cs @@ -32,8 +32,8 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Migrations.Migrations { [DbContext(typeof(PortalDbContext))] - [Migration("20240527114938_538-RemoveSsiDetails")] - partial class _538RemoveSsiDetails + [Migration("20240527114938_2.1.0-rc1")] + partial class _210rc1 { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.cs b/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.cs similarity index 99% rename from src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.cs rename to src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.cs index 49c978eada..a63a3921d4 100644 --- a/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_538-RemoveSsiDetails.cs +++ b/src/portalbackend/PortalBackend.Migrations/Migrations/20240527114938_2.1.0-rc1.cs @@ -27,7 +27,7 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Migrations.Migrations { /// - public partial class _538RemoveSsiDetails : Migration + public partial class _210rc1 : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) From 59fd496b8b8fa65fcfc3058dca0a2791ea6f450e Mon Sep 17 00:00:00 2001 From: Evelyn Gurschler Date: Fri, 12 Jul 2024 18:02:35 +0200 Subject: [PATCH 6/7] docs(2.1.0-rc1): update changelog --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 109eb685c3..9c3fc38168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ New features, fixed bugs, known defects and other noteworthy changes to each release of the Catena-X Portal Backend. +## 2.1.0-RC1 + +### Change +- App Services [#827](https://github.com/eclipse-tractusx/portal-backend/pull/827) + - updated endpoint authorization rule setting of `GET /api/apps/AppChange/{appId}/roles` => new permission validation to `edit_apps` + - updated endpoint authorization rule setting of `GET /api/apps/AppReleaseProcess/{appId}/roles` => new permission validation to `add_apps` +- Process Worker + - moved email validation in decline application process api from child method to parent method [#822](https://github.com/eclipse-tractusx/portal-backend/pull/822) + - removed mail process creation of the decline registration mail if the user self-triggered the decline [#806](https://github.com/eclipse-tractusx/portal-backend/pull/806) + +### Feature +- Registration Process (Administration Service) + - added configuration toggle to deactivate the GX compliance service interface dependency incl. SD factory connectivity to enable registration and connector creation without external dependency [#793](https://github.com/eclipse-tractusx/portal-backend/pull/793) + +### Technical Support +- changed from emulation to cross-compile for building multi-platform images [#803](https://github.com/eclipse-tractusx/portal-backend/pull/803) +- improved dockerfiles by removing unnecessary base stage and aligning environment variables [#803](https://github.com/eclipse-tractusx/portal-backend/pull/803) +- running the app from top-level program has been changed from synchronous to asynchronous execution [#786](https://github.com/eclipse-tractusx/portal-backend/pull/786) +- changed querying db-sequences from synchronous to asynchronous execution [#786](https://github.com/eclipse-tractusx/portal-backend/pull/786) +- added exception-handling to the crypto helper to properly map the system exceptions that are thrown by the Aes-classes to configuration respective conflict exceptions [#790](https://github.com/eclipse-tractusx/portal-backend/pull/790) +- updated GitHub actions [#785](https://github.com/eclipse-tractusx/portal-backend/pull/785)/[#777](https://github.com/eclipse-tractusx/portal-backend/pull/777) +- **Clean-up unused Code** [#783](https://github.com/eclipse-tractusx/portal-backend/pull/783) + - removed all company credential (SSI) related endpoints + - removed all company credential (SSI) related database tables + +### Bugfixes +- fixed nullability-issue in IdentityProviderBusinessLogic [#786](https://github.com/eclipse-tractusx/portal-backend/pull/786) +- fixed ambiguity in IfAny nullable return type declaration [#786](https://github.com/eclipse-tractusx/portal-backend/pull/786) +- added locking for the `invite process` process worker [#788](https://github.com/eclipse-tractusx/portal-backend/pull/788) +- fixed support of null setting of the attribute `url` for offer provider autosetup url configuration endpoint `PUT /api/administration/SubscriptionConfiguration/owncompany` [#783](https://github.com/eclipse-tractusx/portal-backend/pull/783) + ## 2.0.0 ### Change @@ -138,7 +169,7 @@ The following are known issues identified in the current release: * **Validation Limitations:** * Pattern validation for URL inputs in `POST` and `PUT` endpoints is currently limited, potentially allowing invalid URLs to be accepted. [#587](https://github.com/eclipse-tractusx/portal-backend/issues/587) * **Validation of File Upload Limitation:** - * It is recommended to make make use of an existing trustworthy 3rd party virus-scan service for a more broad scan for known malicious signatures. [#779](https://github.com/eclipse-tractusx/portal-backend/issues/779) + * It is recommended to make use of an existing trustworthy 3rd party virus-scan service for a more broad scan for known malicious signatures. [#779](https://github.com/eclipse-tractusx/portal-backend/issues/779) * **In Memory Storage Limitation**: * Sensitive information (such as passwords) is read in an unencrypted manner in memory. From 1816125e31e4de7d185d834668dfc70bd9371887 Mon Sep 17 00:00:00 2001 From: Evelyn Gurschler Date: Fri, 12 Jul 2024 18:03:40 +0200 Subject: [PATCH 7/7] build(2.1.0-rc1): bump version --- src/Directory.Build.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 17fb129034..0e5fecd8cf 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -19,7 +19,7 @@ - 2.0.0 - + 2.1.0 + RC1