Skip to content

Commit

Permalink
feat(connector): add duplicate check to connector creation (#921)
Browse files Browse the repository at this point in the history
Refs: #917
Co-authored-by: Norbert Truchsess <norbert.truchsess@t-online.de>
Reviewed-by: Norbert Truchsess <norbert.truchsess@t-online.de>
  • Loading branch information
Phil91 and ntruchsess authored Sep 11, 2024
1 parent 48ea112 commit 256d7b4
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ private async Task<Guid> CreateConnectorInternalAsync(ConnectorInputModel connec
{
var companyId = _identityData.CompanyId;
var (name, connectorUrl, location, technicalUserId) = connectorInputModel;
await CheckDuplicateConnector(name, connectorUrl).ConfigureAwait(ConfigureAwaitOptions.None);
await CheckLocationExists(location);

var result = await portalRepositories
Expand Down Expand Up @@ -125,6 +126,7 @@ private async Task<Guid> CreateManagedConnectorInternalAsync(ManagedConnectorInp
{
var companyId = _identityData.CompanyId;
var (name, connectorUrl, location, subscriptionId, technicalUserId) = connectorInputModel;
await CheckDuplicateConnector(name, connectorUrl).ConfigureAwait(ConfigureAwaitOptions.None);
await CheckLocationExists(location).ConfigureAwait(ConfigureAwaitOptions.None);

var result = await portalRepositories.GetInstance<IOfferSubscriptionsRepository>()
Expand Down Expand Up @@ -178,6 +180,15 @@ private async Task CheckLocationExists(string location)
}
}

private async Task CheckDuplicateConnector(string name, string connectorUrl)
{
if (await portalRepositories.GetInstance<IConnectorsRepository>()
.CheckConnectorExists(name, connectorUrl).ConfigureAwait(ConfigureAwaitOptions.None))
{
throw ConflictException.Create(AdministrationConnectorErrors.CONNECTOR_DUPLICATE, [new("name", name), new("connectorUrl", connectorUrl)]);
}
}

private async Task ValidateTechnicalUser(Guid? technicalUserId, Guid companyId)
{
if (technicalUserId == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,25 @@ public class AdministrationConnectorErrorMessageContainer : IErrorMessageContain
{
private static readonly IReadOnlyDictionary<int, string> _messageContainer = new Dictionary<AdministrationConnectorErrors, string> {
{ AdministrationConnectorErrors.CONNECTOR_NOT_FOUND, "connector {connectorId} does not exist" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY,"company {companyId} is not provider of connector {connectorId}"},
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY,"company {companyId} is not provider of connector {connectorId}" },
{ AdministrationConnectorErrors.CONNECTOR_UNEXPECTED_NO_BPN_ASSIGNED, "provider company {companyId} has no businessPartnerNumber assigned" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_OFFERSUBSCRIPTION_EXIST,"OfferSubscription {subscriptionId} does not exist"},
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY_OFFER,"Company is not the provider of the offer"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_OFFERSUBSCRIPTION_LINKED,"OfferSubscription is already linked to a connector"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_STATUS_ACTIVE_OR_PENDING,"The offer subscription must be either {offerSubscriptionStatusIdActive} or {offerSubscriptionStatusIdPending}"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_NO_DESCRIPTION,"provider company {CompanyId} has no self description document"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_SET_BPN,"The bpn of company {companyId} must be set"},
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_LOCATION_NOT_EXIST,"Location {location} does not exist"},
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_TECH_USER_NOT_ACTIVE,"Technical User {technicalUserId} is not assigned to company {companyId} or is not active"},
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY_NOR_HOST,"company {companyId} is neither provider nor host-company of connector {connectorId}"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_DELETION_DECLINED,"Connector status does not match a deletion scenario. Deletion declined"},
{ AdministrationConnectorErrors.CONNECTOR_DELETION_FAILED_OFFER_SUBSCRIPTION,"Deletion Failed. Connector {connectorId} connected to an active offer subscription, {activeConnectorOfferSubscription}"},
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_INCORRECT_BPN,"Incorrect BPN {bpns} attribute value"},
{ AdministrationConnectorErrors.CONNECTOR_NOT_EXIST,"Connector {externalId} does not exist"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_ALREADY_ASSIGNED,"Connector {externalId} already has a document assigned"},
{ AdministrationConnectorErrors.CONNECTOR_NOT_HOST_COMPANY,"Company {companyId} is not the connectors host company"},
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_INACTIVE_STATE,"Connector {connectorId} is in state {connectorStatusId}"}
{ AdministrationConnectorErrors.CONNECTOR_NOT_OFFERSUBSCRIPTION_EXIST,"OfferSubscription {subscriptionId} does not exist" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY_OFFER,"Company is not the provider of the offer" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_OFFERSUBSCRIPTION_LINKED,"OfferSubscription is already linked to a connector" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_STATUS_ACTIVE_OR_PENDING,"The offer subscription must be either {offerSubscriptionStatusIdActive} or {offerSubscriptionStatusIdPending}" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_NO_DESCRIPTION,"provider company {CompanyId} has no self description document" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_SET_BPN,"The bpn of company {companyId} must be set" },
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_LOCATION_NOT_EXIST,"Location {location} does not exist" },
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_TECH_USER_NOT_ACTIVE,"Technical User {technicalUserId} is not assigned to company {companyId} or is not active" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_PROVIDER_COMPANY_NOR_HOST,"company {companyId} is neither provider nor host-company of connector {connectorId}" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_DELETION_DECLINED,"Connector status does not match a deletion scenario. Deletion declined" },
{ AdministrationConnectorErrors.CONNECTOR_DELETION_FAILED_OFFER_SUBSCRIPTION,"Deletion Failed. Connector {connectorId} connected to an active offer subscription, {activeConnectorOfferSubscription}" },
{ AdministrationConnectorErrors.CONNECTOR_ARGUMENT_INCORRECT_BPN,"Incorrect BPN {bpns} attribute value" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_EXIST,"Connector {externalId} does not exist" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_ALREADY_ASSIGNED,"Connector {externalId} already has a document assigned" },
{ AdministrationConnectorErrors.CONNECTOR_NOT_HOST_COMPANY,"Company {companyId} is not the connectors host company" },
{ AdministrationConnectorErrors.CONNECTOR_CONFLICT_INACTIVE_STATE,"Connector {connectorId} is in state {connectorStatusId}" },
{ AdministrationConnectorErrors.CONNECTOR_DUPLICATE,"Connector {name} does already exists for url {connectorUrl}" }
}.ToImmutableDictionary(x => (int)x.Key, x => x.Value);

