Skip to content

Commit

Permalink
feat(delete): delete company certificates (#536)
Browse files Browse the repository at this point in the history
Refs: #467
Reviewed-by: Phil Schneider <[email protected]>
Co-authored-by: Phil Schneider <[email protected]>
  • Loading branch information
AnuragNagpure and Phil91 authored Mar 1, 2024
1 parent 05eb39b commit 4b103d6
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -606,4 +606,43 @@ public async IAsyncEnumerable<CompanyCertificateBpnData> GetCompanyCertificatesB
size,
_settings.MaxPageSize,
_portalRepositories.GetInstance<ICompanyCertificateRepository>().GetActiveCompanyCertificatePaginationSource(sorting, certificateStatus, certificateType, _identityData.CompanyId));

public async Task<int> DeleteCompanyCertificateAsync(Guid documentId)
{
var companyCertificateRepository = _portalRepositories.GetInstance<ICompanyCertificateRepository>();

var details = await companyCertificateRepository.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(documentId, _identityData.CompanyId).ConfigureAwait(false);

var certificateCount = details.CompanyCertificateId.Count();
if (certificateCount > 1)
{
throw new ConflictException($"There must not be multiple active certificates for document {documentId}");
}

if (details.DocumentId == Guid.Empty)
{
throw new NotFoundException("Document is not existing");
}

if (!details.IsSameCompany)
{
throw new ForbiddenException("User is not allowed to delete this document");
}

companyCertificateRepository.AttachAndModifyCompanyCertificateDocumentDetails(documentId, null, c =>
{
c.DocumentStatusId = DocumentStatusId.INACTIVE;
c.DateLastChanged = _dateTimeProvider.OffsetNow;
});

if (certificateCount == 1)
{
companyCertificateRepository.AttachAndModifyCompanyCertificateDetails(details.CompanyCertificateId.SingleOrDefault(), null, c =>
{
c.CompanyCertificateStatusId = CompanyCertificateStatusId.INACTVIE;
});
}

return await _portalRepositories.SaveAsync().ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,12 @@ public interface ICompanyDataBusinessLogic
Task RejectCredential(Guid credentialId);

IAsyncEnumerable<VerifiedCredentialTypeId> GetCertificateTypes();

IAsyncEnumerable<CompanyCertificateBpnData> GetCompanyCertificatesByBpn(string businessPartnerNumber);

Task CreateCompanyCertificate(CompanyCertificateCreationData data, CancellationToken cancellationToken);

Task<int> DeleteCompanyCertificateAsync(Guid documentId);

Task<Pagination.Response<CompanyCertificateData>> GetAllCompanyCertificatesAsync(int page, int size, CertificateSorting? sorting, CompanyCertificateStatusId? certificateStatus, CompanyCertificateTypeId? certificateType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,25 @@ public IAsyncEnumerable<CompanyCertificateBpnData> GetCompanyCertificatesByBpn(s
public Task<Pagination.Response<CompanyCertificateData>> GetAllCompanyCertificatesAsync([FromQuery] int page = 0, [FromQuery] int size = 15, [FromQuery] CertificateSorting? sorting = null, [FromQuery] CompanyCertificateStatusId? certificateStatus = null, [FromQuery] CompanyCertificateTypeId? certificateType = null) =>
_logic.GetAllCompanyCertificatesAsync(page, size, sorting, certificateStatus, certificateType);

/// <summary>
/// Deletes the company certificate with the given id
/// </summary>
/// <param name="documentId" example="4ad087bb-80a1-49d3-9ba9-da0b175cd4e3"></param>
/// <returns></returns>
/// <remarks>Example: Delete: /api/administration/companydata/companyCertificate/document/{documentId}</remarks>
/// <response code="200">Successfully deleted the company certificate</response>
/// <response code="400">Incorrect document state</response>
/// <response code="403">The user is not assigned with the Company.</response>
[HttpDelete]
[Authorize(Roles = "delete_certificates")]
[Authorize(Policy = PolicyTypes.ValidCompany)]
[Route("companyCertificate/document/{documentId}")]
[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status409Conflict)]
public Task<int> DeleteCompanyCertificate([FromRoute] Guid documentId) =>
_logic.DeleteCompanyCertificateAsync(documentId);

/// <summary>
/// Gets all outstanding, existing and inactive credentials
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,31 @@ public IAsyncEnumerable<CompanyCertificateBpnData> GetCompanyCertificateData(Gui
companyCertificate.ValidTill
))
.SingleOrDefaultAsync();

public Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, IEnumerable<Guid> CompanyCertificateId, bool IsSameCompany)> GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(Guid documentId, Guid companyId) =>
_context.Documents
.AsNoTracking()
.Where(x => x.Id == documentId)
.Select(document => new ValueTuple<Guid, DocumentStatusId, IEnumerable<Guid>, bool>(
document.Id,
document.DocumentStatusId,
document.CompanyCertificates.Where(x => x.CompanyCertificateStatusId != CompanyCertificateStatusId.INACTVIE).Select(x => x.Id),
document.CompanyUser!.Identity!.CompanyId == companyId))
.SingleOrDefaultAsync();

