Skip to content

Commit

Permalink
Refactoring - Endpoint Tests, Naming, Team Endpoint Urls (#26)
Browse files Browse the repository at this point in the history
* refactored endpoint tests to use URL/GetUrl for retrieving target endpoint url
* fixed endpoints to use ProduceProblem for error codes, fixed endpoints to have consistently ordered options
* added members section for accessing team members endpoints
* renamed user details contract, endpoint, command, command handler and tests to AccountDetails
* renamed logged in user to current user
  • Loading branch information
skrasekmichael authored Feb 23, 2024
1 parent 8c6d51d commit 36901a8
Show file tree
Hide file tree
Showing 36 changed files with 166 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ public void MapEndpoints(RouteGroupBuilder group)
.ProducesProblem(StatusCodes.Status401Unauthorized)
.ProducesProblem(StatusCodes.Status403Forbidden)
.ProducesProblem(StatusCodes.Status404NotFound)
.MapToApiVersion(1)
.WithName(nameof(AcceptInvitationEndpoint));
.WithName(nameof(AcceptInvitationEndpoint))
.MapToApiVersion(1);
}

private async Task<IResult> AcceptInvitationAsync(
Expand All @@ -28,7 +28,7 @@ private async Task<IResult> AcceptInvitationAsync(
[FromServices] IHttpContextAccessor httpContextAccessor,
CancellationToken ct)
{
var command = new AcceptInvitationCommand(httpContextAccessor.GetLoggedUserId(), InvitationId.FromGuid(invitationId));
var command = new AcceptInvitationCommand(httpContextAccessor.GetCurrentUserId(), InvitationId.FromGuid(invitationId));
var result = await sender.Send(command, ct);
return result.Match(TypedResults.Ok);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private async Task<IResult> GetTeamInvitationsAsync(
[FromServices] IHttpContextAccessor httpContextAccessor,
CancellationToken ct)
{
var query = new GetTeamInvitationsQuery(httpContextAccessor.GetLoggedUserId(), TeamId.FromGuid(teamId));
var query = new GetTeamInvitationsQuery(httpContextAccessor.GetCurrentUserId(), TeamId.FromGuid(teamId));
var result = await sender.Send(query, ct);
return result.Match(TypedResults.Ok);
}
Expand Down
12 changes: 6 additions & 6 deletions src/TeamUp.Api/Endpoints/Invitations/InviteUserEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ public void MapEndpoints(RouteGroupBuilder group)
{
group.MapPost("/", InviteUserAsync)
.Produces<InvitationId>(StatusCodes.Status201Created)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status401Unauthorized)
.ProducesProblem(StatusCodes.Status403Forbidden)
.ProducesProblem(StatusCodes.Status404NotFound)
.ProducesValidationProblem()
.MapToApiVersion(1)
.WithName(nameof(InviteUserEndpoint));
.WithName(nameof(InviteUserEndpoint))
.MapToApiVersion(1);
}

private async Task<IResult> InviteUserAsync(
Expand All @@ -29,7 +29,7 @@ private async Task<IResult> InviteUserAsync(
[FromServices] LinkGenerator linkGenerator,
CancellationToken ct)
{
var command = new InviteUserCommand(httpContextAccessor.GetLoggedUserId(), request.TeamId, request.Email);
var command = new InviteUserCommand(httpContextAccessor.GetCurrentUserId(), request.TeamId, request.Email);

var result = await sender.Send(command, ct);
return result.Match(invitationId => TypedResults.Created(
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/ChangeNicknameEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private async Task<IResult> ChangeNicknameAsync(
CancellationToken ct)
{
var command = new ChangeNicknameCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
request.Nickname
);
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/ChangeOwnerShipEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private async Task<IResult> ChangeOwnerShipAsync(
CancellationToken ct)
{
var command = new ChangeOwnershipCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
TeamMemberId.FromGuid(teamMemberId)
);
Expand Down
8 changes: 4 additions & 4 deletions src/TeamUp.Api/Endpoints/Teams/CreateEventTypeEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public void MapEndpoints(RouteGroupBuilder group)
{
group.MapPost("/{teamId:guid}/event-types", CreateEventTypeAsync)
.Produces<EventTypeId>(StatusCodes.Status201Created)
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status403Forbidden)
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status401Unauthorized)
.ProducesProblem(StatusCodes.Status403Forbidden)
.ProducesProblem(StatusCodes.Status404NotFound)
.ProducesValidationProblem()
.WithName(nameof(CreateEventTypeEndpoint))
.MapToApiVersion(1);
Expand All @@ -31,7 +31,7 @@ private async Task<IResult> CreateEventTypeAsync(
CancellationToken ct)
{
var command = new CreateEventTypeCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
request.Name,
request.Description
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/CreateTeamEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private async Task<IResult> CreateTeamAsync(
[FromServices] LinkGenerator linkGenerator,
CancellationToken ct)
{
var command = new CreateTeamCommand(httpContextAccessor.GetLoggedUserId(), request.Name);
var command = new CreateTeamCommand(httpContextAccessor.GetCurrentUserId(), request.Name);
var result = await sender.Send(command, ct);
return result.Match(teamId => TypedResults.Created(
uri: linkGenerator.GetPathByName(nameof(GetTeamEndpoint), teamId.Value),
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/DeleteTeamEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private async Task<IResult> DeleteTeamAsync(
[FromServices] IHttpContextAccessor httpContextAccessor,
CancellationToken ct)
{
var command = new DeleteTeamCommand(httpContextAccessor.GetLoggedUserId(), TeamId.FromGuid(teamId));
var command = new DeleteTeamCommand(httpContextAccessor.GetCurrentUserId(), TeamId.FromGuid(teamId));
var result = await sender.Send(command, ct);
return result.Match(TypedResults.Ok);
}
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/GetTeamEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private async Task<IResult> GetTeamAsync(
[FromServices] IHttpContextAccessor httpContextAccessor,
CancellationToken ct)
{
var query = new GetTeamQuery(httpContextAccessor.GetLoggedUserId(), TeamId.FromGuid(teamId));
var query = new GetTeamQuery(httpContextAccessor.GetCurrentUserId(), TeamId.FromGuid(teamId));
var result = await sender.Send(query, ct);
return result.Match(TypedResults.Ok);
}
Expand Down
4 changes: 2 additions & 2 deletions src/TeamUp.Api/Endpoints/Teams/RemoveTeamMemberEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class RemoveTeamMemberEndpoint : IEndpointGroup
{
public void MapEndpoints(RouteGroupBuilder group)
{
group.MapDelete("/{teamId:guid}/{teamMemberId:guid}", RemoveTeamMemberAsync)
group.MapDelete("/{teamId:guid}/members/{teamMemberId:guid}", RemoveTeamMemberAsync)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status401Unauthorized)
Expand All @@ -30,7 +30,7 @@ private async Task<IResult> RemoveTeamMemberAsync(
CancellationToken ct)
{
var command = new RemoveTeamMemberCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
TeamMemberId.FromGuid(teamMemberId)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class UpdateTeamMemberRoleEndpoint : IEndpointGroup
{
public void MapEndpoints(RouteGroupBuilder group)
{
group.MapPut("/{teamId:guid}/{teamMemberId:guid}/role", UpdateTeamRoleAsync)
group.MapPut("/{teamId:guid}/members/{teamMemberId:guid}/role", UpdateTeamRoleAsync)
.Produces(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status401Unauthorized)
Expand All @@ -32,7 +32,7 @@ private async Task<IResult> UpdateTeamRoleAsync(
CancellationToken ct)
{
var command = new SetMemberRoleCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
TeamMemberId.FromGuid(teamMemberId),
request.Role
Expand Down
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/Teams/UpdateTeamNameEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ private async Task<IResult> UpdateTeamNameAsync(
CancellationToken ct)
{
var command = new SetTeamNameCommand(
httpContextAccessor.GetLoggedUserId(),
httpContextAccessor.GetCurrentUserId(),
TeamId.FromGuid(teamId),
request.Name
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@
using Microsoft.AspNetCore.Mvc;

using TeamUp.Api.Extensions;
using TeamUp.Application.Users.GetUserDetail;
using TeamUp.Application.Users.GetAccountDetails;
using TeamUp.Contracts.Users;

namespace TeamUp.Api.Endpoints.UserAccess;

public sealed class GetUserDetailsEndpoint : IEndpointGroup
public sealed class GetMyAccountDetailsEndpoint : IEndpointGroup
{
public void MapEndpoints(RouteGroupBuilder group)
{
group.MapGet("/", GetUserDetailsAsync)
.Produces<UserResponse>(StatusCodes.Status200OK)
group.MapGet("/", GetAccountDetailsAsync)
.Produces<AccountResponse>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status401Unauthorized)
.WithName(nameof(GetUserDetailsEndpoint))
.WithName(nameof(GetMyAccountDetailsEndpoint))
.MapToApiVersion(1)
.RequireAuthorization();
}

private async Task<IResult> GetUserDetailsAsync(
private async Task<IResult> GetAccountDetailsAsync(
[FromServices] ISender sender,
[FromServices] IHttpContextAccessor httpContextAccessor,
CancellationToken ct)
{
var query = new GetUserDetailsQuery(httpContextAccessor.GetLoggedUserId());
var query = new GetAccountDetailsQuery(httpContextAccessor.GetCurrentUserId());
var result = await sender.Send(query, ct);
return result.Match(TypedResults.Ok);
}
Expand Down
3 changes: 2 additions & 1 deletion src/TeamUp.Api/Endpoints/UserAccess/RegisterUserEndpoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public void MapEndpoints(RouteGroupBuilder group)
{
group.MapPost("/register", RegisterUserAsync)
.Produces<UserId>(StatusCodes.Status201Created)
.ProducesProblem(StatusCodes.Status409Conflict)
.ProducesValidationProblem()
.WithName(nameof(RegisterUserEndpoint))
.MapToApiVersion(1);
Expand All @@ -29,7 +30,7 @@ private async Task<IResult> RegisterUserAsync(
var command = mapper.ToCommand(request);
var result = await sender.Send(command, ct);
return result.Match(
userId => TypedResults.Created(linkGenerator.GetPathByName(nameof(GetUserDetailsEndpoint)), userId)
userId => TypedResults.Created(linkGenerator.GetPathByName(nameof(GetMyAccountDetailsEndpoint)), userId)
);
}
}
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Endpoints/UserAccessEndpointGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public void MapEndpoints(RouteGroupBuilder group)
group.MapEndpoint<RegisterUserEndpoint>()
.MapEndpoint<ActivateAccountEndpoint>()
.MapEndpoint<LoginUserEndpoint>()
.MapEndpoint<GetUserDetailsEndpoint>();
.MapEndpoint<GetMyAccountDetailsEndpoint>();
}
}
2 changes: 1 addition & 1 deletion src/TeamUp.Api/Extensions/HttpContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace TeamUp.Api.Extensions;

public static class HttpContextExtensions
{
public static UserId GetLoggedUserId(this IHttpContextAccessor contextAccessor) =>
public static UserId GetCurrentUserId(this IHttpContextAccessor contextAccessor) =>
UserId.FromGuid(contextAccessor.ParseClaim(ClaimTypes.NameIdentifier, Guid.Parse));

public static TOut ParseClaim<TOut>(this IHttpContextAccessor contextAccessor, string type, Func<string, TOut> parse)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using TeamUp.Application.Abstractions;
using TeamUp.Common;
using TeamUp.Contracts.Users;

namespace TeamUp.Application.Users.GetAccountDetails;

public sealed record GetAccountDetailsQuery(UserId UserId) : IQuery<Result<AccountResponse>>;
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@
using TeamUp.Contracts.Users;
using TeamUp.Domain.Aggregates.Users;

namespace TeamUp.Application.Users.GetUserDetail;
namespace TeamUp.Application.Users.GetAccountDetails;

internal sealed class GetUserDetailsQueryHandler : IQueryHandler<GetUserDetailsQuery, Result<UserResponse>>
internal sealed class GetAccountDetailsQueryHandler : IQueryHandler<GetAccountDetailsQuery, Result<AccountResponse>>
{
private readonly IAppQueryContext _queryContext;

public GetUserDetailsQueryHandler(IAppQueryContext queryContext)
public GetAccountDetailsQueryHandler(IAppQueryContext queryContext)
{
_queryContext = queryContext;
}

public async Task<Result<UserResponse>> Handle(GetUserDetailsQuery request, CancellationToken ct)
public async Task<Result<AccountResponse>> Handle(GetAccountDetailsQuery request, CancellationToken ct)
{
var user = await _queryContext.Users
.Where(user => user.Id == request.UserId)
.Select(user => new UserResponse
.Select(user => new AccountResponse
{
Email = user.Email,
Name = user.Name,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TeamUp.Contracts.Users;

public sealed class UserResponse
public sealed class AccountResponse
{
public required string Email { get; set; }
public required string Name { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;

using TeamUp.Contracts.Invitations;
using TeamUp.Contracts.Teams;

namespace TeamUp.EndToEndTests.EndpointTests.Invitations;
Expand All @@ -8,6 +9,9 @@ public sealed class AcceptInvitationTests : BaseInvitationTests
{
public AcceptInvitationTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { }

public static string GetUrl(InvitationId invitationId) => GetUrl(invitationId.Value);
public static string GetUrl(Guid invitationId) => $"/api/v1/invitations/{invitationId}/accept";

[Fact]
public async Task AcceptInvitation_ThatIsValid_AsRecipient_Should_RemoveInvitationFromDatabase_And_AddUserAsMemberToTeamInDatabase()
{
Expand All @@ -30,7 +34,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.PostAsync($"/api/v1/invitations/{invitation.Id.Value}/accept", null);
var response = await Client.PostAsync(GetUrl(invitation.Id), null);

//assert
response.Should().Be200Ok();
Expand Down Expand Up @@ -75,7 +79,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.PostAsync($"/api/v1/invitations/{invitation.Id.Value}/accept", null);
var response = await Client.PostAsync(GetUrl(invitation.Id), null);

//assert
response.Should().Be400BadRequest();
Expand All @@ -101,11 +105,10 @@ await UseDbContextAsync(dbContext =>
dbContext.Teams.Add(team);
return dbContext.SaveChangesAsync();
});

Authenticate(initiatorUser);

//act
var response = await Client.PostAsync($"/api/v1/invitations/{invitationId}/accept", null);
var response = await Client.PostAsync(GetUrl(invitationId), null);

//assert
response.Should().Be404NotFound();
Expand Down Expand Up @@ -137,7 +140,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.PostAsync($"/api/v1/invitations/{invitation.Id.Value}/accept", null);
var response = await Client.PostAsync(GetUrl(invitation.Id), null);

//assert
response.Should().Be403Forbidden();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ public sealed class GetTeamInvitationsTests : BaseInvitationTests
{
public GetTeamInvitationsTests(TeamApiWebApplicationFactory appFactory) : base(appFactory) { }

public static string GetUrl(TeamId teamId) => GetUrl(teamId.Value);
public static string GetUrl(Guid teamId) => $"/api/v1/invitations/teams/{teamId}";

[Theory]
[InlineData(TeamRole.Coordinator)]
[InlineData(TeamRole.Admin)]
Expand Down Expand Up @@ -34,7 +37,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.GetAsync($"/api/v1/invitations/teams/{team.Id.Value}");
var response = await Client.GetAsync(GetUrl(team.Id));

//assert
response.Should().Be200Ok();
Expand Down Expand Up @@ -64,7 +67,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.GetAsync($"/api/v1/invitations/teams/{team.Id.Value}");
var response = await Client.GetAsync(GetUrl(team.Id));

//assert
response.Should().Be403Forbidden();
Expand Down Expand Up @@ -95,7 +98,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.GetAsync($"/api/v1/invitations/teams/{team.Id.Value}");
var response = await Client.GetAsync(GetUrl(team.Id));

//assert
response.Should().Be403Forbidden();
Expand All @@ -120,7 +123,7 @@ await UseDbContextAsync(dbContext =>
Authenticate(initiatorUser);

//act
var response = await Client.GetAsync($"/api/v1/invitations/teams/{teamId}");
var response = await Client.GetAsync(GetUrl(teamId));

//assert
response.Should().Be404NotFound();
Expand Down
Loading

0 comments on commit 36901a8

Please sign in to comment.