public Type Type { get => typeof(AdministrationConnectorErrors); }
Expand All @@ -70,5 +71,6 @@ public enum AdministrationConnectorErrors
CONNECTOR_NOT_EXIST,
CONNECTOR_CONFLICT_ALREADY_ASSIGNED,
CONNECTOR_NOT_HOST_COMPANY,
CONNECTOR_CONFLICT_INACTIVE_STATE
CONNECTOR_CONFLICT_INACTIVE_STATE,
CONNECTOR_DUPLICATE
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,9 @@ public IAsyncEnumerable<Guid> GetConnectorIdsWithMissingSelfDescription() =>
.Where(c => c.SdCreationProcessId == processId)
.Select(c => new ValueTuple<Guid, string?, Guid>(c.Id, c.Provider!.BusinessPartnerNumber, c.Provider.SelfDescriptionDocumentId!.Value))
.SingleOrDefaultAsync();

public Task<bool> CheckConnectorExists(string name, string connectorUrl) =>
dbContext.Connectors.AnyAsync(x =>
x.Name == name &&
x.ConnectorUrl == connectorUrl);
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,5 @@ public interface IConnectorsRepository
Func<int, int, Task<Pagination.Source<ConnectorMissingSdDocumentData>?>> GetConnectorsWithMissingSdDocument();
IAsyncEnumerable<Guid> GetConnectorIdsWithMissingSelfDescription();
Task<(Guid Id, string? BusinessPartnerNumber, Guid SelfDescriptionDocumentId)> GetConnectorForProcessId(Guid processId);
Task<bool> CheckConnectorExists(string name, string connectorUrl);
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,25 @@ public async Task CreateConnectorAsync_WithValidInput_ReturnsCreatedConnectorDat
A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A<Guid>._, A<string>._, A<string>._, A<CancellationToken>._)).MustHaveHappened(clearingHouseDisabled ? 0 : 1, Times.Exactly);
}