public void AttachAndModifyCompanyCertificateDetails(Guid id, Action<CompanyCertificate>? initialize, Action<CompanyCertificate> updateFields)
{
var entity = new CompanyCertificate(id, default, default, default, default, default);
initialize?.Invoke(entity);
_context.Attach(entity);
updateFields.Invoke(entity);
}

public void AttachAndModifyCompanyCertificateDocumentDetails(Guid id, Action<Document>? initialize, Action<Document> updateFields)
{
var entity = new Document(id, null!, null!, null!, default, default, default, default);
initialize?.Invoke(entity);
_context.Attach(entity);
updateFields.Invoke(entity);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,10 @@ public interface ICompanyCertificateRepository
/// </summary>
/// <returns>Returns an Pagination</returns>
Func<int, int, Task<Pagination.Source<CompanyCertificateData>?>> GetActiveCompanyCertificatePaginationSource(CertificateSorting? sorting, CompanyCertificateStatusId? certificateStatus, CompanyCertificateTypeId? certificateType, Guid companyId);

Task<(Guid DocumentId, DocumentStatusId DocumentStatusId, IEnumerable<Guid> CompanyCertificateId, bool IsSameCompany)> GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(Guid documentId, Guid companyId);

void AttachAndModifyCompanyCertificateDetails(Guid id, Action<CompanyCertificate>? initialize, Action<CompanyCertificate> updateFields);

void AttachAndModifyCompanyCertificateDocumentDetails(Guid id, Action<Document>? initialize, Action<Document> updateFields);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,76 @@ public async Task GetAllCompanyCertificatesAsync_WithSmallSize_GetsExpectedEntri

#endregion

#region DeleteCompanyCertificates

[Fact]
public async Task DeleteCompanyCertificateAsync_WithDocumentNotExisting_ThrowsNotFoundException()
{
// Arrange
//var sut = _fixture.Create<CompanyDataBusinessLogic>();
A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(Guid.NewGuid(), _identity.CompanyId))
.Returns((Guid.NewGuid(), DocumentStatusId.LOCKED, new[] { Guid.NewGuid() }.AsEnumerable(), false));

// Act
async Task Act() => await _sut.DeleteCompanyCertificateAsync(Guid.NewGuid()).ConfigureAwait(false);

// Assert
var ex = await Assert.ThrowsAsync<NotFoundException>(Act);
ex.Message.Should().Be($"Document is not existing");
}

[Fact]
public async Task DeleteCompanyCertificateAsync_WithDifferentCompanyIdNotExisting_ThrowsNotFoundException()
{
// Arrange
var documentId = Guid.NewGuid();
A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(documentId, _identity.CompanyId))
.Returns((documentId, DocumentStatusId.LOCKED, new[] { Guid.NewGuid() }.AsEnumerable(), false));

// Act
async Task Act() => await _sut.DeleteCompanyCertificateAsync(documentId).ConfigureAwait(false);

// Assert
var ex = await Assert.ThrowsAsync<ForbiddenException>(Act);
ex.Message.Should().Be($"User is not allowed to delete this document");
}

[Fact]
public async Task DeleteCompanyCertificateAsync_WithHavingMoreThanOneComapnyCertificate_ConflictException()
{
// Arrange
var documentId = new Guid("aaf53459-c36b-408e-a805-0b406ce9751f");
A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(documentId, _identity.CompanyId))
.Returns((documentId, DocumentStatusId.LOCKED, new[] { new Guid("9f5b9934-4014-4099-91e9-7b1aee696c10"), Guid.NewGuid() }.AsEnumerable(), true));

// Act
async Task Act() => await _sut.DeleteCompanyCertificateAsync(documentId).ConfigureAwait(false);

// Assert
var ex = await Assert.ThrowsAsync<ConflictException>(Act);
ex.Message.Should().Be($"There must not be multiple active certificates for document {documentId}");
}

