From 8994e037d894553776610151ce5f547cb189ed47 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Wed, 28 Aug 2024 14:41:44 +0200 Subject: [PATCH 1/5] feat(clearinghouse): add auto retrigger for END_CLEARINGHOUSE enable maintenance worker to retrigger the clearinghouse process Refs: #810 --- .../PartnerRegistrationSettings.cs | 2 + ...ompanyApplicationStatusFilterExtensions.cs | 22 +++ .../Administration.Service/appsettings.json | 3 +- .../ClearinghouseBusinessLogic.cs | 37 +++++ .../IClearinghouseBusinessLogic.cs | 1 + .../ClearinghouseSettings.cs | 3 + .../Maintenance.App/BatchDeleteService.cs | 100 ------------ .../BatchDeleteServiceSettings.cs | 36 +++++ .../MaintenanceServiceExtensions.cs | 20 +++ .../Maintenance.App/Maintenance.App.csproj | 1 + src/maintenance/Maintenance.App/Program.cs | 28 +++- .../Services/BatchDeleteService.cs | 69 ++++++++ .../Services/IBatchDeleteService.cs | 32 ++++ .../Services/MaintenanceService.cs | 54 +++++++ .../Maintenance.App/appsettings.json | 16 +- .../ApplicationChecklistRepository.cs | 37 +++-- .../Repositories/DocumentRepository.cs | 61 +++---- .../IApplicationChecklistRepository.cs | 1 + .../Repositories/IDocumentRepository.cs | 8 + .../Repositories/ProcessStepRepository.cs | 39 ++--- .../Processes.Worker/appsettings.json | 3 +- .../RegistrationBusinessLogicTest.cs | 2 +- .../appsettings.IntegrationTests.json | 4 +- .../ClearinghouseBusinessLogicTests.cs | 73 ++++++++- .../BatchDeleteServiceTests.cs | 152 ++++++++++-------- .../Maintenance.App.Tests.csproj | 9 ++ .../MaintenanceServiceTests.cs | 72 +++++++++ .../appsettings.IntegrationTests.json | 44 +++++ 28 files changed, 679 insertions(+), 250 deletions(-) create mode 100644 src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs delete mode 100644 src/maintenance/Maintenance.App/BatchDeleteService.cs create mode 100644 src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs create mode 100644 src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs create mode 100644 src/maintenance/Maintenance.App/Services/BatchDeleteService.cs create mode 100644 src/maintenance/Maintenance.App/Services/IBatchDeleteService.cs create mode 100644 src/maintenance/Maintenance.App/Services/MaintenanceService.cs create mode 100644 tests/maintenance/Maintenance.App.Tests/MaintenanceServiceTests.cs create mode 100644 tests/maintenance/Maintenance.App.Tests/appsettings.IntegrationTests.json diff --git a/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs b/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs index 8b7cac7fbb..5d113b8732 100644 --- a/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs +++ b/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs @@ -28,6 +28,8 @@ public class PartnerRegistrationSettings [Required] [DistinctValues("x => x.ClientId")] public IEnumerable InitialRoles { get; set; } = null!; + + public int ApplicationsMaxPageSize { get; set; } } public static class PartnerRegistrationSettingsExtensions diff --git a/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs new file mode 100644 index 0000000000..0c20d93009 --- /dev/null +++ b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs @@ -0,0 +1,22 @@ +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; + +namespace Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Extensions; + +public static class CompanyApplicationStatusFilterExtensions +{ + public static IEnumerable GetCompanyApplicationStatusIds(this CompanyApplicationStatusFilter? companyApplicationStatusFilter) => + companyApplicationStatusFilter switch + { + CompanyApplicationStatusFilter.Closed => + [ + CompanyApplicationStatusId.CONFIRMED, CompanyApplicationStatusId.DECLINED + ], + CompanyApplicationStatusFilter.InReview => [CompanyApplicationStatusId.SUBMITTED], + _ => + [ + CompanyApplicationStatusId.SUBMITTED, CompanyApplicationStatusId.CONFIRMED, + CompanyApplicationStatusId.DECLINED + ] + }; +} diff --git a/src/administration/Administration.Service/appsettings.json b/src/administration/Administration.Service/appsettings.json index f9274c738a..377d312c63 100644 --- a/src/administration/Administration.Service/appsettings.json +++ b/src/administration/Administration.Service/appsettings.json @@ -307,7 +307,8 @@ "Scope": "", "TokenAddress": "", "BaseAddress": "", - "UseDimWallet": false + "UseDimWallet": false, + "RetriggerEndClearinghouseIntervalInDays": 30 }, "SdFactory": { "Username": "", diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs index a71f5ab7c8..157d534101 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Custodian.Library.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; 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; @@ -33,6 +34,7 @@ public class ClearinghouseBusinessLogic( IClearinghouseService clearinghouseService, ICustodianBusinessLogic custodianBusinessLogic, IApplicationChecklistService checklistService, + IDateTimeProvider dateTimeProvider, IOptions options) : IClearinghouseBusinessLogic { @@ -136,4 +138,39 @@ public async Task ProcessEndClearinghouse(Guid applicationId, ClearinghouseRespo ? [ProcessStepTypeId.MANUAL_TRIGGER_OVERRIDE_CLEARING_HOUSE] : [ProcessStepTypeId.START_SELF_DESCRIPTION_LP]); } + + public async Task CheckEndClearinghouseProcesses(CancellationToken stoppingToken) + { + var applicationIds = await portalRepositories.GetInstance() + .GetApplicationsForClearinghouseRetrigger(dateTimeProvider.OffsetNow.AddDays(-_settings.RetriggerEndClearinghouseIntervalInDays)) + .ToListAsync(stoppingToken).ConfigureAwait(false); + + var hasChanges = false; + foreach (var applicationId in applicationIds) + { + var context = await checklistService + .VerifyChecklistEntryAndProcessSteps( + applicationId, + ApplicationChecklistEntryTypeId.CLEARING_HOUSE, + [ApplicationChecklistEntryStatusId.IN_PROGRESS], + ProcessStepTypeId.END_CLEARING_HOUSE) + .ConfigureAwait(ConfigureAwaitOptions.None); + + checklistService.FinalizeChecklistEntryAndProcessSteps( + context, + null, + item => + { + item.ApplicationChecklistEntryStatusId = ApplicationChecklistEntryStatusId.TO_DO; + item.Comment = "Reset to retrigger clearinghouse"; + }, + [ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE]); + hasChanges = true; + } + + if (hasChanges) + { + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + } + } } diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs index a0b024b0f7..bc880a38fa 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs @@ -27,4 +27,5 @@ public interface IClearinghouseBusinessLogic { Task HandleClearinghouse(IApplicationChecklistService.WorkerChecklistProcessStepData context, CancellationToken cancellationToken); Task ProcessEndClearinghouse(Guid applicationId, ClearinghouseResponseData data, CancellationToken cancellationToken); + Task CheckEndClearinghouseProcesses(CancellationToken stoppingToken); } diff --git a/src/externalsystems/Clearinghouse.Library/ClearinghouseSettings.cs b/src/externalsystems/Clearinghouse.Library/ClearinghouseSettings.cs index de4cfca5c0..3eae16b761 100644 --- a/src/externalsystems/Clearinghouse.Library/ClearinghouseSettings.cs +++ b/src/externalsystems/Clearinghouse.Library/ClearinghouseSettings.cs @@ -35,4 +35,7 @@ public class ClearinghouseSettings : KeyVaultAuthSettings public string CallbackUrl { get; set; } = null!; public bool UseDimWallet { get; set; } + + [Required] + public int RetriggerEndClearinghouseIntervalInDays { get; set; } } diff --git a/src/maintenance/Maintenance.App/BatchDeleteService.cs b/src/maintenance/Maintenance.App/BatchDeleteService.cs deleted file mode 100644 index 71738aa79b..0000000000 --- a/src/maintenance/Maintenance.App/BatchDeleteService.cs +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022 BMW Group AG - * Copyright (c) 2022 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Microsoft.EntityFrameworkCore; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; - -namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App; - -/// -/// Service to delete the pending and inactive documents as well as the depending consents from the database -/// -public class BatchDeleteService : BackgroundService -{ - private readonly IHostApplicationLifetime _applicationLifetime; - private readonly IServiceScopeFactory _serviceScopeFactory; - private readonly ILogger _logger; - private readonly int _days; - - /// - /// Creates a new instance of - /// - /// Application lifetime - /// access to the services - /// the logger - /// the apps configuration - public BatchDeleteService( - IHostApplicationLifetime applicationLifetime, - IServiceScopeFactory serviceScopeFactory, - ILogger logger, - IConfiguration config) - { - _applicationLifetime = applicationLifetime; - _serviceScopeFactory = serviceScopeFactory; - _logger = logger; - _days = config.GetValue("DeleteIntervalInDays"); - } - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - using var scope = _serviceScopeFactory.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - - if (!stoppingToken.IsCancellationRequested) - { - try - { - _logger.LogInformation("Getting documents and assignments older {Days} days", _days); - List<(Guid DocumentId, IEnumerable AgreementIds, IEnumerable OfferIds)> documentData = await dbContext.Documents.Where(x => - x.DateCreated < DateTimeOffset.UtcNow.AddDays(-_days) && - !x.Companies.Any() && - x.Connector == null && - !x.Consents.Any() && - x.DocumentStatusId == DocumentStatusId.INACTIVE) - .Select(doc => new ValueTuple, IEnumerable>( - doc.Id, - doc.Agreements.Select(x => x.Id), - doc.Offers.Select(x => x.Id) - )) - .ToListAsync(stoppingToken) - .ConfigureAwait(ConfigureAwaitOptions.None); - _logger.LogInformation("Cleaning up {DocumentCount} Documents and {OfferIdCount} OfferAssignedDocuments", documentData.Count, documentData.SelectMany(x => x.OfferIds).Count()); - - var agreementsToDeleteDocumentId = documentData.SelectMany(data => data.AgreementIds.Select(agreementId => new Agreement(agreementId, default, null!, default, default, default) { DocumentId = data.DocumentId })).ToList(); - dbContext.Agreements.AttachRange(agreementsToDeleteDocumentId); - agreementsToDeleteDocumentId.ForEach(agreement => agreement.DocumentId = null); - dbContext.OfferAssignedDocuments.RemoveRange(documentData.SelectMany(data => data.OfferIds.Select(offerId => new OfferAssignedDocument(offerId, data.DocumentId)))); - dbContext.Documents.RemoveRange(documentData.Select(x => new Document(x.DocumentId, null!, null!, null!, default, default, default, default))); - await dbContext.SaveChangesAsync(stoppingToken).ConfigureAwait(ConfigureAwaitOptions.None); - _logger.LogInformation("Documents older than {Days} days and depending consents successfully cleaned up", _days); - } - catch (Exception ex) - { - Environment.ExitCode = 1; - _logger.LogError("Database clean up failed with error: {Errors}", ex.Message); - } - } - - _applicationLifetime.StopApplication(); - } -} diff --git a/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs b/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs new file mode 100644 index 0000000000..7fca4fa931 --- /dev/null +++ b/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs @@ -0,0 +1,36 @@ +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; +using System.ComponentModel.DataAnnotations; + +namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.DependencyInjection; + +/// +/// Settings for the +/// +public class BatchDeleteServiceSettings +{ + /// + /// Documents older than this configured value will be deleted + /// + [Required] + public int DeleteIntervalInDays { get; set; } +} + +/// +/// Extensions for the +/// +public static class BatchDeleteServiceExtensions +{ + /// + /// Adds the to the service collection + /// + /// The service collection used for di + /// The configuration section to get the settings from + /// The enhanced service collection + public static IServiceCollection AddBatchDelete(this IServiceCollection services, IConfigurationSection section) + { + services.AddOptions().Bind(section); + services + .AddTransient(); + return services; + } +} diff --git a/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs b/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs new file mode 100644 index 0000000000..3e0e3ad8fe --- /dev/null +++ b/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs @@ -0,0 +1,20 @@ +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; + +namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.DependencyInjection; + +/// +/// Extension methods to register the necessary services for the maintenance job +/// +public static class MaintenanceServiceExtensions +{ + /// + /// Adds the dependencies for the maintenance service + /// + /// The service collection + /// The enhanced service collection + public static IServiceCollection AddMaintenanceService(this IServiceCollection services) => + services + .AddTransient() + .AddTransient(); +} diff --git a/src/maintenance/Maintenance.App/Maintenance.App.csproj b/src/maintenance/Maintenance.App/Maintenance.App.csproj index 40d65b3fbc..2c5b5a2ec1 100644 --- a/src/maintenance/Maintenance.App/Maintenance.App.csproj +++ b/src/maintenance/Maintenance.App/Maintenance.App.csproj @@ -46,6 +46,7 @@ + diff --git a/src/maintenance/Maintenance.App/Program.cs b/src/maintenance/Maintenance.App/Program.cs index 8b3e261e30..91d7cacdc7 100644 --- a/src/maintenance/Maintenance.App/Program.cs +++ b/src/maintenance/Maintenance.App/Program.cs @@ -20,8 +20,10 @@ using Laraue.EfCoreTriggers.PostgreSql.Extensions; using Microsoft.EntityFrameworkCore; +using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library; using Org.Eclipse.TractusX.Portal.Backend.Framework.Logging; -using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Auditing; using Org.Eclipse.TractusX.Portal.Backend.Processes.ProcessIdentity.DependencyInjection; @@ -31,22 +33,36 @@ Log.Information("Building service"); try { - var host = Host.CreateDefaultBuilder(args) - .UseSystemd() + var host = Host + .CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { services + .AddMaintenanceService() .AddConfigurationProcessIdentityIdDetermination(hostContext.Configuration.GetSection("ProcessIdentity")) + .AddBatchDelete(hostContext.Configuration.GetSection("BatchDelete")) + .AddClearinghouseService(hostContext.Configuration.GetSection("Clearinghouse")) .AddDbAuditing() .AddDbContext(o => o.UseNpgsql(hostContext.Configuration.GetConnectionString("PortalDb")) - .UsePostgreSqlTriggers()); - services.AddHostedService(); + .UsePostgreSqlTriggers()); }) .AddLogging() .Build(); + Log.Information("Building worker completed"); - await host.RunAsync().ConfigureAwait(ConfigureAwaitOptions.None); + using var tokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += (s, e) => + { + Log.Information("Canceling..."); + tokenSource.Cancel(); + e.Cancel = true; + }; + + Log.Information("Start processing"); + var workerInstance = host.Services.GetRequiredService(); + await workerInstance.ExecuteAsync(tokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.None); + Log.Information("Execution finished shutting down"); } catch (Exception ex) when (!ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) { diff --git a/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs new file mode 100644 index 0000000000..97895877e8 --- /dev/null +++ b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; + +namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; + +/// +public class BatchDeleteService( + ILogger logger, + IOptions options, + IPortalRepositories portalRepositories, + IDateTimeProvider dateTimeProvider) : IBatchDeleteService +{ + private readonly BatchDeleteServiceSettings _settings = options.Value; + + /// + public async Task CleanupDocuments(CancellationToken cancellationToken) + { + try + { + logger.LogInformation("Getting documents and assignments older {Days} days", _settings.DeleteIntervalInDays); + var documentRepository = portalRepositories.GetInstance(); + var documentData = await documentRepository + .GetDocumentDataForCleanup(dateTimeProvider.OffsetNow.AddDays(-_settings.DeleteIntervalInDays)) + .ConfigureAwait(ConfigureAwaitOptions.None); + if (documentData.Count == 0) + { + logger.LogInformation("No documents to cleanup"); + return; + } + + logger.LogInformation("Cleaning up {DocumentCount} Documents and {OfferIdCount} OfferAssignedDocuments", documentData.Count, documentData.SelectMany(x => x.OfferIds).Count()); + + var agreementsToDeleteDocumentId = documentData.SelectMany(data => data.AgreementIds.Select(agreementId => new Agreement(agreementId, default, null!, default, default, default) { DocumentId = data.DocumentId })).ToList(); + portalRepositories.AttachRange(agreementsToDeleteDocumentId); + agreementsToDeleteDocumentId.ForEach(agreement => agreement.DocumentId = null); + documentRepository.RemoveOfferAssignedDocuments(documentData.SelectMany(data => data.OfferIds.Select(offerId => new OfferAssignedDocument(offerId, data.DocumentId)))); + documentRepository.RemoveDocuments(documentData.Select(x => x.DocumentId)); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); + logger.LogInformation("Documents older than {Days} days and depending consents successfully cleaned up", _settings.DeleteIntervalInDays); + } + catch (Exception ex) + { + logger.LogError("Database clean up failed with error: {Errors}", ex.Message); + } + } +} diff --git a/src/maintenance/Maintenance.App/Services/IBatchDeleteService.cs b/src/maintenance/Maintenance.App/Services/IBatchDeleteService.cs new file mode 100644 index 0000000000..f680226bec --- /dev/null +++ b/src/maintenance/Maintenance.App/Services/IBatchDeleteService.cs @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; + +/// +/// Service to delete the pending and inactive documents as well as the depending on consents from the database +/// +public interface IBatchDeleteService +{ + /// + /// Cleans up the documents and related entries from the database + /// + /// The cancellation token + Task CleanupDocuments(CancellationToken cancellationToken); +} diff --git a/src/maintenance/Maintenance.App/Services/MaintenanceService.cs b/src/maintenance/Maintenance.App/Services/MaintenanceService.cs new file mode 100644 index 0000000000..7fd7262bec --- /dev/null +++ b/src/maintenance/Maintenance.App/Services/MaintenanceService.cs @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2022 BMW Group AG + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Processes.ProcessIdentity; + +namespace Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; + +/// +/// Service to +/// 1. delete the pending and inactive documents as well as the depending on consents from the database +/// 2. schedule clearinghouse process steps where the END_CLEARINGHOUSE is in todo for a specific duration +/// +public class MaintenanceService(IServiceScopeFactory serviceScopeFactory) +{ + /// + /// executes the logic + /// + /// The cancellation token + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + using var scope = serviceScopeFactory.CreateScope(); + + var processIdentityDataDetermination = scope.ServiceProvider.GetRequiredService(); + //call processIdentityDataDetermination.GetIdentityData() once to initialize IdentityService IdentityData for synchronous use: + await processIdentityDataDetermination.GetIdentityData().ConfigureAwait(ConfigureAwaitOptions.None); + + var batchDeleteBusinessLogic = scope.ServiceProvider.GetRequiredService(); + var clearinghouseBusinessLogic = scope.ServiceProvider.GetRequiredService(); + + if (!cancellationToken.IsCancellationRequested) + { + await batchDeleteBusinessLogic.CleanupDocuments(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await clearinghouseBusinessLogic.CheckEndClearinghouseProcesses(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + } + } +} diff --git a/src/maintenance/Maintenance.App/appsettings.json b/src/maintenance/Maintenance.App/appsettings.json index c68407236b..f340ce15e3 100644 --- a/src/maintenance/Maintenance.App/appsettings.json +++ b/src/maintenance/Maintenance.App/appsettings.json @@ -23,7 +23,21 @@ "ConnectionStrings": { "PortalDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=placeholder;Ssl Mode=Disable;" }, - "DeleteIntervalInDays": 80, + "BatchDelete": { + "DeleteIntervalInDays": 80 + }, + "Clearinghouse": { + "Username": "", + "Password": "", + "ClientId": "", + "GrantType": "", + "ClientSecret": "", + "Scope": "", + "TokenAddress": "", + "BaseAddress": "", + "UseDimWallet": false, + "RetriggerEndClearinghouseIntervalInDays": 30 + }, "ProcessIdentity": { "ProcessUserId": "" } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs index 468736470f..51fe492b00 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs @@ -26,19 +26,9 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; -public class ApplicationChecklistRepository : IApplicationChecklistRepository +public class ApplicationChecklistRepository(PortalDbContext dbContext) + : IApplicationChecklistRepository { - private readonly PortalDbContext _portalDbContext; - - /// - /// Creates a new instance of - /// - /// The portal db context - public ApplicationChecklistRepository(PortalDbContext portalDbContext) - { - _portalDbContext = portalDbContext; - } - /// public void CreateChecklistForApplication(Guid applicationId, IEnumerable<(ApplicationChecklistEntryTypeId TypeId, ApplicationChecklistEntryStatusId StatusId)> checklistEntries) { @@ -48,7 +38,7 @@ public void CreateChecklistForApplication(Guid applicationId, IEnumerable<(Appli x.TypeId, x.StatusId, DateTimeOffset.UtcNow)); - _portalDbContext.ApplicationChecklist.AddRange(entries); + dbContext.ApplicationChecklist.AddRange(entries); } /// @@ -56,14 +46,14 @@ public ApplicationChecklistEntry AttachAndModifyApplicationChecklist(Guid applic { var entity = new ApplicationChecklistEntry(applicationId, applicationChecklistTypeId, default, default); initialize?.Invoke(entity); - _portalDbContext.ApplicationChecklist.Attach(entity); + dbContext.ApplicationChecklist.Attach(entity); entity.DateLastChanged = DateTimeOffset.UtcNow; setFields.Invoke(entity); return entity; } public Task<(bool IsValidProcessId, Guid ApplicationId, CompanyApplicationStatusId ApplicationStatusId, IEnumerable<(ApplicationChecklistEntryTypeId EntryTypeId, ApplicationChecklistEntryStatusId EntryStatusId)> Checklist)> GetChecklistData(Guid processId) => - _portalDbContext.Processes + dbContext.Processes .AsNoTracking() .Where(process => process.Id == processId) .Select(process => @@ -79,7 +69,7 @@ public ApplicationChecklistEntry AttachAndModifyApplicationChecklist(Guid applic .SingleOrDefaultAsync(); public Task GetChecklistProcessStepData(Guid applicationId, IEnumerable entryTypeIds, IEnumerable processStepTypeIds) => - _portalDbContext.CompanyApplications + dbContext.CompanyApplications .AsNoTracking() .AsSplitQuery() .Where(application => application.Id == applicationId) @@ -106,4 +96,19 @@ public ApplicationChecklistEntry AttachAndModifyApplicationChecklist(Guid applic step.ProcessStepStatusId == ProcessStepStatusId.TODO) : null)) .SingleOrDefaultAsync(); + + public IAsyncEnumerable GetApplicationsForClearinghouseRetrigger(DateTimeOffset dateCreatedThreshold) => + dbContext.CompanyApplications + .Where(ca => + ca.ApplicationChecklistEntries.Any(ce => + ce.ApplicationChecklistEntryTypeId == ApplicationChecklistEntryTypeId.CLEARING_HOUSE && + ce.ApplicationChecklistEntryStatusId == ApplicationChecklistEntryStatusId.IN_PROGRESS && + ce.DateCreated < dateCreatedThreshold) && + ca.ChecklistProcess!.ProcessTypeId == ProcessTypeId.APPLICATION_CHECKLIST && + ca.ChecklistProcess!.ProcessSteps.Any(ps => + ps.ProcessStepTypeId == ProcessStepTypeId.END_CLEARING_HOUSE && + ps.ProcessStepStatusId == ProcessStepStatusId.TODO && + ps.DateCreated < dateCreatedThreshold)) + .Select(x => x.Id) + .ToAsyncEnumerable(); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs index a071201172..e6a7e9a861 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs @@ -26,19 +26,8 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; /// Implementation of accessing database with EF Core. -public class DocumentRepository : IDocumentRepository +public class DocumentRepository(PortalDbContext dbContext) : IDocumentRepository { - private readonly PortalDbContext _dbContext; - - /// - /// Constructor. - /// - /// PortalDb context. - public DocumentRepository(PortalDbContext dbContext) - { - _dbContext = dbContext; - } - /// public Document CreateDocument(string documentName, byte[] documentContent, byte[] hash, MediaTypeId mediaTypeId, DocumentTypeId documentTypeId, Action? setupOptionalFields) { @@ -53,12 +42,12 @@ public Document CreateDocument(string documentName, byte[] documentContent, byte documentTypeId); setupOptionalFields?.Invoke(document); - return _dbContext.Documents.Add(document).Entity; + return dbContext.Documents.Add(document).Entity; } /// public Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, IEnumerable ConsentIds, bool IsSameUser)> GetDocumentDetailsForIdUntrackedAsync(Guid documentId, Guid companyUserId) => - _dbContext.Documents + dbContext.Documents .AsNoTracking() .Where(x => x.Id == documentId) .Select(document => @@ -70,7 +59,7 @@ public Document CreateDocument(string documentName, byte[] documentContent, byte .SingleOrDefaultAsync(); public Task<(bool IsApplicationAssignedUser, IEnumerable Documents)> GetUploadedDocumentsAsync(Guid applicationId, DocumentTypeId documentTypeId, Guid companyUserId) => - _dbContext.CompanyApplications + dbContext.CompanyApplications .AsNoTracking() .Where(application => application.Id == applicationId) .Select(application => new @@ -92,14 +81,14 @@ public Document CreateDocument(string documentName, byte[] documentContent, byte /// public Task<(Guid DocumentId, bool IsSameUser)> GetDocumentIdWithCompanyUserCheckAsync(Guid documentId, Guid companyUserId) => - _dbContext.Documents + dbContext.Documents .Where(x => x.Id == documentId) .Select(x => new ValueTuple(x.Id, x.CompanyUserId == companyUserId)) .SingleOrDefaultAsync(); /// public Task<(byte[]? Content, string FileName, MediaTypeId MediaTypeId, bool IsUserInCompany)> GetDocumentDataAndIsCompanyUserAsync(Guid documentId, Guid userCompanyId) => - _dbContext.Documents + dbContext.Documents .Where(x => x.Id == documentId) .Select(x => new { @@ -115,22 +104,22 @@ public Document CreateDocument(string documentName, byte[] documentContent, byte /// public Task<(byte[] Content, string FileName, MediaTypeId MediaTypeId)> GetDocumentDataByIdAndTypeAsync(Guid documentId, DocumentTypeId documentTypeId) => - _dbContext.Documents + dbContext.Documents .Where(x => x.Id == documentId && x.DocumentTypeId == documentTypeId) .Select(x => new ValueTuple(x.DocumentContent, x.DocumentName, x.MediaTypeId)) .SingleOrDefaultAsync(); /// public void RemoveDocument(Guid documentId) => - _dbContext.Documents.Remove(new Document(documentId, null!, null!, null!, default, default, default, default)); + dbContext.Documents.Remove(new Document(documentId, null!, null!, null!, default, default, default, default)); /// public Task GetDocumentByIdAsync(Guid documentId) => - _dbContext.Documents.SingleOrDefaultAsync(x => x.Id == documentId); + dbContext.Documents.SingleOrDefaultAsync(x => x.Id == documentId); /// public Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, bool IsSameApplicationUser, DocumentTypeId documentTypeId, bool IsQueriedApplicationStatus, IEnumerable applicationId)> GetDocumentDetailsForApplicationUntrackedAsync(Guid documentId, Guid userCompanyId, IEnumerable applicationStatusIds) => - _dbContext.Documents + dbContext.Documents .AsNoTracking() .Where(x => x.Id == documentId) .Select(document => new @@ -152,7 +141,7 @@ public void AttachAndModifyDocument(Guid documentId, Action? initializ { var document = new Document(documentId, null!, null!, null!, default, default, default, default); initialize?.Invoke(document); - _dbContext.Attach(document); + dbContext.Attach(document); modify(document); } @@ -165,13 +154,13 @@ public void AttachAndModifyDocuments(IEnumerable<(Guid DocumentId, Action x.Document)); + dbContext.AttachRange(initial.Select(x => x.Document)); initial.ForEach(x => x.Modify(x.Document)); } /// public Task GetDocumentSeedDataByIdAsync(Guid documentId) => - _dbContext.Documents + dbContext.Documents .AsNoTracking() .Where(x => x.Id == documentId) .Select(doc => new DocumentSeedData( @@ -187,7 +176,7 @@ public void AttachAndModifyDocuments(IEnumerable<(Guid DocumentId, Action public Task GetOfferDocumentContentAsync(Guid offerId, Guid documentId, IEnumerable documentTypeIds, OfferTypeId offerTypeId, CancellationToken cancellationToken) => - _dbContext.Documents + dbContext.Documents .Where(document => document.Id == documentId) .Select(document => new { @@ -215,7 +204,7 @@ public void AttachAndModifyDocuments(IEnumerable<(Guid DocumentId, Action public Task<(IEnumerable<(OfferStatusId OfferStatusId, Guid OfferId, bool IsOfferType)> OfferData, bool IsDocumentTypeMatch, DocumentStatusId DocumentStatusId, bool IsProviderCompanyUser)> GetOfferDocumentsAsync(Guid documentId, Guid userCompanyId, IEnumerable documentTypeIds, OfferTypeId offerTypeId) => - _dbContext.Documents + dbContext.Documents .Where(document => document.Id == documentId) .Select(document => new { @@ -240,12 +229,28 @@ public void AttachAndModifyDocuments(IEnumerable<(Guid DocumentId, Action public void RemoveDocuments(IEnumerable documentIds) => - _dbContext.Documents.RemoveRange(documentIds.Select(documentId => new Document(documentId, null!, null!, null!, default, default, default, default))); + dbContext.Documents.RemoveRange(documentIds.Select(documentId => new Document(documentId, null!, null!, null!, default, default, default, default))); + + public void RemoveOfferAssignedDocuments(IEnumerable offerAssignedDocuments) => + dbContext.OfferAssignedDocuments.RemoveRange(offerAssignedDocuments); public Task<(byte[] Content, string FileName, bool IsDocumentTypeMatch, MediaTypeId MediaTypeId)> GetDocumentAsync(Guid documentId, IEnumerable documentTypeIds) => - _dbContext.Documents + dbContext.Documents .Where(x => x.Id == documentId) .Select(x => new ValueTuple(x.DocumentContent, x.DocumentName, documentTypeIds.Contains(x.DocumentTypeId), x.MediaTypeId)) .SingleOrDefaultAsync(); + public Task AgreementIds, IEnumerable OfferIds)>> GetDocumentDataForCleanup(DateTimeOffset dateCreated) => + dbContext.Documents.Where(x => + x.DateCreated < dateCreated && + !x.Companies.Any() && + x.Connector == null && + !x.Consents.Any() && + x.DocumentStatusId == DocumentStatusId.INACTIVE) + .Select(doc => new ValueTuple, IEnumerable>( + doc.Id, + doc.Agreements.Select(x => x.Id), + doc.Offers.Select(x => x.Id) + )) + .ToListAsync(); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IApplicationChecklistRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IApplicationChecklistRepository.cs index e7d0323ac6..c632346ebb 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IApplicationChecklistRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IApplicationChecklistRepository.cs @@ -46,4 +46,5 @@ public interface IApplicationChecklistRepository Task<(bool IsValidProcessId, Guid ApplicationId, CompanyApplicationStatusId ApplicationStatusId, IEnumerable<(ApplicationChecklistEntryTypeId EntryTypeId, ApplicationChecklistEntryStatusId EntryStatusId)> Checklist)> GetChecklistData(Guid processId); Task GetChecklistProcessStepData(Guid applicationId, IEnumerable entryTypeIds, IEnumerable processStepTypeIds); + IAsyncEnumerable GetApplicationsForClearinghouseRetrigger(DateTimeOffset dateCreatedThreshold); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs index 88b36d717d..e3e2c84485 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs @@ -146,6 +146,12 @@ public interface IDocumentRepository /// void RemoveDocuments(IEnumerable documentIds); + /// + /// Delete List Of Document + /// + /// + void RemoveOfferAssignedDocuments(IEnumerable offerAssignedDocuments); + /// /// Gets the registration document with the given id /// @@ -153,4 +159,6 @@ public interface IDocumentRepository /// the document types /// Task<(byte[] Content, string FileName, bool IsDocumentTypeMatch, MediaTypeId MediaTypeId)> GetDocumentAsync(Guid documentId, IEnumerable documentTypeIds); + + Task AgreementIds, IEnumerable OfferIds)>> GetDocumentDataForCleanup(DateTimeOffset dateCreated); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ProcessStepRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ProcessStepRepository.cs index af4077983e..7c53f6d24d 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ProcessStepRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ProcessStepRepository.cs @@ -26,36 +26,25 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; -public class ProcessStepRepository : IProcessStepRepository +public class ProcessStepRepository(PortalDbContext dbContext) : IProcessStepRepository { - private readonly PortalDbContext _context; - - /// - /// Constructor - /// - /// PortalDb context. - public ProcessStepRepository(PortalDbContext portalDbContext) - { - _context = portalDbContext; - } - public Process CreateProcess(ProcessTypeId processTypeId) => - _context.Add(new Process(Guid.NewGuid(), processTypeId, Guid.NewGuid())).Entity; + dbContext.Add(new Process(Guid.NewGuid(), processTypeId, Guid.NewGuid())).Entity; public IEnumerable CreateProcessRange(IEnumerable processTypeIds) { var processes = processTypeIds.Select(x => new Process(Guid.NewGuid(), x, Guid.NewGuid())).ToImmutableList(); - _context.AddRange(processes); + dbContext.AddRange(processes); return processes; } public ProcessStep CreateProcessStep(ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid processId) => - _context.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, processId, DateTimeOffset.UtcNow)).Entity; + dbContext.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, processId, DateTimeOffset.UtcNow)).Entity; public IEnumerable CreateProcessStepRange(IEnumerable<(ProcessStepTypeId ProcessStepTypeId, ProcessStepStatusId ProcessStepStatusId, Guid ProcessId)> processStepTypeStatus) { var processSteps = processStepTypeStatus.Select(x => new ProcessStep(Guid.NewGuid(), x.ProcessStepTypeId, x.ProcessStepStatusId, x.ProcessId, DateTimeOffset.UtcNow)).ToImmutableList(); - _context.AddRange(processSteps); + dbContext.AddRange(processSteps); return processSteps; } @@ -63,7 +52,7 @@ public void AttachAndModifyProcessStep(Guid processStepId, Action? { var step = new ProcessStep(processStepId, default, default, Guid.Empty, default); initialize?.Invoke(step); - _context.Attach(step); + dbContext.Attach(step); step.DateLastChanged = DateTimeOffset.UtcNow; modify(step); } @@ -76,7 +65,7 @@ public void AttachAndModifyProcessSteps(IEnumerable<(Guid ProcessStepId, Action< data.Initialize?.Invoke(step); return (Step: step, data.Modify); }).ToImmutableList(); - _context.AttachRange(stepModifyData.Select(data => data.Step)); + dbContext.AttachRange(stepModifyData.Select(data => data.Step)); stepModifyData.ForEach(data => { data.Step.DateLastChanged = DateTimeOffset.UtcNow; @@ -85,7 +74,7 @@ public void AttachAndModifyProcessSteps(IEnumerable<(Guid ProcessStepId, Action< } public IAsyncEnumerable GetActiveProcesses(IEnumerable processTypeIds, IEnumerable processStepTypeIds, DateTimeOffset lockExpiryDate) => - _context.Processes + dbContext.Processes .AsNoTracking() .Where(process => processTypeIds.Contains(process.ProcessTypeId) && @@ -94,8 +83,7 @@ public IAsyncEnumerable GetActiveProcesses(IEnumerable p .AsAsyncEnumerable(); public IAsyncEnumerable<(Guid ProcessStepId, ProcessStepTypeId ProcessStepTypeId)> GetProcessStepData(Guid processId) => - _context.ProcessSteps - .AsNoTracking() + dbContext.ProcessSteps .Where(step => step.ProcessId == processId && step.ProcessStepStatusId == ProcessStepStatusId.TODO) @@ -107,8 +95,7 @@ public IAsyncEnumerable GetActiveProcesses(IEnumerable p .AsAsyncEnumerable(); public Task<(bool ProcessExists, VerifyProcessData ProcessData)> IsValidProcess(Guid processId, ProcessTypeId processTypeId, IEnumerable processStepTypeIds) => - _context.Processes - .AsNoTracking() + dbContext.Processes .Where(x => x.Id == processId && x.ProcessTypeId == processTypeId) .Select(x => new ValueTuple( true, @@ -122,8 +109,7 @@ public IAsyncEnumerable GetActiveProcesses(IEnumerable p .SingleOrDefaultAsync(); public Task<(ProcessTypeId ProcessTypeId, VerifyProcessData ProcessData, Guid? ServiceAccountId, Guid? ServiceAccountVersion)> GetProcessDataForServiceAccountCallback(Guid processId, IEnumerable processStepTypeIds) => - _context.Processes - .AsNoTracking() + dbContext.Processes .Where(x => x.Id == processId) .Select(x => new ValueTuple( x.ProcessTypeId, @@ -139,8 +125,7 @@ public IAsyncEnumerable GetActiveProcesses(IEnumerable p .SingleOrDefaultAsync(); public Task<(ProcessTypeId ProcessTypeId, VerifyProcessData ProcessData, Guid? ServiceAccountId)> GetProcessDataForServiceAccountDeletionCallback(Guid processId, IEnumerable? processStepTypeIds) => - _context.Processes - .AsNoTracking() + dbContext.Processes .Where(x => x.Id == processId && x.ProcessTypeId == ProcessTypeId.DIM_TECHNICAL_USER) .Select(x => new ValueTuple( x.ProcessTypeId, diff --git a/src/processes/Processes.Worker/appsettings.json b/src/processes/Processes.Worker/appsettings.json index fe0ba62f9f..a30cff5fa1 100644 --- a/src/processes/Processes.Worker/appsettings.json +++ b/src/processes/Processes.Worker/appsettings.json @@ -345,7 +345,8 @@ "Scope": "", "TokenAddress": "", "BaseAddress": "", - "UseDimWallet": false + "UseDimWallet": false, + "RetriggerEndClearinghouseIntervalInDays": 30 }, "Dim": { "Username": "", diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs index b0b4a1d0e4..e6a41683d6 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/RegistrationBusinessLogicTest.cs @@ -205,7 +205,7 @@ public async Task GetCompanyApplicationDetailsAsync_WithClosedRequest_GetsExpect public async Task GetOspCompanyApplicationDetailsAsync_WithDefaultRequest_GetsExpectedEntries(CompanyApplicationStatusFilter? statusFilter, DateCreatedOrderFilter? dateCreatedOrderFilter) { // Arrange - var companyName = _fixture.Create(); + var companyName = "TestCompany"; var externalId = _fixture.Create(); var data = _fixture.CreateMany<(Guid Id, Guid CompanyId, CompanyApplicationStatusId CompanyApplicationStatusId, DateTimeOffset Created)>(10) .Select(x => new CompanyApplication(x.Id, x.CompanyId, x.CompanyApplicationStatusId, CompanyApplicationTypeId.EXTERNAL, x.Created) diff --git a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json index c5200bde64..b6600bcbfa 100644 --- a/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json +++ b/tests/administration/Administration.Service.Tests/appsettings.IntegrationTests.json @@ -315,7 +315,9 @@ "ClientSecret": "test", "Scope": "test", "TokenAddress": "test", - "BaseAddress": "test" + "BaseAddress": "test", + "UseDimWallet": false, + "RetriggerEndClearinghouseIntervalInDays": 30 }, "SdFactory":{ "Username": "test", diff --git a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs index 578c8f1d88..3a03967099 100644 --- a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs +++ b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs @@ -22,6 +22,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Custodian.Library.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Custodian.Library.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; @@ -52,6 +53,8 @@ public class ClearinghouseBusinessLogicTests private readonly IClearinghouseService _clearinghouseService; private readonly IApplicationChecklistService _checklistService; private readonly ICustodianBusinessLogic _custodianBusinessLogic; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IApplicationChecklistRepository _applicationChecklistRepository; public ClearinghouseBusinessLogicTests() { @@ -61,14 +64,19 @@ public ClearinghouseBusinessLogicTests() _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); _applicationRepository = A.Fake(); + _applicationChecklistRepository = A.Fake(); _portalRepositories = A.Fake(); _clearinghouseService = A.Fake(); _custodianBusinessLogic = A.Fake(); _checklistService = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_applicationRepository); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_applicationChecklistRepository); - _logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, Options.Create(new ClearinghouseSettings + _dateTimeProvider = A.Fake(); + A.CallTo(() => _dateTimeProvider.OffsetNow).Returns(DateTimeOffset.UtcNow); + + _logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings { CallbackUrl = "https://api.com", UseDimWallet = false @@ -225,7 +233,7 @@ public async Task HandleStartClearingHouse_WithDimActiveAndNonExistingApplicatio var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns<(bool, string?)>(default); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, Options.Create(new ClearinghouseSettings + var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings { CallbackUrl = "https://api.com", UseDimWallet = true @@ -256,7 +264,7 @@ public async Task HandleStartClearingHouse_WithDimActiveAndDidNotSet_ThrowsConfl var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns((true, null)); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, Options.Create(new ClearinghouseSettings + var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings { CallbackUrl = "https://api.com", UseDimWallet = true @@ -289,7 +297,7 @@ public async Task HandleStartClearingHouse_WithDimActive_CallsExpected() A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns((true, "did:web:test123456")); SetupForHandleStartClearingHouse(); - var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, Options.Create(new ClearinghouseSettings + var logic = new ClearinghouseBusinessLogic(_portalRepositories, _clearinghouseService, _custodianBusinessLogic, _checklistService, _dateTimeProvider, Options.Create(new ClearinghouseSettings { CallbackUrl = "https://api.com", UseDimWallet = true @@ -360,6 +368,29 @@ public async Task ProcessClearinghouseResponseAsync_WithDecline_UpdatesEntry() #endregion + #region CheckEndClearinghouseProcesses + + [Fact] + public async Task CheckEndClearinghouseProcesses_WithEntry_CreatesProcessStep() + { + // Arrange + var entry = new ApplicationChecklistEntry(IdWithBpn, ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); + A.CallTo(() => _applicationChecklistRepository.GetApplicationsForClearinghouseRetrigger(A._)) + .Returns(Enumerable.Repeat(IdWithBpn, 1).ToAsyncEnumerable()); + SetupForCheckEndClearinghouseProcesses(Enumerable.Repeat(entry, 1)); + + // Act + await _logic.CheckEndClearinghouseProcesses(CancellationToken.None); + + // Assert + A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps(A._, A>._, A>._, A>.That.Matches(x => x.Count(y => y == ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE) == 1))).MustHaveHappenedOnceExactly(); + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + entry.Comment.Should().Be("Reset to retrigger clearinghouse"); + entry.ApplicationChecklistEntryStatusId.Should().Be(ApplicationChecklistEntryStatusId.TO_DO); + } + + #endregion + #region Setup private void SetupForHandleStartClearingHouse() @@ -418,5 +449,39 @@ private void SetupForProcessClearinghouseResponse(ApplicationChecklistEntry appl .Returns(new IApplicationChecklistService.ManualChecklistProcessStepData(Guid.Empty, new Process(Guid.NewGuid(), ProcessTypeId.APPLICATION_CHECKLIST, Guid.NewGuid()), Guid.Empty, ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ImmutableDictionary.Empty, new List())); } + private void SetupForCheckEndClearinghouseProcesses(IEnumerable applicationChecklistEntries) + { + A.CallTo(() => _checklistService.FinalizeChecklistEntryAndProcessSteps(A._, A>._, A>._, A>._)) + .Invokes((IApplicationChecklistService.ManualChecklistProcessStepData data, Action? _, Action modifyApplicationChecklistEntry, IEnumerable _) => + { + var entry = applicationChecklistEntries.SingleOrDefault(x => x.ApplicationId == data.ApplicationId); + if (entry == null) + return; + + entry.DateLastChanged = DateTimeOffset.UtcNow; + modifyApplicationChecklistEntry(entry); + }); + + A.CallTo(() => _checklistService.VerifyChecklistEntryAndProcessSteps( + A._, + ApplicationChecklistEntryTypeId.CLEARING_HOUSE, + A>._, + ProcessStepTypeId.END_CLEARING_HOUSE, + A?>._, + A?>._)) + .ReturnsLazily((Guid id, + ApplicationChecklistEntryTypeId _, + IEnumerable _, + ProcessStepTypeId _, + IEnumerable? _, + IEnumerable? _) => new IApplicationChecklistService.ManualChecklistProcessStepData( + id, + new Process(Guid.NewGuid(), ProcessTypeId.APPLICATION_CHECKLIST, Guid.NewGuid()), + Guid.Empty, + ApplicationChecklistEntryTypeId.CLEARING_HOUSE, + ImmutableDictionary.Empty, + new List())); + } + #endregion } diff --git a/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs b/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs index fb3502cc37..6837e18a41 100644 --- a/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs +++ b/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs @@ -1,86 +1,110 @@ -/******************************************************************************** - * Copyright (c) 2022 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using FluentAssertions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Maintenance.App.Tests.Setup; -using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; -using Xunit.Extensions.AssemblyFixture; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Tests.Shared; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Maintenance.App.Tests; -/// -/// Tests the functionality of the -/// -public class BatchDeleteServiceTests : IAssemblyFixture +public class BatchDeleteServiceTests { - private readonly TestDbFixture _dbTestDbFixture; - private readonly IFixture _fixture; + private readonly IPortalRepositories _portalRepositories; + private readonly IDocumentRepository _documentRepository; + private readonly BatchDeleteService _sut; + private readonly IMockLogger _mockLogger; -#pragma warning disable xUnit1041 - public BatchDeleteServiceTests(TestDbFixture testDbFixture) -#pragma warning restore xUnit1041 + public BatchDeleteServiceTests() { - _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); - _fixture.Behaviors.OfType().ToList() - .ForEach(b => _fixture.Behaviors.Remove(b)); + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); - _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); - _dbTestDbFixture = testDbFixture; + _mockLogger = A.Fake>(); + ILogger logger = new MockLogger(_mockLogger); + + _portalRepositories = A.Fake(); + _documentRepository = A.Fake(); + + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_documentRepository); + + var dateTimeProvider = A.Fake(); + A.CallTo(() => dateTimeProvider.OffsetNow).Returns(DateTimeOffset.UtcNow); + + var options = Options.Create(new BatchDeleteServiceSettings { DeleteIntervalInDays = 5 }); + _sut = new BatchDeleteService(logger, options, _portalRepositories, dateTimeProvider); } [Fact] - public async Task ExecuteAsync_WithOldDocumentsAndAssigned_Removes() + public async Task CleanupDocuments_WithoutMatchingDocuments_DoesNothing() { // Arrange - var sut = await CreateSut(); + A.CallTo(() => _documentRepository.GetDocumentDataForCleanup(A._)) + .Returns([]); // Act - await sut.StartAsync(CancellationToken.None); + await _sut.CleanupDocuments(CancellationToken.None); // Assert - true.Should().BeTrue(); + A.CallTo(() => _portalRepositories.SaveAsync()) + .MustNotHaveHappened(); + A.CallTo(() => _portalRepositories.AttachRange(A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _documentRepository.RemoveDocuments(A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>._)) + .MustNotHaveHappened(); } - private async Task CreateSut() + [Fact] + public async Task CleanupDocuments_WithMatchingDocuments_RemovesExpected() { - var hostApplicationLifetime = _fixture.Create(); - var inMemorySettings = new[] - { - KeyValuePair.Create("DeleteIntervalInDays", "5") - }; - var config = new ConfigurationBuilder() - .AddInMemoryCollection(inMemorySettings) - .Build(); - var context = await _dbTestDbFixture.GetPortalDbContext(); - - var serviceProvider = _fixture.Create(); - A.CallTo(() => serviceProvider.GetService(typeof(PortalDbContext))).Returns(context); - var serviceScope = _fixture.Create(); - A.CallTo(() => serviceScope.ServiceProvider).Returns(serviceProvider); - var serviceScopeFactory = _fixture.Create(); - A.CallTo(() => serviceScopeFactory.CreateScope()).Returns(serviceScope); - - return new BatchDeleteService(hostApplicationLifetime, serviceScopeFactory, _fixture.Create>(), config); + // Arrange + var documentId = Guid.NewGuid(); + var agreementId1 = Guid.NewGuid(); + var agreementId2 = Guid.NewGuid(); + var offerId1 = Guid.NewGuid(); + var offerId2 = Guid.NewGuid(); + A.CallTo(() => _documentRepository.GetDocumentDataForCleanup(A._)) + .Returns([new(documentId, new[] { agreementId1, agreementId2 }, new[] { offerId1, offerId2 })]); + + // Act + await _sut.CleanupDocuments(CancellationToken.None); + + // Assert + A.CallTo(() => _portalRepositories.SaveAsync()) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _portalRepositories.AttachRange(A>.That.Matches(x => x.Count(a => a.Id == agreementId1) == 1 && x.Count(a => a.Id == agreementId2) == 1))) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _documentRepository.RemoveDocuments(A>.That.Matches(x => x.Single() == documentId))) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>.That.Matches(x => x.Count(a => a.OfferId == offerId1 && a.DocumentId == documentId) == 1 && x.Count(a => a.OfferId == offerId2 && a.DocumentId == documentId) == 1))) + .MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task CleanupDocuments_WithException_LogsException() + { + // Arrange + A.CallTo(() => _documentRepository.GetDocumentDataForCleanup(A._)) + .Throws(new DataMisalignedException("Test message")); + + // Act + await _sut.CleanupDocuments(CancellationToken.None); + + // Assert + A.CallTo(() => _portalRepositories.SaveAsync()) + .MustNotHaveHappened(); + A.CallTo(() => _portalRepositories.AttachRange(A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _documentRepository.RemoveDocuments(A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>._)) + .MustNotHaveHappened(); + A.CallTo(() => _mockLogger.Log(A.That.IsEqualTo(LogLevel.Error), A._, A._)).MustHaveHappenedOnceExactly(); } } diff --git a/tests/maintenance/Maintenance.App.Tests/Maintenance.App.Tests.csproj b/tests/maintenance/Maintenance.App.Tests/Maintenance.App.Tests.csproj index d877c564ba..b5be8c3945 100644 --- a/tests/maintenance/Maintenance.App.Tests/Maintenance.App.Tests.csproj +++ b/tests/maintenance/Maintenance.App.Tests/Maintenance.App.Tests.csproj @@ -21,6 +21,7 @@ Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Maintenance.App.Tests + Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Maintenance.App.Tests net8.0 enable enable @@ -54,4 +55,12 @@ + + + true + PreserveNewest + PreserveNewest + + + diff --git a/tests/maintenance/Maintenance.App.Tests/MaintenanceServiceTests.cs b/tests/maintenance/Maintenance.App.Tests/MaintenanceServiceTests.cs new file mode 100644 index 0000000000..3265313e87 --- /dev/null +++ b/tests/maintenance/Maintenance.App.Tests/MaintenanceServiceTests.cs @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2022 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.Extensions.DependencyInjection; +using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; +using Org.Eclipse.TractusX.Portal.Backend.Processes.ProcessIdentity; + +namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.Maintenance.App.Tests; + +public class MaintenanceServiceTests +{ + private readonly IBatchDeleteService _batchDeleteService; + private readonly IClearinghouseBusinessLogic _clearinghouseBusinessLogic; + private readonly MaintenanceService _service; + private readonly IProcessIdentityDataDetermination _processIdentityDataDetermination; + + public MaintenanceServiceTests() + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + + _batchDeleteService = A.Fake(); + _clearinghouseBusinessLogic = A.Fake(); + _processIdentityDataDetermination = A.Fake(); + + var serviceProvider = A.Fake(); + A.CallTo(() => serviceProvider.GetService(typeof(IBatchDeleteService))).Returns(_batchDeleteService); + A.CallTo(() => serviceProvider.GetService(typeof(IClearinghouseBusinessLogic))).Returns(_clearinghouseBusinessLogic); + A.CallTo(() => serviceProvider.GetService(typeof(IProcessIdentityDataDetermination))).Returns(_processIdentityDataDetermination); + var serviceScope = A.Fake(); + A.CallTo(() => serviceScope.ServiceProvider).Returns(serviceProvider); + var serviceScopeFactory = A.Fake(); + A.CallTo(() => serviceScopeFactory.CreateScope()).Returns(serviceScope); + A.CallTo(() => serviceProvider.GetService(typeof(IServiceScopeFactory))).Returns(serviceScopeFactory); + + _service = new MaintenanceService(serviceScopeFactory); + } + + [Fact] + public async Task ExecuteAsync_CallsExpectedServices() + { + // Act + await _service.ExecuteAsync(CancellationToken.None); + + // Assert + A.CallTo(() => _processIdentityDataDetermination.GetIdentityData()) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _batchDeleteService.CleanupDocuments(A._)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _clearinghouseBusinessLogic.CheckEndClearinghouseProcesses(A._)) + .MustHaveHappenedOnceExactly(); + } +} diff --git a/tests/maintenance/Maintenance.App.Tests/appsettings.IntegrationTests.json b/tests/maintenance/Maintenance.App.Tests/appsettings.IntegrationTests.json new file mode 100644 index 0000000000..5af5539af9 --- /dev/null +++ b/tests/maintenance/Maintenance.App.Tests/appsettings.IntegrationTests.json @@ -0,0 +1,44 @@ +{ + "Serilog": { + "Using": [ "Serilog.Sinks.Console" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Information", + "Microsoft.Hosting.Lifetime": "Information", + "Org.Eclipse.TractusX.Portal.Backend": "Information" + } + }, + "WriteTo": [ + { "Name": "Console" } + ], + "Enrich": [ + "FromLogContext" + ], + "Properties": { + "Application": "Org.Eclipse.TractusX.Portal.Backend.Maintenance.App" + } + }, + "ConnectionStrings": { + "PortalDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=placeholder;Ssl Mode=Disable;" + }, + "BatchDelete": { + "DeleteIntervalInDays": 5 + }, + "Clearinghouse": { + "Username": "empty", + "Password": "empty", + "ClientId": "empty", + "GrantType": "empty", + "ClientSecret": "empty", + "Scope": "openid", + "TokenAddress": "https://example.org/", + "BaseAddress": "https://validation.dev.dih-cloud.com/api/v1", + "UseDimWallet": false, + "RetriggerEndClearinghouseIntervalInDays": 30 + }, + "ProcessIdentity": { + "ProcessUserId": "" + } +} From 8cafd57f655284250eedd5e509265728dc063565 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Fri, 13 Sep 2024 08:14:34 +0200 Subject: [PATCH 2/5] chore: add file header Refs: #810 --- ...ompanyApplicationStatusFilterExtensions.cs | 19 +++++++++++++++++++ .../ClearinghouseBusinessLogic.cs | 19 ++++++++----------- .../BatchDeleteServiceSettings.cs | 19 +++++++++++++++++++ .../MaintenanceServiceExtensions.cs | 19 +++++++++++++++++++ 4 files changed, 65 insertions(+), 11 deletions(-) diff --git a/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs index 0c20d93009..1220e5b3b3 100644 --- a/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs +++ b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs @@ -1,3 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs index 157d534101..d9ee9b1115 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs @@ -143,21 +143,18 @@ public async Task CheckEndClearinghouseProcesses(CancellationToken stoppingToken { var applicationIds = await portalRepositories.GetInstance() .GetApplicationsForClearinghouseRetrigger(dateTimeProvider.OffsetNow.AddDays(-_settings.RetriggerEndClearinghouseIntervalInDays)) - .ToListAsync(stoppingToken).ConfigureAwait(false); + .ToListAsync(stoppingToken) + .ConfigureAwait(false); var hasChanges = false; - foreach (var applicationId in applicationIds) + foreach (var context in applicationIds.Select(applicationId => checklistService.VerifyChecklistEntryAndProcessSteps( + applicationId, + ApplicationChecklistEntryTypeId.CLEARING_HOUSE, + [ApplicationChecklistEntryStatusId.IN_PROGRESS], + ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE))) { - var context = await checklistService - .VerifyChecklistEntryAndProcessSteps( - applicationId, - ApplicationChecklistEntryTypeId.CLEARING_HOUSE, - [ApplicationChecklistEntryStatusId.IN_PROGRESS], - ProcessStepTypeId.END_CLEARING_HOUSE) - .ConfigureAwait(ConfigureAwaitOptions.None); - checklistService.FinalizeChecklistEntryAndProcessSteps( - context, + await context.ConfigureAwait(ConfigureAwaitOptions.None), null, item => { diff --git a/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs b/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs index 7fca4fa931..cf61c33569 100644 --- a/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs +++ b/src/maintenance/Maintenance.App/DependencyInjection/BatchDeleteServiceSettings.cs @@ -1,3 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; using System.ComponentModel.DataAnnotations; diff --git a/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs b/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs index 3e0e3ad8fe..8255c03321 100644 --- a/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs +++ b/src/maintenance/Maintenance.App/DependencyInjection/MaintenanceServiceExtensions.cs @@ -1,3 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; using Org.Eclipse.TractusX.Portal.Backend.Maintenance.App.Services; From 915c520f8e4827f53a3838debddfb5da35772b93 Mon Sep 17 00:00:00 2001 From: Norbert Truchsess Date: Mon, 16 Sep 2024 21:58:26 +0200 Subject: [PATCH 3/5] fix compile-errors after rebase and review findings --- .../RegistrationBusinessLogic.cs | 24 +--- ...ompanyApplicationStatusFilterExtensions.cs | 16 +-- .../ClearinghouseBusinessLogic.cs | 30 ++--- .../IClearinghouseBusinessLogic.cs | 3 +- .../Clearinghouse.Library.csproj | 1 + .../Framework.Async/Directory.Build.props | 2 +- .../ToAsyncEnumerableExtensions.cs | 34 ++++++ .../Framework.Cors/Directory.Build.props | 2 +- .../Framework.DBAccess/Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Directory.Build.props | 2 +- .../Framework.IO/Directory.Build.props | 2 +- .../Framework.Linq/Directory.Build.props | 2 +- .../Framework.Logging/Directory.Build.props | 2 +- .../Framework.Models/Directory.Build.props | 2 +- .../Framework.Seeding/Directory.Build.props | 2 +- .../Framework.Swagger/Directory.Build.props | 2 +- .../Framework.Token/Directory.Build.props | 2 +- .../Framework.Web/Directory.Build.props | 2 +- .../Services/BatchDeleteService.cs | 20 +++- .../Repositories/AgreementRepository.cs | 13 ++ .../ApplicationChecklistRepository.cs | 3 +- .../Repositories/DocumentRepository.cs | 11 +- .../Repositories/IAgreementRepository.cs | 3 + .../Repositories/IDocumentRepository.cs | 8 +- .../ClearinghouseBusinessLogicTests.cs | 112 +++++++++--------- .../BatchDeleteServiceTests.cs | 47 ++++++-- 31 files changed, 209 insertions(+), 150 deletions(-) create mode 100644 src/framework/Framework.Async/ToAsyncEnumerableExtensions.cs diff --git a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs index e29195c0a9..3db4a91b2d 100644 --- a/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/RegistrationBusinessLogic.cs @@ -20,6 +20,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Extensions; using Org.Eclipse.TractusX.Portal.Backend.Administration.Service.Models; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.BusinessLogic; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; @@ -130,7 +131,7 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu var applications = portalRepositories.GetInstance() .GetCompanyApplicationsFilteredQuery( companyName?.Length >= 3 ? companyName : null, - GetCompanyApplicationStatusIds(companyApplicationStatusFilter)); + companyApplicationStatusFilter.GetCompanyApplicationStatusIds()); return Pagination.CreateResponseAsync( page, @@ -170,7 +171,7 @@ private async Task GetCompanyWithAddressAsyncInternal(Gu var applicationsQuery = portalRepositories.GetInstance() .GetExternalCompanyApplicationsFilteredQuery(_identityData.CompanyId, companyName?.Length >= 3 ? companyName : null, externalId, - GetCompanyApplicationStatusIds(companyApplicationStatusFilter)); + companyApplicationStatusFilter.GetCompanyApplicationStatusIds()); var orderedQuery = dateCreatedOrderFilter == null || dateCreatedOrderFilter.Value == DateCreatedOrderFilter.DESC ? applicationsQuery.AsSplitQuery().OrderByDescending(application => application.DateCreated) @@ -606,25 +607,6 @@ private void PostRegistrationCancelEmailAsync(ICollection emailData, } } - private static IEnumerable GetCompanyApplicationStatusIds(CompanyApplicationStatusFilter? companyApplicationStatusFilter) - { - switch (companyApplicationStatusFilter) - { - case CompanyApplicationStatusFilter.Closed: - { - return [CompanyApplicationStatusId.CONFIRMED, CompanyApplicationStatusId.DECLINED]; - } - case CompanyApplicationStatusFilter.InReview: - { - return [CompanyApplicationStatusId.SUBMITTED]; - } - default: - { - return [CompanyApplicationStatusId.SUBMITTED, CompanyApplicationStatusId.CONFIRMED, CompanyApplicationStatusId.DECLINED]; - } - } - } - /// public async Task<(string fileName, byte[] content, string contentType)> GetDocumentAsync(Guid documentId) { diff --git a/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs index 1220e5b3b3..d405603f75 100644 --- a/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs +++ b/src/administration/Administration.Service/Extensions/CompanyApplicationStatusFilterExtensions.cs @@ -27,14 +27,16 @@ public static class CompanyApplicationStatusFilterExtensions public static IEnumerable GetCompanyApplicationStatusIds(this CompanyApplicationStatusFilter? companyApplicationStatusFilter) => companyApplicationStatusFilter switch { - CompanyApplicationStatusFilter.Closed => - [ - CompanyApplicationStatusId.CONFIRMED, CompanyApplicationStatusId.DECLINED + CompanyApplicationStatusFilter.Closed => [ + CompanyApplicationStatusId.CONFIRMED, + CompanyApplicationStatusId.DECLINED + ], + CompanyApplicationStatusFilter.InReview => [ + CompanyApplicationStatusId.SUBMITTED ], - CompanyApplicationStatusFilter.InReview => [CompanyApplicationStatusId.SUBMITTED], - _ => - [ - CompanyApplicationStatusId.SUBMITTED, CompanyApplicationStatusId.CONFIRMED, + _ => [ + CompanyApplicationStatusId.SUBMITTED, + CompanyApplicationStatusId.CONFIRMED, CompanyApplicationStatusId.DECLINED ] }; diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs index d9ee9b1115..ccffde8768 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/ClearinghouseBusinessLogic.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Clearinghouse.Library.Models; using Org.Eclipse.TractusX.Portal.Backend.Custodian.Library.BusinessLogic; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Async; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess; @@ -139,22 +140,27 @@ public async Task ProcessEndClearinghouse(Guid applicationId, ClearinghouseRespo : [ProcessStepTypeId.START_SELF_DESCRIPTION_LP]); } - public async Task CheckEndClearinghouseProcesses(CancellationToken stoppingToken) + public async Task CheckEndClearinghouseProcesses(CancellationToken cancellationToken) { var applicationIds = await portalRepositories.GetInstance() .GetApplicationsForClearinghouseRetrigger(dateTimeProvider.OffsetNow.AddDays(-_settings.RetriggerEndClearinghouseIntervalInDays)) - .ToListAsync(stoppingToken) + .ToListAsync(cancellationToken) .ConfigureAwait(false); - var hasChanges = false; - foreach (var context in applicationIds.Select(applicationId => checklistService.VerifyChecklistEntryAndProcessSteps( - applicationId, - ApplicationChecklistEntryTypeId.CLEARING_HOUSE, - [ApplicationChecklistEntryStatusId.IN_PROGRESS], - ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE))) + if (applicationIds.Count == 0) + return; + + await foreach (var context in applicationIds + .Select(applicationId => + checklistService.VerifyChecklistEntryAndProcessSteps( + applicationId, + ApplicationChecklistEntryTypeId.CLEARING_HOUSE, + [ApplicationChecklistEntryStatusId.IN_PROGRESS], + ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE)) + .TasksToAsyncEnumerable().WithCancellation(cancellationToken)) { checklistService.FinalizeChecklistEntryAndProcessSteps( - await context.ConfigureAwait(ConfigureAwaitOptions.None), + context, null, item => { @@ -162,12 +168,8 @@ await context.ConfigureAwait(ConfigureAwaitOptions.None), item.Comment = "Reset to retrigger clearinghouse"; }, [ProcessStepTypeId.START_OVERRIDE_CLEARING_HOUSE]); - hasChanges = true; } - if (hasChanges) - { - await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); - } + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); } } diff --git a/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs b/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs index bc880a38fa..d3a087e6d2 100644 --- a/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs +++ b/src/externalsystems/Clearinghouse.Library/BusinessLogic/IClearinghouseBusinessLogic.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2022 Microsoft and BMW Group AG * Copyright (c) 2022 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -27,5 +26,5 @@ public interface IClearinghouseBusinessLogic { Task HandleClearinghouse(IApplicationChecklistService.WorkerChecklistProcessStepData context, CancellationToken cancellationToken); Task ProcessEndClearinghouse(Guid applicationId, ClearinghouseResponseData data, CancellationToken cancellationToken); - Task CheckEndClearinghouseProcesses(CancellationToken stoppingToken); + Task CheckEndClearinghouseProcesses(CancellationToken cancellationToken); } diff --git a/src/externalsystems/Clearinghouse.Library/Clearinghouse.Library.csproj b/src/externalsystems/Clearinghouse.Library/Clearinghouse.Library.csproj index f2b188c869..c29ec0ea95 100644 --- a/src/externalsystems/Clearinghouse.Library/Clearinghouse.Library.csproj +++ b/src/externalsystems/Clearinghouse.Library/Clearinghouse.Library.csproj @@ -31,6 +31,7 @@ + diff --git a/src/framework/Framework.Async/Directory.Build.props b/src/framework/Framework.Async/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Async/Directory.Build.props +++ b/src/framework/Framework.Async/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Async/ToAsyncEnumerableExtensions.cs b/src/framework/Framework.Async/ToAsyncEnumerableExtensions.cs new file mode 100644 index 0000000000..1a83290abf --- /dev/null +++ b/src/framework/Framework.Async/ToAsyncEnumerableExtensions.cs @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using System.Runtime.CompilerServices; + +namespace Org.Eclipse.TractusX.Portal.Backend.Framework.Async; + +public static class ToAsyncEnumerableExtensions +{ + public static async IAsyncEnumerable TasksToAsyncEnumerable(this IEnumerable> tasks, [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + foreach (var task in tasks) + { + cancellationToken.ThrowIfCancellationRequested(); + yield return await task.ConfigureAwait(ConfigureAwaitOptions.None); + } + } +} diff --git a/src/framework/Framework.Cors/Directory.Build.props b/src/framework/Framework.Cors/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Cors/Directory.Build.props +++ b/src/framework/Framework.Cors/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.DBAccess/Directory.Build.props b/src/framework/Framework.DBAccess/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.DBAccess/Directory.Build.props +++ b/src/framework/Framework.DBAccess/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.DateTimeProvider/Directory.Build.props b/src/framework/Framework.DateTimeProvider/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.DateTimeProvider/Directory.Build.props +++ b/src/framework/Framework.DateTimeProvider/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.DependencyInjection/Directory.Build.props b/src/framework/Framework.DependencyInjection/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.DependencyInjection/Directory.Build.props +++ b/src/framework/Framework.DependencyInjection/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props b/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling.Controller/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.ErrorHandling.Web/Directory.Build.props b/src/framework/Framework.ErrorHandling.Web/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.ErrorHandling.Web/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling.Web/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.ErrorHandling/Directory.Build.props b/src/framework/Framework.ErrorHandling/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.ErrorHandling/Directory.Build.props +++ b/src/framework/Framework.ErrorHandling/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.HttpClientExtensions/Directory.Build.props b/src/framework/Framework.HttpClientExtensions/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.HttpClientExtensions/Directory.Build.props +++ b/src/framework/Framework.HttpClientExtensions/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.IO/Directory.Build.props b/src/framework/Framework.IO/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.IO/Directory.Build.props +++ b/src/framework/Framework.IO/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Linq/Directory.Build.props b/src/framework/Framework.Linq/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Linq/Directory.Build.props +++ b/src/framework/Framework.Linq/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Logging/Directory.Build.props b/src/framework/Framework.Logging/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Logging/Directory.Build.props +++ b/src/framework/Framework.Logging/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Models/Directory.Build.props b/src/framework/Framework.Models/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Models/Directory.Build.props +++ b/src/framework/Framework.Models/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Seeding/Directory.Build.props b/src/framework/Framework.Seeding/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Seeding/Directory.Build.props +++ b/src/framework/Framework.Seeding/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Swagger/Directory.Build.props b/src/framework/Framework.Swagger/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Swagger/Directory.Build.props +++ b/src/framework/Framework.Swagger/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Token/Directory.Build.props b/src/framework/Framework.Token/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Token/Directory.Build.props +++ b/src/framework/Framework.Token/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/framework/Framework.Web/Directory.Build.props b/src/framework/Framework.Web/Directory.Build.props index 0f881739fd..e909f97822 100644 --- a/src/framework/Framework.Web/Directory.Build.props +++ b/src/framework/Framework.Web/Directory.Build.props @@ -19,7 +19,7 @@ - 2.7.0 + 2.8.0 diff --git a/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs index 97895877e8..5b946ae958 100644 --- a/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs +++ b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs @@ -42,9 +42,11 @@ public async Task CleanupDocuments(CancellationToken cancellationToken) { logger.LogInformation("Getting documents and assignments older {Days} days", _settings.DeleteIntervalInDays); var documentRepository = portalRepositories.GetInstance(); + var documentData = await documentRepository .GetDocumentDataForCleanup(dateTimeProvider.OffsetNow.AddDays(-_settings.DeleteIntervalInDays)) - .ConfigureAwait(ConfigureAwaitOptions.None); + .ToListAsync(cancellationToken) + .ConfigureAwait(false); if (documentData.Count == 0) { logger.LogInformation("No documents to cleanup"); @@ -53,11 +55,19 @@ public async Task CleanupDocuments(CancellationToken cancellationToken) logger.LogInformation("Cleaning up {DocumentCount} Documents and {OfferIdCount} OfferAssignedDocuments", documentData.Count, documentData.SelectMany(x => x.OfferIds).Count()); - var agreementsToDeleteDocumentId = documentData.SelectMany(data => data.AgreementIds.Select(agreementId => new Agreement(agreementId, default, null!, default, default, default) { DocumentId = data.DocumentId })).ToList(); - portalRepositories.AttachRange(agreementsToDeleteDocumentId); - agreementsToDeleteDocumentId.ForEach(agreement => agreement.DocumentId = null); - documentRepository.RemoveOfferAssignedDocuments(documentData.SelectMany(data => data.OfferIds.Select(offerId => new OfferAssignedDocument(offerId, data.DocumentId)))); + portalRepositories.GetInstance().AttachAndModifyAgreements( + documentData.SelectMany(data => data.AgreementIds.Select?, Action)>(agreementId => ( + agreementId, + agreement => agreement.DocumentId = data.DocumentId, + agreement => agreement.DocumentId = null)))); + + portalRepositories.GetInstance().RemoveOfferAssignedDocuments( + documentData.SelectMany(data => data.OfferIds.Select(offerId => ( + offerId, + data.DocumentId)))); + documentRepository.RemoveDocuments(documentData.Select(x => x.DocumentId)); + await portalRepositories.SaveAsync().ConfigureAwait(ConfigureAwaitOptions.None); logger.LogInformation("Documents older than {Days} days and depending consents successfully cleaned up", _settings.DeleteIntervalInDays); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/AgreementRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/AgreementRepository.cs index 2351c602ce..5923459b3a 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/AgreementRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/AgreementRepository.cs @@ -21,6 +21,7 @@ using Microsoft.EntityFrameworkCore; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -129,4 +130,16 @@ public IAsyncEnumerable GetAgreementIdsForOfferAsync(Guid o .Where(assigned => assigned.OfferId == offerId) .Select(assigned => new AgreementStatusData(assigned.AgreementId, assigned.Agreement!.AgreementStatusId)) .AsAsyncEnumerable(); + + public void AttachAndModifyAgreements(IEnumerable<(Guid AgreementId, Action? Initialize, Action Modify)> agreementModificationIds) + { + var items = agreementModificationIds.Select(agreementModificationId => + { + var agreement = new Agreement(agreementModificationId.AgreementId, default, null!, default, default, default); + agreementModificationId.Initialize?.Invoke(agreement); + return (Agreement: agreement, agreementModificationId.Modify); + }).ToList(); + _context.AttachRange(items.Select(item => item.Agreement)); + items.ForEach(item => item.Modify(item.Agreement)); + } } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs index 51fe492b00..c14797340d 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ApplicationChecklistRepository.cs @@ -1,5 +1,4 @@ /******************************************************************************** - * Copyright (c) 2022 BMW Group AG * Copyright (c) 2022 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional @@ -106,7 +105,7 @@ public IAsyncEnumerable GetApplicationsForClearinghouseRetrigger(DateTimeO ce.DateCreated < dateCreatedThreshold) && ca.ChecklistProcess!.ProcessTypeId == ProcessTypeId.APPLICATION_CHECKLIST && ca.ChecklistProcess!.ProcessSteps.Any(ps => - ps.ProcessStepTypeId == ProcessStepTypeId.END_CLEARING_HOUSE && + ps.ProcessStepTypeId == ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE && ps.ProcessStepStatusId == ProcessStepStatusId.TODO && ps.DateCreated < dateCreatedThreshold)) .Select(x => x.Id) diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs index e6a7e9a861..1fc4debca4 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs @@ -231,17 +231,16 @@ public void AttachAndModifyDocuments(IEnumerable<(Guid DocumentId, Action documentIds) => dbContext.Documents.RemoveRange(documentIds.Select(documentId => new Document(documentId, null!, null!, null!, default, default, default, default))); - public void RemoveOfferAssignedDocuments(IEnumerable offerAssignedDocuments) => - dbContext.OfferAssignedDocuments.RemoveRange(offerAssignedDocuments); - public Task<(byte[] Content, string FileName, bool IsDocumentTypeMatch, MediaTypeId MediaTypeId)> GetDocumentAsync(Guid documentId, IEnumerable documentTypeIds) => dbContext.Documents .Where(x => x.Id == documentId) .Select(x => new ValueTuple(x.DocumentContent, x.DocumentName, documentTypeIds.Contains(x.DocumentTypeId), x.MediaTypeId)) .SingleOrDefaultAsync(); - public Task AgreementIds, IEnumerable OfferIds)>> GetDocumentDataForCleanup(DateTimeOffset dateCreated) => - dbContext.Documents.Where(x => + public IAsyncEnumerable<(Guid DocumentId, IEnumerable AgreementIds, IEnumerable OfferIds)> GetDocumentDataForCleanup(DateTimeOffset dateCreated) => + dbContext.Documents + .AsSplitQuery() + .Where(x => x.DateCreated < dateCreated && !x.Companies.Any() && x.Connector == null && @@ -252,5 +251,5 @@ public void RemoveOfferAssignedDocuments(IEnumerable offe doc.Agreements.Select(x => x.Id), doc.Offers.Select(x => x.Id) )) - .ToListAsync(); + .AsAsyncEnumerable(); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IAgreementRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IAgreementRepository.cs index a039d5759b..bbf608cafc 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IAgreementRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IAgreementRepository.cs @@ -19,6 +19,7 @@ ********************************************************************************/ using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Models; +using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Entities; using Org.Eclipse.TractusX.Portal.Backend.PortalBackend.PortalEntities.Enums; namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; @@ -92,4 +93,6 @@ public interface IAgreementRepository /// Id of the offer the agreement must be associated with /// IAsyncEnumerable GetAgreementIdsForOfferAsync(Guid offerId); + + void AttachAndModifyAgreements(IEnumerable<(Guid AgreementId, Action? Initialize, Action Modify)> agreementModificationIds); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs index e3e2c84485..9337874f11 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IDocumentRepository.cs @@ -146,12 +146,6 @@ public interface IDocumentRepository /// void RemoveDocuments(IEnumerable documentIds); - /// - /// Delete List Of Document - /// - /// - void RemoveOfferAssignedDocuments(IEnumerable offerAssignedDocuments); - /// /// Gets the registration document with the given id /// @@ -160,5 +154,5 @@ public interface IDocumentRepository /// Task<(byte[] Content, string FileName, bool IsDocumentTypeMatch, MediaTypeId MediaTypeId)> GetDocumentAsync(Guid documentId, IEnumerable documentTypeIds); - Task AgreementIds, IEnumerable OfferIds)>> GetDocumentDataForCleanup(DateTimeOffset dateCreated); + IAsyncEnumerable<(Guid DocumentId, IEnumerable AgreementIds, IEnumerable OfferIds)> GetDocumentDataForCleanup(DateTimeOffset dateCreated); } diff --git a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs index 3a03967099..081feb5958 100644 --- a/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs +++ b/tests/externalsystems/Clearinghouse.Library.Tests/ClearinghouseBusinessLogicTests.cs @@ -120,14 +120,14 @@ public async Task HandleStartClearingHouse_WithNotExistingApplication_ThrowsConf { // Arrange var applicationId = Guid.NewGuid(); - var checklist = new Dictionary - { - { ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE }, - { ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE }, - { ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE }, - { ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO } - } - .ToImmutableDictionary(); + + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(applicationId, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); SetupForHandleStartClearingHouse(); @@ -143,14 +143,13 @@ public async Task HandleStartClearingHouse_WithNotExistingApplication_ThrowsConf public async Task HandleStartClearingHouse_WithCreatedApplication_ThrowsConflictException() { // Arrange - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO} - } - .ToImmutableDictionary(); + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithApplicationCreated, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); SetupForHandleStartClearingHouse(); @@ -166,14 +165,13 @@ public async Task HandleStartClearingHouse_WithCreatedApplication_ThrowsConflict public async Task HandleStartClearingHouse_WithBpnNull_ThrowsConflictException() { // Arrange - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO} - } - .ToImmutableDictionary(); + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithoutBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); SetupForHandleStartClearingHouse(); @@ -192,14 +190,14 @@ public async Task HandleStartClearingHouse_WithValidData_CallsExpected(ProcessSt { // Arrange var entry = new ApplicationChecklistEntry(Guid.NewGuid(), ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO} - } - .ToImmutableDictionary(); + + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, stepTypeId, checklist, Enumerable.Empty()); SetupForHandleStartClearingHouse(); @@ -222,14 +220,13 @@ public async Task HandleStartClearingHouse_WithValidData_CallsExpected(ProcessSt public async Task HandleStartClearingHouse_WithDimActiveAndNonExistingApplication_ThrowsConflictException() { // Arrange - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO} - } - .ToImmutableDictionary(); + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns<(bool, string?)>(default); @@ -253,14 +250,13 @@ public async Task HandleStartClearingHouse_WithDimActiveAndNonExistingApplicatio public async Task HandleStartClearingHouse_WithDimActiveAndDidNotSet_ThrowsConflictException() { // Arrange - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO} - } - .ToImmutableDictionary(); + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns((true, null)); @@ -285,14 +281,14 @@ public async Task HandleStartClearingHouse_WithDimActive_CallsExpected() { // Arrange var entry = new ApplicationChecklistEntry(Guid.NewGuid(), ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO, DateTimeOffset.UtcNow); - var checklist = new Dictionary - { - {ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE}, - {ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO}, - } - .ToImmutableDictionary(); + + var checklist = ImmutableDictionary.CreateRange([ + new(ApplicationChecklistEntryTypeId.REGISTRATION_VERIFICATION, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.BUSINESS_PARTNER_NUMBER, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.IDENTITY_WALLET, ApplicationChecklistEntryStatusId.DONE), + new(ApplicationChecklistEntryTypeId.CLEARING_HOUSE, ApplicationChecklistEntryStatusId.TO_DO) + ]); + var context = new IApplicationChecklistService.WorkerChecklistProcessStepData(IdWithBpn, ProcessStepTypeId.START_CLEARING_HOUSE, checklist, Enumerable.Empty()); A.CallTo(() => _applicationRepository.GetDidForApplicationId(A._)) .Returns((true, "did:web:test123456")); @@ -466,7 +462,7 @@ private void SetupForCheckEndClearinghouseProcesses(IEnumerable._, ApplicationChecklistEntryTypeId.CLEARING_HOUSE, A>._, - ProcessStepTypeId.END_CLEARING_HOUSE, + ProcessStepTypeId.AWAIT_CLEARING_HOUSE_RESPONSE, A?>._, A?>._)) .ReturnsLazily((Guid id, @@ -480,7 +476,7 @@ private void SetupForCheckEndClearinghouseProcesses(IEnumerable.Empty, - new List())); + [])); } #endregion diff --git a/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs b/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs index 6837e18a41..2e6ac4faa9 100644 --- a/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs +++ b/tests/maintenance/Maintenance.App.Tests/BatchDeleteServiceTests.cs @@ -1,3 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; @@ -14,6 +33,8 @@ public class BatchDeleteServiceTests { private readonly IPortalRepositories _portalRepositories; private readonly IDocumentRepository _documentRepository; + private readonly IAgreementRepository _agreementRepository; + private readonly IOfferRepository _offerRepository; private readonly BatchDeleteService _sut; private readonly IMockLogger _mockLogger; @@ -29,8 +50,12 @@ public BatchDeleteServiceTests() _portalRepositories = A.Fake(); _documentRepository = A.Fake(); + _agreementRepository = A.Fake(); + _offerRepository = A.Fake(); A.CallTo(() => _portalRepositories.GetInstance()).Returns(_documentRepository); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_agreementRepository); + A.CallTo(() => _portalRepositories.GetInstance()).Returns(_offerRepository); var dateTimeProvider = A.Fake(); A.CallTo(() => dateTimeProvider.OffsetNow).Returns(DateTimeOffset.UtcNow); @@ -44,19 +69,19 @@ public async Task CleanupDocuments_WithoutMatchingDocuments_DoesNothing() { // Arrange A.CallTo(() => _documentRepository.GetDocumentDataForCleanup(A._)) - .Returns([]); + .Returns(Array.Empty<(Guid, IEnumerable, IEnumerable)>().ToAsyncEnumerable()); // Act await _sut.CleanupDocuments(CancellationToken.None); // Assert - A.CallTo(() => _portalRepositories.SaveAsync()) + A.CallTo(() => _agreementRepository.AttachAndModifyAgreements(A?, Action)>>._)) .MustNotHaveHappened(); - A.CallTo(() => _portalRepositories.AttachRange(A>._)) + A.CallTo(() => _offerRepository.RemoveOfferAssignedDocuments(A>._)) .MustNotHaveHappened(); A.CallTo(() => _documentRepository.RemoveDocuments(A>._)) .MustNotHaveHappened(); - A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>._)) + A.CallTo(() => _portalRepositories.SaveAsync()) .MustNotHaveHappened(); } @@ -70,19 +95,19 @@ public async Task CleanupDocuments_WithMatchingDocuments_RemovesExpected() var offerId1 = Guid.NewGuid(); var offerId2 = Guid.NewGuid(); A.CallTo(() => _documentRepository.GetDocumentDataForCleanup(A._)) - .Returns([new(documentId, new[] { agreementId1, agreementId2 }, new[] { offerId1, offerId2 })]); + .Returns(new (Guid, IEnumerable, IEnumerable)[] { (documentId, [agreementId1, agreementId2], [offerId1, offerId2]) }.ToAsyncEnumerable()); // Act await _sut.CleanupDocuments(CancellationToken.None); // Assert - A.CallTo(() => _portalRepositories.SaveAsync()) + A.CallTo(() => _agreementRepository.AttachAndModifyAgreements(A?, Action)>>.That.Matches(x => x.Count(a => a.Id == agreementId1) == 1 && x.Count(a => a.Id == agreementId2) == 1))) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _portalRepositories.AttachRange(A>.That.Matches(x => x.Count(a => a.Id == agreementId1) == 1 && x.Count(a => a.Id == agreementId2) == 1))) + A.CallTo(() => _offerRepository.RemoveOfferAssignedDocuments(A>.That.Matches(x => x.Count(a => a.OfferId == offerId1 && a.DocumentId == documentId) == 1 && x.Count(a => a.OfferId == offerId2 && a.DocumentId == documentId) == 1))) .MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.RemoveDocuments(A>.That.Matches(x => x.Single() == documentId))) .MustHaveHappenedOnceExactly(); - A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>.That.Matches(x => x.Count(a => a.OfferId == offerId1 && a.DocumentId == documentId) == 1 && x.Count(a => a.OfferId == offerId2 && a.DocumentId == documentId) == 1))) + A.CallTo(() => _portalRepositories.SaveAsync()) .MustHaveHappenedOnceExactly(); } @@ -97,13 +122,13 @@ public async Task CleanupDocuments_WithException_LogsException() await _sut.CleanupDocuments(CancellationToken.None); // Assert - A.CallTo(() => _portalRepositories.SaveAsync()) + A.CallTo(() => _agreementRepository.AttachAndModifyAgreements(A?, Action)>>._)) .MustNotHaveHappened(); - A.CallTo(() => _portalRepositories.AttachRange(A>._)) + A.CallTo(() => _offerRepository.RemoveOfferAssignedDocuments(A>._)) .MustNotHaveHappened(); A.CallTo(() => _documentRepository.RemoveDocuments(A>._)) .MustNotHaveHappened(); - A.CallTo(() => _documentRepository.RemoveOfferAssignedDocuments(A>._)) + A.CallTo(() => _portalRepositories.SaveAsync()) .MustNotHaveHappened(); A.CallTo(() => _mockLogger.Log(A.That.IsEqualTo(LogLevel.Error), A._, A._)).MustHaveHappenedOnceExactly(); } From 4069141de07f840ca57ca442a04f35653041c18e Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 17 Sep 2024 11:55:59 +0200 Subject: [PATCH 4/5] chore: add documentation to repo Refs: #810 --- .../DependencyInjection/PartnerRegistrationSettings.cs | 2 -- .../PortalBackend.DBAccess/Repositories/DocumentRepository.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs b/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs index 5d113b8732..8b7cac7fbb 100644 --- a/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs +++ b/src/administration/Administration.Service/DependencyInjection/PartnerRegistrationSettings.cs @@ -28,8 +28,6 @@ public class PartnerRegistrationSettings [Required] [DistinctValues("x => x.ClientId")] public IEnumerable InitialRoles { get; set; } = null!; - - public int ApplicationsMaxPageSize { get; set; } } public static class PartnerRegistrationSettingsExtensions diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs index 1fc4debca4..be47ec475d 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/DocumentRepository.cs @@ -25,7 +25,9 @@ namespace Org.Eclipse.TractusX.Portal.Backend.PortalBackend.DBAccess.Repositories; +/// /// Implementation of accessing database with EF Core. +/// public class DocumentRepository(PortalDbContext dbContext) : IDocumentRepository { /// From 293e2d42e2eed455cedf9f709321715745af4d78 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Tue, 17 Sep 2024 11:59:05 +0200 Subject: [PATCH 5/5] fix: adjust error logging Refs: #810 --- src/maintenance/Maintenance.App/Services/BatchDeleteService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs index 5b946ae958..7297db6748 100644 --- a/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs +++ b/src/maintenance/Maintenance.App/Services/BatchDeleteService.cs @@ -73,7 +73,7 @@ public async Task CleanupDocuments(CancellationToken cancellationToken) } catch (Exception ex) { - logger.LogError("Database clean up failed with error: {Errors}", ex.Message); + logger.LogError(ex, "Database clean up failed with error: {Errors}", ex.Message); } } }