[Fact]
public async Task CreateConnectorAsync_WithExistingConnector_ThrowsConflictException()
{
// Arrange
A.CallTo(() => _connectorsRepository.CheckConnectorExists(A<string>._, A<string>._))
.Returns(true);
var connectorInput = new ConnectorInputModel("connectorName", "https://test.de", "de", ServiceAccountUserId);
Task Act() => _logic.CreateConnectorAsync(connectorInput, CancellationToken.None);

// Act
var ex = await Assert.ThrowsAsync<ConflictException>(Act);

// Assert
ex.Message.Should().Be("CONNECTOR_DUPLICATE");
_connectors.Should().BeEmpty();
A.CallTo(() => _connectorsRepository.CreateConnectorAssignedSubscriptions(A<Guid>._, A<Guid>._)).MustNotHaveHappened();
A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A<Guid>._, A<string>._, A<string>._, A<CancellationToken>._)).MustNotHaveHappened();
}

[Fact]
public async Task CreateConnectorAsync_WithInvalidTechnicalUser_ThrowsControllerArgumentException()
{
Expand Down Expand Up @@ -383,6 +402,27 @@ public async Task CreateManagedConnectorAsync_WithTechnicalUser_ReturnsCreatedCo
A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A<Guid>._, A<string>._, A<string>._, A<CancellationToken>._)).MustHaveHappened(clearingHouseDisabled ? 0 : 1, Times.Exactly);
}

[Fact]
public async Task CreateManagedConnectorAsync_WithExistingConnector_ThrowsConflictException()
{
// Arrange
A.CallTo(() => _connectorsRepository.CheckConnectorExists(A<string>._, A<string>._))
.Returns(true);
var connectorInput = new ManagedConnectorInputModel("connectorName", "https://test.de", "de", _validOfferSubscriptionId, ServiceAccountUserId);
Task Act() => _logic.CreateManagedConnectorAsync(connectorInput, CancellationToken.None);

// Act
var ex = await Assert.ThrowsAsync<ConflictException>(Act);

// Assert
ex.Message.Should().Be("CONNECTOR_DUPLICATE");
_connectors.Should().BeEmpty();
A.CallTo(() => _connectorsRepository.CreateConnectorAssignedSubscriptions(A<Guid>._, _validOfferSubscriptionId))
.MustNotHaveHappened();
A.CallTo(() => _sdFactoryBusinessLogic.RegisterConnectorAsync(A<Guid>._, A<string>._, A<string>._, A<CancellationToken>._))
.MustNotHaveHappened();
}

[Fact]
public async Task CreateManagedConnectorAsync_WithInvalidLocation_ThrowsControllerArgumentException()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,36 @@ public async Task GetConnectorsWithMissingSelfDescription_ReturnsExpectedConnect

#endregion

#region CheckConnectorExists

[Fact]
public async Task CheckConnectorExists_WithExisting_ReturnsTrue()
{
// Arrange
var (sut, _) = await CreateSut();

// Act
var result = await sut.CheckConnectorExists("Test Connector 6", "www.google.de").ConfigureAwait(false);

// Assert
result.Should().BeTrue();
}

[Fact]
public async Task CheckConnectorExists_WithoutExisting_ReturnsFalse()
{
// Arrange
var (sut, _) = await CreateSut();

// Act
var result = await sut.CheckConnectorExists("not existing", "www.google.de").ConfigureAwait(false);

// Assert
result.Should().BeFalse();
}

#endregion

private async Task<(ConnectorsRepository, PortalDbContext)> CreateSut()
{
var context = await _dbTestDbFixture.GetPortalDbContext();
Expand Down

0 comments on commit 256d7b4

Please sign in to comment.