Skip to content

Commit

Permalink
✨ 320 ddd complete a mission (#325)
Browse files Browse the repository at this point in the history
* Fix bugs with endpoint

* Added tests
  • Loading branch information
danielmackay authored May 29, 2024
1 parent 585b0b4 commit e4f8a7b
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using SSW.CleanArchitecture.Application.Common.Exceptions;
using SSW.CleanArchitecture.Application.Common.Interfaces;
using SSW.CleanArchitecture.Domain.Teams;

namespace SSW.CleanArchitecture.Application.Features.Teams.Commands.ExecuteMission;

public sealed record ExecuteMissionCommand(Guid TeamId, string Description) : IRequest<Guid>;

// ReSharper disable once UnusedType.Global
public sealed class ExecuteMissionCommandHandler(IApplicationDbContext dbContext)
: IRequestHandler<ExecuteMissionCommand, Guid>
{
public async Task<Guid> Handle(ExecuteMissionCommand request, CancellationToken cancellationToken)
{
var teamId = new TeamId(request.TeamId);
var team = dbContext.Teams
.WithSpecification(new TeamByIdSpec(teamId))
.FirstOrDefault();

if (team is null)
{
throw new NotFoundException(nameof(Team), teamId);
}

team.ExecuteMission(request.Description);
await dbContext.SaveChangesAsync(cancellationToken);

return team.Missions.First().Id.Value;
}
}

public class ExecuteMissionCommandValidator : AbstractValidator<ExecuteMissionCommand>
{
public ExecuteMissionCommandValidator()
{
RuleFor(v => v.TeamId)
.NotEmpty();

RuleFor(v => v.Description)
.NotEmpty();
}
}
17 changes: 16 additions & 1 deletion src/WebApi/Features/TeamEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using SSW.CleanArchitecture.Application.Features.Teams.Commands.AddHeroToTeam;
using SSW.CleanArchitecture.Application.Features.Teams.Commands.CreateTeam;
using SSW.CleanArchitecture.Application.Features.Teams.Commands.ExecuteMission;
using SSW.CleanArchitecture.Application.Features.Teams.Queries.GetAllTeams;
using SSW.CleanArchitecture.Application.Features.Teams.Queries.GetTeam;
using SSW.CleanArchitecture.Domain.Heroes;
Expand Down Expand Up @@ -55,5 +57,18 @@ public static void MapTeamEndpoints(this WebApplication app)
})
.WithName("GetTeam")
.ProducesGet<TeamDto>();

group
.MapPost("/{teamId:guid}/missions",
async (ISender sender, Guid teamId, [FromBody]ExcuteMissionRequest request, CancellationToken ct) =>
{
var command = new ExecuteMissionCommand(teamId, request.Description);
var response = await sender.Send(command, ct);
return Results.Ok(response);
})
.WithName("ExecuteMission")
.ProducesPost();
}
}
}

public record ExcuteMissionRequest(string Description);
8 changes: 8 additions & 0 deletions src/WebApi/WebApi.http
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,11 @@ Content-Type: application/json
}
]
}

### Execute Mission
POST {{baseUrl}}/teams/{{teamId}}/missions
Content-Type: application/json

{
"description": "Save the world from the evil villains"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SSW.CleanArchitecture.Infrastructure.Persistence;

Expand All @@ -20,6 +21,8 @@ public abstract class IntegrationTestBase : IAsyncLifetime
// see: https://github.com/SSWConsulting/SSW.CleanArchitecture/issues/324
protected ApplicationDbContext Context { get; }

protected IQueryable<T> GetQueryable<T>() where T : class => Context.Set<T>().AsNoTracking();

protected IntegrationTestBase(TestingDatabaseFixture fixture, ITestOutputHelper output)
{
_fixture = fixture;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore;
using SSW.CleanArchitecture.Domain.Teams;
using SSW.CleanArchitecture.WebApi.Features;
using System.Net;
using System.Net.Http.Json;
using WebApi.IntegrationTests.Common.Factories;
using WebApi.IntegrationTests.Common.Fixtures;

namespace WebApi.IntegrationTests.Endpoints.Teams.Commands;

public class ExecuteMissionCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output)
: IntegrationTestBase(fixture, output)
{
[Fact]
public async Task Command_ShouldExecuteMission()
{
// Arrange
var hero = HeroFactory.Generate();
var team = TeamFactory.Generate();
await AddEntityAsync(hero);
await AddEntityAsync(team);
team.AddHero(hero);
await Context.SaveChangesAsync();
var teamId = team.Id.Value;
var client = GetAnonymousClient();
var request = new ExcuteMissionRequest("Save the world");

// Act
var result = await client.PostAsJsonAsync($"/api/teams/{teamId}/missions", request);

// Assert
var response = await result.Content.ReadFromJsonAsync<Guid>();
var missionId = new MissionId(response);
var mission = await GetQueryable<Mission>().FirstOrDefaultAsync(m => m.Id == missionId);

result.StatusCode.Should().Be(HttpStatusCode.OK);
mission.Should().NotBeNull();
mission!.Description.Should().Be(request.Description);
}
}

0 comments on commit e4f8a7b

Please sign in to comment.