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)
{