Skip to content

Commit

Permalink
Check whether a relationship can be established with a given identity (
Browse files Browse the repository at this point in the history
…#808)

* feat: Add /Relationships/CanCreate endpoint, query, response and handler

* test: Add handler tests

* chore: Add bruno file for /Relationships/CanCreate endpoint

* chore: Add validator for CanEstablishRelationshipQuery and use string instead of IdentityAddress

* chore: Remove unnecessary code

* chore: Add CanCreate endpoint to ConsumerApi SDK

* chore: Remove unnecessary handler tests

* chore: Add integration tests

* chore: Use helper method and modify Relationship.CountsAsActive method

* chore: Add validator for the identity address format

* chore: Remove unnecessary validator check

* chore: Remove now obsolete static responses

* chore: Use helper methods

* chore: Create new helper method for rejected relationships

* chore: Move and rename feature file

* chore: Extract common helper method code into own method

* chore: Don't use the null fallback

* chore: Remove unused method

* chore: Extract THEN method cluster into two methods

* chore: cleanup

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Timo Notheisen <[email protected]>
  • Loading branch information
3 people authored Aug 26, 2024
1 parent 86f46a8 commit 5a4d55e
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
meta {
name: /Relationships/CanCreate
type: http
seq: 4
}

get {
url: {{baseUrl}}/Relationships/CanCreate?peer={{PeerId}}
body: none
auth: inherit
}

params:query {
peer: {{PeerId}}
}

vars:pre-request {
PeerId: did:e:localhost:dids:8234cca0160ff05c785636
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@Integration
Feature: GET Relationships/CanCreate

Scenario: Two identities without a relationship can create one
Given Identities i1 and i2
When a GET request is sent to the /Relationships/CanCreate?peer={i.id} endpoint by i1 for i2
Then the response status code is 200 (OK)
And a relationship can be established

Scenario: Two identities with an active relationship can't create another one
Given Identities i1 and i2
And an active Relationship between i1 and i2 created by i1
When a GET request is sent to the /Relationships/CanCreate?peer={i.id} endpoint by i1 for i2
Then the response status code is 200 (OK)
And a relationship can not be established

Scenario: Two identities with a rejected relationship can create one
Given Identities i1 and i2
And a rejected Relationship between i1 and i2 created by i1
When a GET request is sent to the /Relationships/CanCreate?peer={i.id} endpoint by i1 for i2
Then the response status code is 200 (OK)
And a relationship can be established

Scenario: Two identities with a rejected and an active relationship can't create one
Given Identities i1 and i2
And a rejected Relationship between i1 and i2 created by i1
And an active Relationship between i1 and i2 created by i1
When a GET request is sent to the /Relationships/CanCreate?peer={i.id} endpoint by i1 for i2
Then the response status code is 200 (OK)
And a relationship can not be established

Scenario: Two identities with two rejected relationships can create one
Given Identities i1 and i2
And a rejected Relationship between i1 and i2 created by i1
And a rejected Relationship between i1 and i2 created by i1
When a GET request is sent to the /Relationships/CanCreate?peer={i.id} endpoint by i1 for i2
Then the response status code is 200 (OK)
And a relationship can be established
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Backbone.ConsumerApi.Tests.Integration.Helpers;

public static class Utils
{
public static async Task<Relationship> EstablishRelationshipBetween(Client client1, Client client2)
public static async Task<RelationshipMetadata> CreatePendingRelationshipBetween(Client client1, Client client2)
{
var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest
{
Expand All @@ -31,15 +31,40 @@ public static async Task<Relationship> EstablishRelationshipBetween(Client clien
var createRelationshipResponse = await client2.Relationships.CreateRelationship(createRelationshipRequest);
createRelationshipResponse.Should().BeASuccess();

return createRelationshipResponse.Result!;
}

public static async Task<Relationship> EstablishRelationshipBetween(Client client1, Client client2)
{
var relationshipMetadata = await CreatePendingRelationshipBetween(client1, client2);

var acceptRelationshipRequest = new AcceptRelationshipRequest
{
CreationResponseContent = "AAA".GetBytes()
};

var acceptRelationshipResponse = await client1.Relationships.AcceptRelationship(createRelationshipResponse.Result!.Id, acceptRelationshipRequest);
var acceptRelationshipResponse = await client1.Relationships.AcceptRelationship(relationshipMetadata.Id, acceptRelationshipRequest);
acceptRelationshipResponse.Should().BeASuccess();

var getRelationshipResponse = await client1.Relationships.GetRelationship(createRelationshipResponse.Result.Id);
var getRelationshipResponse = await client1.Relationships.GetRelationship(relationshipMetadata.Id);
getRelationshipResponse.Should().BeASuccess();

return getRelationshipResponse.Result!;
}

public static async Task<Relationship> CreateRejectedRelationshipBetween(Client client1, Client client2)
{
var relationshipMetadata = await CreatePendingRelationshipBetween(client1, client2);

var rejectRelationshipRequest = new RejectRelationshipRequest
{
CreationResponseContent = "AAA".GetBytes()
};

var rejectRelationshipResponse = await client1.Relationships.RejectRelationship(relationshipMetadata.Id, rejectRelationshipRequest);
rejectRelationshipResponse.Should().BeASuccess();

var getRelationshipResponse = await client1.Relationships.GetRelationship(relationshipMetadata.Id);
getRelationshipResponse.Should().BeASuccess();

return getRelationshipResponse.Result!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
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.Relationships.Types.Responses;
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.Helpers;
using Backbone.ConsumerApi.Tests.Integration.Support;
using Backbone.Tooling.Extensions;
using Microsoft.Extensions.Options;
Expand All @@ -15,6 +17,7 @@ namespace Backbone.ConsumerApi.Tests.Integration.StepDefinitions;

[Binding]
[Scope(Feature = "POST Relationship")]
[Scope(Feature = "GET Relationships/CanCreate")]
internal class RelationshipsStepDefinitions
{
private Client _client1 = null!;
Expand All @@ -26,6 +29,7 @@ internal class RelationshipsStepDefinitions
private ApiResponse<RelationshipMetadata>? _acceptRelationshipResponse;
private ApiResponse<RelationshipMetadata>? _rejectRelationshipResponse;
private ApiResponse<RelationshipMetadata>? _revokeRelationshipResponse;
private ApiResponse<CanEstablishRelationshipResponse>? _canEstablishResponse;
private string _relationshipId = string.Empty;

public RelationshipsStepDefinitions(HttpClientFactory factory, IOptions<HttpConfiguration> httpConfiguration)
Expand All @@ -34,6 +38,8 @@ public RelationshipsStepDefinitions(HttpClientFactory factory, IOptions<HttpConf
_clientCredentials = new ClientCredentials(httpConfiguration.Value.ClientCredentials.ClientId, httpConfiguration.Value.ClientCredentials.ClientSecret);
}

#region Given

[Given("Identities i1 and i2")]
public async Task GivenIdentitiesI1AndI2()
{
Expand Down Expand Up @@ -65,6 +71,18 @@ public async Task GivenAPendingRelationshipBetweenI1AndI2CreatedByI1()
_relationshipId = createRelationshipResponse.Result!.Id;
}

[Given("an active Relationship between i1 and i2 created by i1")]
public async Task GivenAnActiveRelationshipBetweenI1AndI2()
{
_relationshipId = (await Utils.EstablishRelationshipBetween(_client1, _client2)).Id;
}

[Given("a rejected Relationship between i1 and i2 created by i1")]
public async Task GivenARejectedRelationshipBetweenI1AndI2()
{
_relationshipId = (await Utils.CreateRejectedRelationshipBetween(_client1, _client2)).Id;
}

[Given("i2 is in status \"ToBeDeleted\"")]
public async Task GivenIdentityI2IsToBeDeleted()
{
Expand All @@ -79,6 +97,10 @@ public async Task GivenIdentityI1IsToBeDeleted()
startDeletionProcessResponse.Should().BeASuccess();
}

#endregion

#region When

[When("a POST request is sent to the /Relationships endpoint by i1 with rt.id")]
public async Task WhenAPostRequestIsSentToTheRelationshipsEndpointByI1With()
{
Expand Down Expand Up @@ -115,6 +137,16 @@ public async Task WhenAPostRequestIsSentToTheRevokeRelationshipEndpointByI2()
_revokeRelationshipResponse = await _client1.Relationships.RevokeRelationship(_relationshipId, revokeRelationshipRequest);
}

[When("a GET request is sent to the /Relationships/CanCreate\\?peer={i.id} endpoint by i1 for i2")]
public async Task WhenAGetRequestIsSentToTheCanCreateEndpointByI1()
{
_canEstablishResponse = await _client1.Relationships.CanCreateRelationship(_client2.IdentityData!.Address);
}

#endregion

#region Then

[Then(@"the response status code is (\d\d\d) \(.+\)")]
public void ThenTheResponseStatusCodeIs(int expectedStatusCode)
{
Expand Down Expand Up @@ -187,6 +219,22 @@ public async Task ThenTheResponseContainsARelationship()
}
}

[Then("a relationship can be established")]
public void ThenARelationshipCanBeEstablished()
{
if (_canEstablishResponse != null)
_canEstablishResponse.Result!.CanCreate.Should().BeTrue();
}

[Then("a relationship can not be established")]
public void ThenARelationshipCanNotBeEstablished()
{
if (_canEstablishResponse != null)
_canEstablishResponse.Result!.CanCreate.Should().BeFalse();
}

#endregion

private async Task<ApiResponse<CreateRelationshipTemplateResponse>> CreateRelationshipTemplate(Client client)
{
var createRelationshipTemplateRequest = new CreateRelationshipTemplateRequest
Expand Down
4 changes: 0 additions & 4 deletions Backbone.sln
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerformanceSnapshotCreator"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tokens.Domain.Tests", "Modules\Tokens\test\Tokens.Domain.Tests\Tokens.Domain.Tests.csproj", "{EDCB84BE-54C3-4CAD-977E-45EEBEFA1402}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AAE548AB-4843-476A-BF61-002CF6FEE713}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{824495A9-A255-487D-AF26-6F6CA92BC715}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Challenges.Application.Tests", "Modules\Challenges\test\Challenges.Application.Tests\Challenges.Application.Tests.csproj", "{EAA10D43-BD28-40D8-BE07-B8F6DE47C156}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Others", "Others", "{8B8EE965-0373-46D6-BB30-7C131EE524E4}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Backbone.DevelopmentKit.Identity.ValueObjects;
using MediatR;

namespace Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship;

public class CanEstablishRelationshipQuery : IRequest<CanEstablishRelationshipResponse>
{
public required string PeerAddress { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Backbone.BuildingBlocks.Application.FluentValidation;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using FluentValidation;

namespace Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship;

// ReSharper disable once UnusedType.Global
public class CanEstablishRelationshipQueryValidator : AbstractValidator<CanEstablishRelationshipQuery>
{
public CanEstablishRelationshipQueryValidator()
{
RuleFor(q => q.PeerAddress).Must(IdentityAddress.IsValid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship;

public class CanEstablishRelationshipResponse
{
public required bool CanCreate { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Backbone.BuildingBlocks.Application.Abstractions.Infrastructure.UserContext;
using Backbone.DevelopmentKit.Identity.ValueObjects;
using Backbone.Modules.Relationships.Application.Infrastructure.Persistence.Repository;
using MediatR;

namespace Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship;

public class Handler : IRequestHandler<CanEstablishRelationshipQuery, CanEstablishRelationshipResponse>
{
private readonly IRelationshipsRepository _relationshipsRepository;
private readonly IUserContext _userContext;

public Handler(IUserContext userContext, IRelationshipsRepository relationshipsRepository)
{
_relationshipsRepository = relationshipsRepository;
_userContext = userContext;
}

public async Task<CanEstablishRelationshipResponse> Handle(CanEstablishRelationshipQuery request, CancellationToken cancellationToken)
{
var hasActiveRelationship = await _relationshipsRepository.RelationshipBetweenTwoIdentitiesExists(_userContext.GetAddress(), IdentityAddress.Parse(request.PeerAddress), cancellationToken);

return new CanEstablishRelationshipResponse { CanCreate = !hasActiveRelationship };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Backbone.BuildingBlocks.API.Mvc.ControllerAttributes;
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;
Expand All @@ -17,6 +18,7 @@
using Backbone.Modules.Relationships.Application.Relationships.Commands.RevokeRelationshipReactivation;
using Backbone.Modules.Relationships.Application.Relationships.Commands.TerminateRelationship;
using Backbone.Modules.Relationships.Application.Relationships.DTOs;
using Backbone.Modules.Relationships.Application.Relationships.Queries.CanEstablishRelationship;
using Backbone.Modules.Relationships.Application.Relationships.Queries.GetPeerOfActiveIdentityInRelationship;
using Backbone.Modules.Relationships.Application.Relationships.Queries.GetRelationship;
using Backbone.Modules.Relationships.Application.Relationships.Queries.ListRelationships;
Expand Down Expand Up @@ -195,6 +197,15 @@ public async Task<IActionResult> DecomposeRelationship([FromRoute] string id, Ca
return Ok(response);
}

[HttpGet("CanCreate")]
[ProducesResponseType(typeof(HttpResponseEnvelopeResult<CanEstablishRelationshipResponse>), StatusCodes.Status200OK)]
[ProducesError(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CanEstablishRelationship([FromQuery] string peer, CancellationToken cancellationToken)
{
var response = await _mediator.Send(new CanEstablishRelationshipQuery { PeerAddress = peer }, cancellationToken);
return Ok(response);
}

private async Task EnsurePeerIsNotToBeDeleted(string peerIdentityAddress, CancellationToken cancellationToken)
{
var peerIdentity = await _mediator.Send(new GetIdentityQuery(peerIdentityAddress), cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ public static Expression<Func<Relationship, bool>> HasParticipant(string identit
public static Expression<Func<Relationship, bool>> CountsAsActive()
{
return r => r.Status != RelationshipStatus.Rejected &&
r.Status != RelationshipStatus.Revoked;
r.Status != RelationshipStatus.Revoked &&
r.Status != RelationshipStatus.ReadyForDeletion;
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,13 @@ public async Task<ApiResponse<RelationshipMetadata>> DecomposeRelationship(strin
{
return await _client.Put<RelationshipMetadata>($"api/{API_VERSION}/Relationships/{relationshipId}/Decompose");
}

public async Task<ApiResponse<CanEstablishRelationshipResponse>> CanCreateRelationship(string peerIdentityAddress)
{
return await _client
.Request<CanEstablishRelationshipResponse>(HttpMethod.Get, $"api/{API_VERSION}/Relationships/CanCreate")
.Authenticate()
.AddQueryParameter("peer", peerIdentityAddress)
.Execute();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Backbone.ConsumerApi.Sdk.Endpoints.Relationships.Types.Responses;

public class CanEstablishRelationshipResponse
{
public required bool CanCreate { get; set; }
}

0 comments on commit 5a4d55e

Please sign in to comment.