[Fact]
public async Task DeleteCompanyCertificateAsync_WithExpectedResult()
{
//Arrange
var documentId = new Guid("aaf53459-c36b-408e-a805-0b406ce9751f");
A.CallTo(() => _companyCertificateRepository.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(documentId, _identity.CompanyId))
.Returns((documentId, DocumentStatusId.LOCKED, new[] { new Guid("9f5b9934-4014-4099-91e9-7b1aee696c10") }.AsEnumerable(), true));

//Act
await _sut.DeleteCompanyCertificateAsync(documentId).ConfigureAwait(false);

//Assert
A.CallTo(() => _companyCertificateRepository.AttachAndModifyCompanyCertificateDetails(A<Guid>._, null, A<Action<CompanyCertificate>>._)).MustHaveHappenedOnceExactly();
A.CallTo(() => _companyCertificateRepository.AttachAndModifyCompanyCertificateDocumentDetails(documentId, null, A<Action<Document>>._)).MustHaveHappenedOnceExactly();
A.CallTo(() => _portalRepositories.SaveAsync()).MustHaveHappened(1, Times.OrMore);

}

#endregion

#region Setup

private void SetupCreateUseCaseParticipation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,84 @@ public async Task GetCompanyCertificateData_NoResults_ReturnsExpected()

#endregion

#region DeleteCertificate

[Fact]
public async Task GetCompanyCertificateDocumentDetailsForIdUntrackedAsync_ReturnsExpectedResult()
{
// Arrange
var sut = await CreateSut().ConfigureAwait(false);

// Act
var companyCertificateDetail = await sut.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(new Guid("aaf53459-c36b-408e-a805-0b406ce9751e"), new Guid("41fd2ab8-71cd-4546-9bef-a388d91b2542")).ConfigureAwait(false);

// Assert
companyCertificateDetail.Should().NotBeNull();
companyCertificateDetail.IsSameCompany.Should().Be(true);
companyCertificateDetail.DocumentStatusId.Should().Be(DocumentStatusId.LOCKED);
}

[Fact]
public async Task GetCompanyCertificateDocumentDetailsForIdUntrackedAsync_ReturnsInvalidExpectedResult()
{
// Arrange
var sut = await CreateSut().ConfigureAwait(false);

// Act
var companyCertificateDetail = await sut.GetCompanyCertificateDocumentDetailsForIdUntrackedAsync(new Guid("aaf53459-c36b-408e-a805-0b406ce9751e"), new Guid("41fd2ab8-71cd-4546-9bef-a388d91b2544")).ConfigureAwait(false);

// Assert
companyCertificateDetail.Should().NotBeNull();
companyCertificateDetail.IsSameCompany.Should().Be(false);
}

[Fact]
public async Task CompanyCertificateDetailsModify_WithValidData_ReturnsExpected()
{
// Arrange
var now = DateTimeOffset.UtcNow;
var (sut, context) = await CreateSutWithContext();

// Act
sut.AttachAndModifyCompanyCertificateDetails(new("9f5b9934-4014-4099-91e9-7b1aee696c12"), null, x =>
{
x.CompanyCertificateStatusId = CompanyCertificateStatusId.INACTVIE;
});

// Assert
var changeTracker = context.ChangeTracker;
var changedEntries = changeTracker.Entries().ToList();
changeTracker.HasChanges().Should().BeTrue();
changedEntries.Should().ContainSingle()
.Which.Entity.Should().BeOfType<CompanyCertificate>()
.And.Match<CompanyCertificate>(x => x.CompanyCertificateStatusId == CompanyCertificateStatusId.INACTVIE);
}

[Fact]
public async Task CompanyCertificateDocumentDetailsModify_WithValidData_ReturnsExpected()
{
// Arrange
var now = DateTimeOffset.UtcNow;
var (sut, context) = await CreateSutWithContext();

// Act
sut.AttachAndModifyCompanyCertificateDocumentDetails(new("aaf53459-c36b-408e-a805-0b406ce9751e"), null, x =>
{
x.DocumentStatusId = DocumentStatusId.INACTIVE;
x.DateLastChanged = now;
});

// Assert
var changeTracker = context.ChangeTracker;
var changedEntries = changeTracker.Entries().ToList();
changeTracker.HasChanges().Should().BeTrue();
changedEntries.Should().ContainSingle()
.Which.Entity.Should().BeOfType<Document>()
.And.Match<Document>(x => x.DocumentStatusId == DocumentStatusId.INACTIVE);
}

#endregion

#region Setup

private async Task<CompanyCertificateRepository> CreateSut()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"company_certificate_type_id": 6,
"company_certificate_status_id": 1,
"company_id": "2dc4249f-b5ca-4d42-bef1-7a7a950a4f87",
"document_id": "9685f744-9d90-4102-a949-fcd0bb86f954"
"document_id": "aaf53459-c36b-408e-a805-0b406ce9751f"
},
{
"id": "9f5b9934-4014-4099-91e9-7b1aee696c11",
Expand Down
Loading

0 comments on commit 4b103d6

Please sign in to comment.