From 277fc1d799df1d0fba14a6053ea824960dbfca39 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Thu, 3 Aug 2023 15:41:40 +0200 Subject: [PATCH] fix(connector): fix delete connector remove assigned offersubscriptions from connector when deleting the connector Refs: CPLP-3105 --- .../BusinessLogic/ConnectorsBusinessLogic.cs | 26 ++++++++---- .../Repositories/ConnectorsRepository.cs | 11 +++-- .../Repositories/IConnectorsRepository.cs | 4 +- .../ConnectorsBusinessLogicTests.cs | 42 +++++++++++++++---- .../ConnectorRepositoryTests.cs | 23 ++++++++++ 5 files changed, 86 insertions(+), 20 deletions(-) diff --git a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs index a975bedaaf..4c3ed29a6e 100644 --- a/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs +++ b/src/administration/Administration.Service/BusinessLogic/ConnectorsBusinessLogic.cs @@ -272,7 +272,7 @@ public async Task DeleteConnectorAsync(Guid connectorId) { var companyId = _identityService.IdentityData.CompanyId; var connectorsRepository = _portalRepositories.GetInstance(); - var (isValidConnectorId, isProvidingOrHostCompany, selfDescriptionDocumentId, documentStatusId, connectorStatus) = await connectorsRepository.GetConnectorDeleteDataAsync(connectorId, companyId).ConfigureAwait(false); + var (isValidConnectorId, isProvidingOrHostCompany, selfDescriptionDocumentId, documentStatusId, connectorStatus, assignedOfferSubscriptions) = await connectorsRepository.GetConnectorDeleteDataAsync(connectorId, companyId).ConfigureAwait(false); if (!isValidConnectorId) { @@ -287,26 +287,26 @@ public async Task DeleteConnectorAsync(Guid connectorId) switch (connectorStatus) { case ConnectorStatusId.PENDING when selfDescriptionDocumentId == null: - await DeleteConnectorWithoutDocuments(connectorId, connectorsRepository); + await DeleteConnectorWithoutDocuments(connectorId, assignedOfferSubscriptions, connectorsRepository); break; case ConnectorStatusId.PENDING: - await DeleteConnectorWithDocuments(connectorId, selfDescriptionDocumentId.Value, connectorsRepository); + await DeleteConnectorWithDocuments(connectorId, assignedOfferSubscriptions, selfDescriptionDocumentId.Value, connectorsRepository); break; case ConnectorStatusId.ACTIVE when selfDescriptionDocumentId != null && documentStatusId != null: - await DeleteConnector(connectorId, selfDescriptionDocumentId.Value, documentStatusId.Value, connectorsRepository); + await DeleteConnector(connectorId, assignedOfferSubscriptions, selfDescriptionDocumentId.Value, documentStatusId.Value, connectorsRepository); break; default: throw new ConflictException("Connector status does not match a deletion scenario. Deletion declined"); } } - private async Task DeleteConnector(Guid connectorId, Guid selfDescriptionDocumentId, DocumentStatusId documentStatus, IConnectorsRepository connectorsRepository) + private async Task DeleteConnector(Guid connectorId, IEnumerable assignedOfferSubscriptions, Guid selfDescriptionDocumentId, DocumentStatusId documentStatus, IConnectorsRepository connectorsRepository) { _portalRepositories.GetInstance().AttachAndModifyDocument( selfDescriptionDocumentId, a => { a.DocumentStatusId = documentStatus; }, a => { a.DocumentStatusId = DocumentStatusId.INACTIVE; }); - + RemoveConnectorAssignedOfferSubscriptions(connectorId, assignedOfferSubscriptions, connectorsRepository); await DeleteUpdateConnectorDetail(connectorId, connectorsRepository); } @@ -320,19 +320,29 @@ private async Task DeleteUpdateConnectorDetail(Guid connectorId, IConnectorsRepo await _portalRepositories.SaveAsync(); } - private async Task DeleteConnectorWithDocuments(Guid connectorId, Guid selfDescriptionDocumentId, IConnectorsRepository connectorsRepository) + private async Task DeleteConnectorWithDocuments(Guid connectorId, IEnumerable assignedOfferSubscriptions, Guid selfDescriptionDocumentId, IConnectorsRepository connectorsRepository) { _portalRepositories.GetInstance().RemoveDocument(selfDescriptionDocumentId); + RemoveConnectorAssignedOfferSubscriptions(connectorId, assignedOfferSubscriptions, connectorsRepository); connectorsRepository.DeleteConnector(connectorId); await _portalRepositories.SaveAsync(); } - private async Task DeleteConnectorWithoutDocuments(Guid connectorId, IConnectorsRepository connectorsRepository) + private async Task DeleteConnectorWithoutDocuments(Guid connectorId, IEnumerable assignedOfferSubscriptions, IConnectorsRepository connectorsRepository) { + RemoveConnectorAssignedOfferSubscriptions(connectorId, assignedOfferSubscriptions, connectorsRepository); connectorsRepository.DeleteConnector(connectorId); await _portalRepositories.SaveAsync(); } + private static void RemoveConnectorAssignedOfferSubscriptions(Guid connectorId, IEnumerable assignedOfferSubscriptions, IConnectorsRepository connectorsRepository) + { + if (assignedOfferSubscriptions.Any()) + { + connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, assignedOfferSubscriptions); + } + } + /// public IAsyncEnumerable GetCompanyConnectorEndPointAsync(IEnumerable bpns) { diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs index 97e0b8b161..b37a5449fa 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/ConnectorsRepository.cs @@ -137,15 +137,16 @@ public Connector AttachAndModifyConnector(Guid connectorId, Action? i .SingleOrDefaultAsync(); /// - public Task<(bool IsValidConnectorId, bool IsProvidingOrHostCompany, Guid? SelfDescriptionDocumentId, DocumentStatusId? DocumentStatusId, ConnectorStatusId ConnectorStatus)> GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId) => + public Task<(bool IsValidConnectorId, bool IsProvidingOrHostCompany, Guid? SelfDescriptionDocumentId, DocumentStatusId? DocumentStatusId, ConnectorStatusId ConnectorStatus, IEnumerable AssignedOfferSubscriptions)> GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId) => _context.Connectors .Where(x => x.Id == connectorId) - .Select(connector => new ValueTuple( + .Select(connector => new ValueTuple>( true, connector.ProviderId == companyId || connector.HostId == companyId, connector.SelfDescriptionDocumentId, connector.SelfDescriptionDocument!.DocumentStatusId, - connector.StatusId + connector.StatusId, + connector.ConnectorAssignedOfferSubscriptions.Select(x => x.OfferSubscriptionId) )).SingleOrDefaultAsync(); /// @@ -167,4 +168,8 @@ public void DeleteConnector(Guid connectorId) => /// public ConnectorAssignedOfferSubscription CreateConnectorAssignedSubscriptions(Guid connectorId, Guid subscriptionId) => _context.ConnectorAssignedOfferSubscriptions.Add(new ConnectorAssignedOfferSubscription(connectorId, subscriptionId)).Entity; + + /// + public void DeleteConnectorAssignedSubscriptions(Guid connectorId, IEnumerable assignedOfferSubscriptions) => + _context.ConnectorAssignedOfferSubscriptions.RemoveRange(assignedOfferSubscriptions.Select(x => new ConnectorAssignedOfferSubscription(connectorId, x))); } diff --git a/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs b/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs index 7a0a94d29b..c797c6cdf2 100644 --- a/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs +++ b/src/portalbackend/PortalBackend.DBAccess/Repositories/IConnectorsRepository.cs @@ -87,7 +87,7 @@ public interface IConnectorsRepository /// Id of the connector /// Id of the company /// returns SelfDescriptionDocument Data/c> - Task<(bool IsValidConnectorId, bool IsProvidingOrHostCompany, Guid? SelfDescriptionDocumentId, DocumentStatusId? DocumentStatusId, ConnectorStatusId ConnectorStatus)> GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId); + Task<(bool IsValidConnectorId, bool IsProvidingOrHostCompany, Guid? SelfDescriptionDocumentId, DocumentStatusId? DocumentStatusId, ConnectorStatusId ConnectorStatus, IEnumerable AssignedOfferSubscriptions)> GetConnectorDeleteDataAsync(Guid connectorId, Guid companyId); /// /// Gets the data required for the connector update @@ -104,4 +104,6 @@ public interface IConnectorsRepository void DeleteConnector(Guid connectorId); ConnectorAssignedOfferSubscription CreateConnectorAssignedSubscriptions(Guid connectorId, Guid subscriptionId); + + void DeleteConnectorAssignedSubscriptions(Guid connectorId, IEnumerable assignedOfferSubscriptions); } diff --git a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs index 5f36df4783..49c2aec538 100644 --- a/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs +++ b/tests/administration/Administration.Service.Tests/BusinessLogic/ConnectorsBusinessLogicTests.cs @@ -478,8 +478,9 @@ public async Task DeleteConnectorAsync_WithDocumentId_ExpectedCalls() var connectorId = Guid.NewGuid(); var connector = new Connector(connectorId, null!, null!, null!); var selfDescriptionDocumentId = Guid.NewGuid(); + var assignedOfferSubscriptions = _fixture.CreateMany(2); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns((true, true, selfDescriptionDocumentId, documentStatusId, ConnectorStatusId.ACTIVE)); + .Returns((true, true, selfDescriptionDocumentId, documentStatusId, ConnectorStatusId.ACTIVE, assignedOfferSubscriptions)); A.CallTo(() => _documentRepository.AttachAndModifyDocument(A._, A>._, A>._)) .Invokes((Guid docId, Action? initialize, Action modify) @@ -504,6 +505,7 @@ public async Task DeleteConnectorAsync_WithDocumentId_ExpectedCalls() A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.AttachAndModifyDocument(selfDescriptionDocumentId, A>._, A>._)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.AttachAndModifyConnector(connectorId, A>._, A>._)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); } @@ -512,8 +514,9 @@ public async Task DeleteConnectorAsync_WithPendingAndWithoutDocumentId_ExpectedC { // Arrange var connectorId = Guid.NewGuid(); + var assignedOfferSubscriptions = _fixture.CreateMany(2); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns((true, true, null, null, ConnectorStatusId.PENDING)); + .Returns((true, true, null, null, ConnectorStatusId.PENDING, assignedOfferSubscriptions)); // Act await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); @@ -521,6 +524,7 @@ public async Task DeleteConnectorAsync_WithPendingAndWithoutDocumentId_ExpectedC // Assert A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); } @@ -531,8 +535,9 @@ public async Task DeleteConnectorAsync_WithPendingAndDocumentId_ExpectedCalls() const DocumentStatusId documentStatusId = DocumentStatusId.LOCKED; var connectorId = Guid.NewGuid(); var selfDescriptionDocumentId = Guid.NewGuid(); + var assignedOfferSubscriptions = _fixture.CreateMany(2); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) - .Returns((true, true, selfDescriptionDocumentId, documentStatusId, ConnectorStatusId.PENDING)); + .Returns((true, true, selfDescriptionDocumentId, documentStatusId, ConnectorStatusId.PENDING, assignedOfferSubscriptions)); // Act await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); @@ -541,6 +546,28 @@ public async Task DeleteConnectorAsync_WithPendingAndDocumentId_ExpectedCalls() A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _documentRepository.RemoveDocument(selfDescriptionDocumentId)).MustHaveHappenedOnceExactly(); A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>.That.Matches(x => x.Count() == 2))).MustHaveHappenedOnceExactly(); + A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); + } + + [Fact] + public async Task DeleteConnectorAsync_WithoutAssignedOfferSubscriptions_ExpectedCalls() + { + // Arrange + const DocumentStatusId documentStatusId = DocumentStatusId.LOCKED; + var connectorId = Guid.NewGuid(); + var selfDescriptionDocumentId = Guid.NewGuid(); + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(A._, _identity.CompanyId)) + .Returns((true, true, selfDescriptionDocumentId, documentStatusId, ConnectorStatusId.PENDING, Enumerable.Empty())); + + // Act + await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); + + // Assert + A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _documentRepository.RemoveDocument(selfDescriptionDocumentId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.DeleteConnector(connectorId)).MustHaveHappenedOnceExactly(); + A.CallTo(() => _connectorsRepository.DeleteConnectorAssignedSubscriptions(connectorId, A>._)).MustNotHaveHappened(); A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappenedOnceExactly(); } @@ -549,9 +576,8 @@ public async Task DeleteConnectorAsync_WithOutDocumentId_ExpectedCalls() { // Arrange var connectorId = Guid.NewGuid(); - var connector = new Connector(connectorId, null!, null!, null!); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns((true, true, null, null, ConnectorStatusId.ACTIVE)); + .Returns((true, true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty())); // Act async Task Act() => await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); @@ -567,7 +593,7 @@ public async Task DeleteConnectorAsync_WithInactiveConnector_ThrowsConflictExcep // Arrange var connectorId = Guid.NewGuid(); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns((true, true, null, null, ConnectorStatusId.ACTIVE)); + .Returns((true, true, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty())); // Act async Task Act() => await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); @@ -583,7 +609,7 @@ public async Task DeleteConnectorAsync_ThrowsNotFoundException() // Arrange var connectorId = Guid.NewGuid(); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns((false, false, null, null, ConnectorStatusId.ACTIVE)); + .Returns((false, false, null, null, ConnectorStatusId.ACTIVE, Enumerable.Empty())); // Act async Task Act() => await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); @@ -599,7 +625,7 @@ public async Task DeleteConnectorAsync_ThrowsForbiddenException() // Arrange var connectorId = Guid.NewGuid(); A.CallTo(() => _connectorsRepository.GetConnectorDeleteDataAsync(connectorId, _identity.CompanyId)) - .Returns((true, false, null, null, default)); + .Returns((true, false, null, null, default, Enumerable.Empty())); // Act async Task Act() => await _logic.DeleteConnectorAsync(connectorId).ConfigureAwait(false); diff --git a/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs b/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs index a6de158a78..5860d72910 100644 --- a/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs +++ b/tests/portalbackend/PortalBackend.DBAccess.Tests/ConnectorRepositoryTests.cs @@ -478,6 +478,29 @@ public async Task CreateConnectorAssignedSubscriptions_ExecutesExpected() #endregion + #region DeleteConnector + + [Fact] + public async Task DeleteConnectorAssignedSubscriptions_ExecutesExpected() + { + // Arrange + var (sut, context) = await CreateSut().ConfigureAwait(false); + + // Act + sut.DeleteConnectorAssignedSubscriptions(new Guid("7e86a0b8-6903-496b-96d1-0ef508206833"), Enumerable.Repeat(new Guid("0b2ca541-206d-48ad-bc02-fb61fbcb5552"), 1)); + + // Assert + var changeTracker = context.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().NotBeEmpty(); + changedEntries.Should().HaveCount(1); + var removedEntity = changedEntries.Single(); + removedEntity.State.Should().Be(EntityState.Deleted); + } + + #endregion + private async Task<(ConnectorsRepository, PortalDbContext)> CreateSut() { var context = await _dbTestDbFixture.GetPortalDbContext().ConfigureAwait(false);