diff --git a/BuildingBlocks/src/Tooling/Extensions/StringExtensions.cs b/BuildingBlocks/src/Tooling/Extensions/StringExtensions.cs index d4cd21137b..547082d1d8 100644 --- a/BuildingBlocks/src/Tooling/Extensions/StringExtensions.cs +++ b/BuildingBlocks/src/Tooling/Extensions/StringExtensions.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Text; using System.Text.RegularExpressions; namespace Backbone.Tooling.Extensions; @@ -20,4 +21,9 @@ public static bool MatchesRegex(this string text, string regexString) var regex = new Regex(regexString); return regex.IsMatch(text); } + + public static byte[] GetBytes(this string text) + { + return Encoding.UTF8.GetBytes(text); + } } diff --git a/ConsumerApi.Tests.Integration/ConsumerApi.Tests.Integration.csproj b/ConsumerApi.Tests.Integration/ConsumerApi.Tests.Integration.csproj index 6f6df531fd..6aee665f69 100644 --- a/ConsumerApi.Tests.Integration/ConsumerApi.Tests.Integration.csproj +++ b/ConsumerApi.Tests.Integration/ConsumerApi.Tests.Integration.csproj @@ -1,58 +1,58 @@ - - false - + + false + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + - - - - - - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - + + + + + - - - - - + + + Always + + + Always + + + Always + + - - - Always - - - Always - - - Always - - - - - - $(UsingMicrosoftNETSdk) - %(RelativeDir)%(Filename).feature$(DefaultLanguageSourceExtension) - - - $(UsingMicrosoftNETSdk) - %(RelativeDir)%(Filename).feature$(DefaultLanguageSourceExtension) - - + + + $(UsingMicrosoftNETSdk) + %(RelativeDir)%(Filename).feature$(DefaultLanguageSourceExtension) + + + $(UsingMicrosoftNETSdk) + %(RelativeDir)%(Filename).feature$(DefaultLanguageSourceExtension) + + diff --git a/ConsumerApi.Tests.Integration/Features/Relationships/POST.feature b/ConsumerApi.Tests.Integration/Features/Relationships/POST.feature new file mode 100644 index 0000000000..d6e4be9fde --- /dev/null +++ b/ConsumerApi.Tests.Integration/Features/Relationships/POST.feature @@ -0,0 +1,64 @@ +@Integration +Feature: POST Relationship + +User creates a Relationship + +Scenario: Creating a Relationship + Given Identities i1 and i2 + And a Relationship Template rt created by i2 + When a POST request is sent to the /Relationships endpoint by i1 with rt.id + Then the response status code is 201 (Created) + And the response contains a Relationship + +Scenario: Creating a Relationship to an Identity in status "ToBeDeleted" + Given Identities i1 and i2 + And a Relationship Template rt created by i2 + And i2 is in status "ToBeDeleted" + When a POST request is sent to the /Relationships endpoint by i1 with rt.id + Then the response status code is 400 (Bad Request) + And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + +Scenario: Accept Relationship Change + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i2 + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Accept endpoint by i1 + Then the response status code is 200 (OK) + And the response contains an AcceptRelationshipChangeResponse + +Scenario: Accept Relationship Change to an Identity in status "ToBeDeleted" + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i2 + And i2 is in status "ToBeDeleted" + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Accept endpoint by i1 + Then the response status code is 400 (Bad Request) + And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + +Scenario: Reject Relationship Change + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i2 + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Reject endpoint by i1 + Then the response status code is 200 (OK) + And the response contains an RejectRelationshipChangeResponse + +Scenario: Reject Relationship Change to an Identity in status "ToBeDeleted" + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i2 + And i2 is in status "ToBeDeleted" + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Reject endpoint by i1 + Then the response status code is 400 (Bad Request) + And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" + +Scenario: Revoke Relationship Change + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i1 + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Revoke endpoint by i1 + Then the response status code is 200 (OK) + And the response contains an RevokeRelationshipChangeResponse + +Scenario: Revoke Relationship Change to an Identity in status "ToBeDeleted" + Given Identities i1 and i2 + And a pending Relationship between i1 and i2 created by i1 + And i2 is in status "ToBeDeleted" + When a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Revoke endpoint by i1 + Then the response status code is 400 (Bad Request) + And the response content contains an error with the error code "error.platform.validation.relationship.peerIsToBeDeleted" diff --git a/ConsumerApi.Tests.Integration/Helpers/Utils.cs b/ConsumerApi.Tests.Integration/Helpers/Utils.cs index dc80a43546..b770c77784 100644 --- a/ConsumerApi.Tests.Integration/Helpers/Utils.cs +++ b/ConsumerApi.Tests.Integration/Helpers/Utils.cs @@ -2,7 +2,7 @@ using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Requests; using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Requests; using Backbone.ConsumerApi.Tests.Integration.Extensions; -using Backbone.Crypto; +using Backbone.Tooling.Extensions; namespace Backbone.ConsumerApi.Tests.Integration.Helpers; @@ -12,7 +12,7 @@ public static async Task EstablishRelationshipBetween(Client client1, Client cli { var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest { - Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + Content = "AAA".GetBytes() }; var relationshipTemplateResponse = await client1.RelationshipTemplates.CreateTemplate(createRelationshipTemplateRequest); @@ -21,7 +21,7 @@ public static async Task EstablishRelationshipBetween(Client client1, Client cli var createRelationshipRequest = new CreateRelationshipRequest { RelationshipTemplateId = relationshipTemplateResponse.Result!.Id, - Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + Content = "AAA".GetBytes() }; var createRelationshipResponse = await client2.Relationships.CreateRelationship(createRelationshipRequest); @@ -29,7 +29,7 @@ public static async Task EstablishRelationshipBetween(Client client1, Client cli var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest { - Content = ConvertibleString.FromUtf8("AAA").BytesRepresentation + Content = "AAA".GetBytes() }; var acceptRelationChangeResponse = await client1.Relationships.AcceptChange(createRelationshipResponse.Result!.Id, createRelationshipResponse.Result.Changes.First().Id, completeRelationshipChangeRequest); diff --git a/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs b/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs new file mode 100644 index 0000000000..820211b5f2 --- /dev/null +++ b/ConsumerApi.Tests.Integration/StepDefinitions/RelationshipsStepDefinitions.cs @@ -0,0 +1,213 @@ +using Backbone.BuildingBlocks.SDK.Endpoints.Common.Types; +using Backbone.ConsumerApi.Sdk; +using Backbone.ConsumerApi.Sdk.Authentication; +using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types; +using Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Requests; +using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Requests; +using Backbone.ConsumerApi.Sdk.Endpoints.RelationshipTemplates.Types.Responses; +using Backbone.ConsumerApi.Tests.Integration.Configuration; +using Backbone.ConsumerApi.Tests.Integration.Extensions; +using Backbone.ConsumerApi.Tests.Integration.Support; +using Backbone.Tooling.Extensions; +using Microsoft.Extensions.Options; + +namespace Backbone.ConsumerApi.Tests.Integration.StepDefinitions; + +[Binding] +[Scope(Feature = "POST Relationship")] +internal class RelationshipsStepDefinitions +{ + private Client _client1 = null!; + private Client _client2 = null!; + private readonly ClientCredentials _clientCredentials; + private readonly HttpClient _httpClient; + private ApiResponse? _relationshipTemplateResponse; + private ApiResponse? _createRelationshipResponse; + private ApiResponse? _acceptRelationshipChangeResponse; + private ApiResponse? _rejectRelationshipChangeResponse; + private ApiResponse? _revokeRelationshipChangeResponse; + private string _relationshipId = string.Empty; + private string _relationshipChangeId = string.Empty; + + public RelationshipsStepDefinitions(HttpClientFactory factory, IOptions httpConfiguration) + { + _httpClient = factory.CreateClient(); + _clientCredentials = new ClientCredentials(httpConfiguration.Value.ClientCredentials.ClientId, httpConfiguration.Value.ClientCredentials.ClientSecret); + } + + [Given("Identities i1 and i2")] + public async Task GivenIdentitiesI1AndI2() + { + _client1 = await Client.CreateForNewIdentity(_httpClient, _clientCredentials, Constants.DEVICE_PASSWORD); + _client2 = await Client.CreateForNewIdentity(_httpClient, _clientCredentials, Constants.DEVICE_PASSWORD); + } + + [Given("a Relationship Template rt created by i2")] + public async Task GivenARelationshipTemplateRtCreatedByI2() + { + _relationshipTemplateResponse = await CreateRelationshipTemplate(_client2); + } + + [Given("a pending Relationship between i1 and i2 created by i1")] + public async Task GivenAPendingRelationshipBetweenI1AndI2CreatedByI2() + { + var relationshipTemplateResponse = await CreateRelationshipTemplate(_client2); + var createRelationshipResponse = await CreateRelationship(_client1, relationshipTemplateResponse.Result!.Id); + + _relationshipId = createRelationshipResponse.Result!.Id; + _relationshipChangeId = createRelationshipResponse.Result!.Changes.First().Id; + } + + [Given("a pending Relationship between i1 and i2 created by i2")] + public async Task GivenAPendingRelationshipBetweenI1AndI2CreatedByI1() + { + var relationshipTemplateResponse = await CreateRelationshipTemplate(_client1); + var createRelationshipResponse = await CreateRelationship(_client2, relationshipTemplateResponse.Result!.Id); + + _relationshipId = createRelationshipResponse.Result!.Id; + _relationshipChangeId = createRelationshipResponse!.Result!.Changes.First().Id; + } + + [Given("i2 is in status \"ToBeDeleted\"")] + public async Task GivenIdentityI2IsToBeDeleted() + { + var startDeletionProcessResponse = await _client2.Identities.StartDeletionProcess(); + startDeletionProcessResponse.Should().BeASuccess(); + } + + [Given("i1 is in status \"ToBeDeleted\"")] + public async Task GivenIdentityI1IsToBeDeleted() + { + var startDeletionProcessResponse = await _client1.Identities.StartDeletionProcess(); + startDeletionProcessResponse.Should().BeASuccess(); + } + + [When("a POST request is sent to the /Relationships endpoint by i1 with rt.id")] + public async Task WhenAPostRequestIsSentToTheRelationshipsEndpointByI1With() + { + _createRelationshipResponse = await CreateRelationship(_client1, _relationshipTemplateResponse!.Result!.Id); + } + + [When("a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Accept endpoint by i1")] + public async Task WhenAPostRequestIsSentToTheAcceptRelationshipChangeEndpointByI1() + { + var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest + { + Content = "AAA".GetBytes() + }; + _acceptRelationshipChangeResponse = await _client1.Relationships.AcceptChange(_relationshipId, _relationshipChangeId, completeRelationshipChangeRequest); + } + + [When("a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Reject endpoint by i1")] + public async Task WhenAPostRequestIsSentToTheRejectRelationshipChangeEndpointByI1() + { + var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest + { + Content = "AAA".GetBytes() + }; + _rejectRelationshipChangeResponse = await _client1.Relationships.RejectChange(_relationshipId, _relationshipChangeId, completeRelationshipChangeRequest); + } + + [When("a POST request is sent to the /Relationships/{r.Id}/Changes/{r.Changes.Id}/Revoke endpoint by i1")] + public async Task WhenAPostRequestIsSentToTheRevokeRelationshipChangeEndpointByI2() + { + var completeRelationshipChangeRequest = new CompleteRelationshipChangeRequest + { + Content = "AAA".GetBytes() + }; + _revokeRelationshipChangeResponse = await _client1.Relationships.RevokeChange(_relationshipId, _relationshipChangeId, completeRelationshipChangeRequest); + } + + [Then(@"the response status code is (\d\d\d) \(.+\)")] + public void ThenTheResponseStatusCodeIs(int expectedStatusCode) + { + if (_createRelationshipResponse != null) + ((int)_createRelationshipResponse!.Status).Should().Be(expectedStatusCode); + + if (_acceptRelationshipChangeResponse != null) + ((int)_acceptRelationshipChangeResponse!.Status).Should().Be(expectedStatusCode); + + if (_rejectRelationshipChangeResponse != null) + ((int)_rejectRelationshipChangeResponse!.Status).Should().Be(expectedStatusCode); + + if (_revokeRelationshipChangeResponse != null) + ((int)_revokeRelationshipChangeResponse!.Status).Should().Be(expectedStatusCode); + } + + [Then(@"the response content contains an error with the error code ""([^""]*)""")] + public void ThenTheResponseContentIncludesAnErrorWithTheErrorCode(string errorCode) + { + if (_createRelationshipResponse != null) + { + _createRelationshipResponse!.Error.Should().NotBeNull(); + _createRelationshipResponse.Error!.Code.Should().Be(errorCode); + } + + if (_acceptRelationshipChangeResponse != null) + { + _acceptRelationshipChangeResponse!.Error.Should().NotBeNull(); + _acceptRelationshipChangeResponse.Error!.Code.Should().Be(errorCode); + } + + if (_rejectRelationshipChangeResponse != null) + { + _rejectRelationshipChangeResponse!.Error.Should().NotBeNull(); + _rejectRelationshipChangeResponse.Error!.Code.Should().Be(errorCode); + } + + if (_revokeRelationshipChangeResponse != null) + { + _revokeRelationshipChangeResponse!.Error.Should().NotBeNull(); + _revokeRelationshipChangeResponse.Error!.Code.Should().Be(errorCode); + } + } + + [Then("the response contains a Relationship")] + public void ThenTheResponseContainsARelationship() + { + _createRelationshipResponse!.Should().BeASuccess(); + _createRelationshipResponse!.Should().ComplyWithSchema(); + } + + [Then("the response contains an AcceptRelationshipChangeResponse")] + public void ThenTheResponseContainsAnAcceptRelationshipChangeResponse() + { + _acceptRelationshipChangeResponse!.Should().BeASuccess(); + _acceptRelationshipChangeResponse!.Should().ComplyWithSchema(); + } + + [Then("the response contains an RejectRelationshipChangeResponse")] + public void ThenTheResponseContainsAnRejectRelationshipChangeResponse() + { + _rejectRelationshipChangeResponse!.Should().BeASuccess(); + _rejectRelationshipChangeResponse!.Should().ComplyWithSchema(); + } + + [Then("the response contains an RevokeRelationshipChangeResponse")] + public void ThenTheResponseContainsAnRevokeRelationshipChangeResponse() + { + _revokeRelationshipChangeResponse!.Should().BeASuccess(); + _revokeRelationshipChangeResponse!.Should().ComplyWithSchema(); + } + + private async Task> CreateRelationshipTemplate(Client client) + { + var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest + { + Content = "AAA".GetBytes() + }; + + return await client.RelationshipTemplates.CreateTemplate(createRelationshipTemplateRequest); + } + + private async Task> CreateRelationship(Client client, string relationshipTemplateId) + { + var createRelationshipRequest = new CreateRelationshipRequest + { + RelationshipTemplateId = relationshipTemplateId, + Content = "AAA".GetBytes() + }; + + return await client.Relationships.CreateRelationship(createRelationshipRequest); + } +} diff --git a/Modules/Devices/src/Devices.Application/Devices.Application.csproj b/Modules/Devices/src/Devices.Application/Devices.Application.csproj index 4180f2f233..0c9f75521d 100644 --- a/Modules/Devices/src/Devices.Application/Devices.Application.csproj +++ b/Modules/Devices/src/Devices.Application/Devices.Application.csproj @@ -3,8 +3,8 @@ - - + + diff --git a/Modules/Relationships/src/Relationships.Application/ApplicationErrors.cs b/Modules/Relationships/src/Relationships.Application/ApplicationErrors.cs index 6aefb44e98..24df5098b0 100644 --- a/Modules/Relationships/src/Relationships.Application/ApplicationErrors.cs +++ b/Modules/Relationships/src/Relationships.Application/ApplicationErrors.cs @@ -6,6 +6,12 @@ public static class ApplicationErrors { public static class Relationship { + public static ApplicationError PeerIsToBeDeleted(string peerToBeDeleted) + { + return new ApplicationError("error.platform.validation.relationship.peerIsToBeDeleted", + $"Cannot establish relationship with '{peerToBeDeleted}' because they are in status 'ToBeDeleted'."); + } + public static ApplicationError RelationshipToTargetAlreadyExists(string targetIdentity = "") { var targetIdentityString = string.IsNullOrEmpty(targetIdentity) ? "the target identity" : targetIdentity; diff --git a/Modules/Relationships/src/Relationships.Application/Infrastructure/Persistence/Repository/IRelationshipsRepository.cs b/Modules/Relationships/src/Relationships.Application/Infrastructure/Persistence/Repository/IRelationshipsRepository.cs index 5f70913f22..bc3af0c907 100644 --- a/Modules/Relationships/src/Relationships.Application/Infrastructure/Persistence/Repository/IRelationshipsRepository.cs +++ b/Modules/Relationships/src/Relationships.Application/Infrastructure/Persistence/Repository/IRelationshipsRepository.cs @@ -15,6 +15,7 @@ Task> FindChangesWithIds(IEnumerable FindRelationship(RelationshipId id, IdentityAddress identityAddress, CancellationToken cancellationToken, bool track = false); + Task FindRelationshipPeer(RelationshipId id, IdentityAddress identityAddress, CancellationToken cancellationToken); Task FindRelationshipChange(RelationshipChangeId id, IdentityAddress identityAddress, CancellationToken cancellationToken, bool track = false); Task Add(Relationship relationship, CancellationToken cancellationToken); Task Update(Relationship relationship); diff --git a/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipQuery.cs b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipQuery.cs new file mode 100644 index 0000000000..d3efc214da --- /dev/null +++ b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipQuery.cs @@ -0,0 +1,9 @@ +using Backbone.Modules.Relationships.Domain.Ids; +using MediatR; + +namespace Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship; + +public class GetPeerOfActiveIdentityInRelationshipQuery : IRequest +{ + public required RelationshipId Id { get; set; } +} diff --git a/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipResponse.cs b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipResponse.cs new file mode 100644 index 0000000000..523cb7ece3 --- /dev/null +++ b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/GetPeerOfActiveIdentityInRelationshipResponse.cs @@ -0,0 +1,6 @@ +namespace Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship; + +public class GetPeerOfActiveIdentityInRelationshipResponse +{ + public required string IdentityAddress { get; set; } +} diff --git a/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/Handler.cs b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/Handler.cs new file mode 100644 index 0000000000..f8f2be38fa --- /dev/null +++ b/Modules/Relationships/src/Relationships.Application/Relationships/Queries/GetPeerOfActiveIdentityInRelationship/Handler.cs @@ -0,0 +1,23 @@ +using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.UserContext; +using Backbone.Modules.Relationships.Application.Infrastructure.Persistence.Repository; +using MediatR; + +namespace Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship; + +public class Handler : IRequestHandler +{ + private readonly IRelationshipsRepository _relationshipsRepository; + private readonly IUserContext _userContext; + + public Handler(IUserContext userContext, IRelationshipsRepository relationshipsRepository) + { + _relationshipsRepository = relationshipsRepository; + _userContext = userContext; + } + + public async Task Handle(GetPeerOfActiveIdentityInRelationshipQuery request, CancellationToken cancellationToken) + { + var peerIdentityAddress = await _relationshipsRepository.FindRelationshipPeer(request.Id, _userContext.GetAddress(), cancellationToken); + return new GetPeerOfActiveIdentityInRelationshipResponse { IdentityAddress = peerIdentityAddress }; + } +} diff --git a/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs b/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs index d69a8024e2..954d0d281d 100644 --- a/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs +++ b/Modules/Relationships/src/Relationships.ConsumerApi/Controllers/RelationshipsController.cs @@ -4,6 +4,8 @@ using Backbone.BuildingBlocks.Application.Abstractions.Exceptions; using Backbone.BuildingBlocks.Application.Pagination; using Backbone.DevelopmentKit.Identity.ValueObjects; +using Backbone.Modules.Devices.Application.Identities.Queries.GetIdentity; +using Backbone.Modules.Devices.Domain.Entities.Identities; using Backbone.Modules.Relationships.Application; using Backbone.Modules.Relationships.Application.Relationships.Commands.AcceptRelationshipChangeRequest; using Backbone.Modules.Relationships.Application.Relationships.Commands.CreateRelationship; @@ -11,9 +13,11 @@ using Backbone.Modules.Relationships.Application.Relationships.Commands.RevokeRelationshipChangeRequest; using Backbone.Modules.Relationships.Application.Relationships.DTOs; using Backbone.Modules.Relationships.Application.Relationships.Queries.GetChange; +using Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship; using Backbone.Modules.Relationships.Application.Relationships.Queries.GetRelationship; using Backbone.Modules.Relationships.Application.Relationships.Queries.ListChanges; using Backbone.Modules.Relationships.Application.Relationships.Queries.ListRelationships; +using Backbone.Modules.Relationships.Application.RelationshipTemplates.Queries.GetRelationshipTemplate; using Backbone.Modules.Relationships.Common; using Backbone.Modules.Relationships.Domain.Entities; using Backbone.Modules.Relationships.Domain.Ids; @@ -112,6 +116,9 @@ public async Task GetChangeById(RelationshipChangeId id, Cancella [ProducesResponseType(typeof(HttpResponseEnvelopeResult), StatusCodes.Status200OK)] public async Task CreateRelationship(CreateRelationshipCommand request, CancellationToken cancellationToken) { + var relationshipTemplate = await _mediator.Send(new GetRelationshipTemplateQuery { Id = request.RelationshipTemplateId }, cancellationToken); + await EnsurePeerIsNotToBeDeleted(relationshipTemplate.CreatedBy, cancellationToken); + var relationship = await _mediator.Send(request, cancellationToken); return Created(relationship); } @@ -121,6 +128,9 @@ public async Task CreateRelationship(CreateRelationshipCommand re public async Task AcceptRelationshipChange([FromRoute] RelationshipId relationshipId, [FromRoute] RelationshipChangeId changeId, CompleteRelationshipChangeRequest request, CancellationToken cancellationToken) { + var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = relationshipId }, cancellationToken); + await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); + var change = await _mediator.Send(new AcceptRelationshipChangeRequestCommand { Id = relationshipId, @@ -136,6 +146,9 @@ public async Task AcceptRelationshipChange([FromRoute] Relationsh public async Task RejectRelationshipChange([FromRoute] RelationshipId relationshipId, [FromRoute(Name = "changeId")] RelationshipChangeId changeId, CompleteRelationshipChangeRequest request, CancellationToken cancellationToken) { + var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = relationshipId }, cancellationToken); + await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); + var change = await _mediator.Send(new RejectRelationshipChangeRequestCommand { Id = relationshipId, @@ -151,6 +164,9 @@ public async Task RejectRelationshipChange([FromRoute] Relationsh public async Task RevokeRelationshipChange([FromRoute] RelationshipId relationshipId, [FromRoute(Name = "changeId")] RelationshipChangeId changeId, CompleteRelationshipChangeRequest request, CancellationToken cancellationToken) { + var peerOfActiveIdentityInRelationshipResponse = await _mediator.Send(new GetPeerOfActiveIdentityInRelationshipQuery { Id = relationshipId }, cancellationToken); + await EnsurePeerIsNotToBeDeleted(peerOfActiveIdentityInRelationshipResponse.IdentityAddress, cancellationToken); + var change = await _mediator.Send(new RevokeRelationshipChangeRequestCommand { Id = relationshipId, @@ -160,6 +176,13 @@ public async Task RevokeRelationshipChange([FromRoute] Relationsh return Ok(change); } + + private async Task EnsurePeerIsNotToBeDeleted(string peerIdentityAddress, CancellationToken cancellationToken) + { + var peerIdentity = await _mediator.Send(new GetIdentityQuery(peerIdentityAddress), cancellationToken); + if (peerIdentity.Status is IdentityStatus.ToBeDeleted) + throw new ApplicationException(ApplicationErrors.Relationship.PeerIsToBeDeleted(peerIdentity.Address)); + } } public class CreateRelationshipChangeRequest diff --git a/Modules/Relationships/src/Relationships.Infrastructure/Persistence/Database/Repository/RelationshipsRepository.cs b/Modules/Relationships/src/Relationships.Infrastructure/Persistence/Database/Repository/RelationshipsRepository.cs index 22565afdc3..12c33443ab 100644 --- a/Modules/Relationships/src/Relationships.Infrastructure/Persistence/Database/Repository/RelationshipsRepository.cs +++ b/Modules/Relationships/src/Relationships.Infrastructure/Persistence/Database/Repository/RelationshipsRepository.cs @@ -67,6 +67,15 @@ public async Task> FindChangesWithIds(IEn return changes; } + public async Task FindRelationshipPeer(RelationshipId id, IdentityAddress identityAddress, CancellationToken cancellationToken) + { + var relationship = await _readOnlyRelationships + .WithParticipant(identityAddress) + .FirstWithId(id, cancellationToken); + + return relationship.To != identityAddress ? relationship.To : relationship.From; + } + public async Task FindRelationship(RelationshipId id, IdentityAddress identityAddress, CancellationToken cancellationToken, bool track = false) {