-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ 318 ddd add a hero to a team (#323)
* AddHeroToTeam endpoint and command * Added command to add hero to a team * Added query to get a team * Fixed warning * Fixed issue with Database project. * Fixed integration test * Tidied up code * Reverted test data entity numbers * Replace IEnumerable<T> on aggregates to IReadonlyList<T> * Update tests/WebApi.IntegrationTests/Common/Fixtures/IntegrationTestBase.cs Co-authored-by: Matt Goldman [SSW] <[email protected]> --------- Co-authored-by: Matt Goldman [SSW] <[email protected]>
- Loading branch information
1 parent
cf28e78
commit 585b0b4
Showing
14 changed files
with
246 additions
and
3 deletions.
There are no files selected for viewing
52 changes: 52 additions & 0 deletions
52
src/Application/Features/Teams/Commands/AddHeroToTeam/AddHeroToTeamCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
using SSW.CleanArchitecture.Application.Common.Exceptions; | ||
using SSW.CleanArchitecture.Application.Common.Interfaces; | ||
using SSW.CleanArchitecture.Domain.Heroes; | ||
using SSW.CleanArchitecture.Domain.Teams; | ||
|
||
namespace SSW.CleanArchitecture.Application.Features.Teams.Commands.AddHeroToTeam; | ||
|
||
public sealed record AddHeroToTeamCommand(Guid TeamId, Guid HeroId) : IRequest; | ||
|
||
// ReSharper disable once UnusedType.Global | ||
public sealed class AddHeroToTeamCommandHandler(IApplicationDbContext dbContext) | ||
: IRequestHandler<AddHeroToTeamCommand> | ||
{ | ||
public async Task Handle(AddHeroToTeamCommand request, CancellationToken cancellationToken) | ||
{ | ||
var teamId = new TeamId(request.TeamId); | ||
var heroId = new HeroId(request.HeroId); | ||
|
||
var team = dbContext.Teams | ||
.WithSpecification(new TeamByIdSpec(teamId)) | ||
.FirstOrDefault(); | ||
|
||
if (team is null) | ||
{ | ||
throw new NotFoundException(nameof(Team), teamId); | ||
} | ||
|
||
var hero = dbContext.Heroes | ||
.WithSpecification(new HeroByIdSpec(heroId)) | ||
.FirstOrDefault(); | ||
|
||
if (hero is null) | ||
{ | ||
throw new NotFoundException(nameof(Hero), heroId); | ||
} | ||
|
||
team.AddHero(hero); | ||
await dbContext.SaveChangesAsync(cancellationToken); | ||
} | ||
} | ||
|
||
public class AddHeroToTeamCommandValidator : AbstractValidator<AddHeroToTeamCommand> | ||
{ | ||
public AddHeroToTeamCommandValidator() | ||
{ | ||
RuleFor(v => v.HeroId) | ||
.NotEmpty(); | ||
|
||
RuleFor(v => v.TeamId) | ||
.NotEmpty(); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
src/Application/Features/Teams/Queries/GetTeam/GetTeamQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using SSW.CleanArchitecture.Application.Common.Interfaces; | ||
using SSW.CleanArchitecture.Domain.Teams; | ||
|
||
namespace SSW.CleanArchitecture.Application.Features.Teams.Queries.GetTeam; | ||
|
||
public record GetTeamQuery(Guid TeamId) : IRequest<TeamDto?>; | ||
|
||
public sealed class GetAllTeamsQueryHandler(IApplicationDbContext dbContext) : IRequestHandler<GetTeamQuery, TeamDto?> | ||
{ | ||
public async Task<TeamDto?> Handle( | ||
GetTeamQuery request, | ||
CancellationToken cancellationToken) | ||
{ | ||
var teamId = new TeamId(request.TeamId); | ||
|
||
var team = await dbContext.Teams | ||
.Where(t => t.Id == teamId) | ||
.Select(t => new TeamDto | ||
{ | ||
Id = t.Id.Value, | ||
Name = t.Name, | ||
Heroes = t.Heroes.Select(h => new HeroDto { Id = h.Id.Value, Name = h.Name }).ToList() | ||
}) | ||
.FirstOrDefaultAsync(cancellationToken); | ||
|
||
return team; | ||
} | ||
} | ||
|
||
public class TeamDto | ||
{ | ||
public Guid Id { get; init; } | ||
public required string Name { get; init; } | ||
public List<HeroDto> Heroes { get; init; } = []; | ||
} | ||
|
||
public class HeroDto | ||
{ | ||
public Guid Id { get; init; } | ||
public required string Name { get; init; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Ardalis.Specification; | ||
|
||
namespace SSW.CleanArchitecture.Domain.Heroes; | ||
|
||
public sealed class HeroByIdSpec : SingleResultSpecification<Hero> | ||
{ | ||
public HeroByIdSpec(HeroId heroId) | ||
{ | ||
Query.Where(t => t.Id == heroId); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using Ardalis.Specification; | ||
|
||
namespace SSW.CleanArchitecture.Domain.Teams; | ||
|
||
public sealed class TeamByIdSpec : SingleResultSpecification<Team> | ||
{ | ||
public TeamByIdSpec(TeamId teamId) | ||
{ | ||
Query.Where(t => t.Id == teamId) | ||
.Include(t => t.Missions) | ||
.Include(t => t.Heroes); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
@baseUrl = https://localhost:7255/api | ||
@teamId = a3e403bb-082e-494c-81a4-1b1eac2d1d5c | ||
@heroId = d6771ee7-d844-472c-90a0-6ccabf488630 | ||
|
||
### Get Heroes | ||
GET {{baseUrl}}/heroes | ||
|
||
### Get Teams | ||
GET {{baseUrl}}/teams | ||
|
||
### Add Hero to Team | ||
POST {{baseUrl}}/teams/{{teamId}}/heroes/{{heroId}} | ||
|
||
### Get Team | ||
GET {{baseUrl}}/teams/{{teamId}} | ||
|
||
### Create Team | ||
POST {{baseUrl}}/teams | ||
Content-Type: application/json | ||
|
||
{ | ||
"name": "Dans A-Team" | ||
} | ||
|
||
### Create Hero | ||
POST {{baseUrl}}/heroes | ||
Content-Type: application/json | ||
|
||
{ | ||
"name": "Super Dan", | ||
"alias": "Dan", | ||
"powers": [ | ||
{ | ||
"name": "Super Strength", | ||
"powerLevel": 1 | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
tests/WebApi.IntegrationTests/Endpoints/Teams/Commands/AddHeroToTeamCommandTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using Ardalis.Specification.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore; | ||
using SSW.CleanArchitecture.Application.Features.Teams.Commands.AddHeroToTeam; | ||
using SSW.CleanArchitecture.Application.Features.Teams.Commands.CreateTeam; | ||
using SSW.CleanArchitecture.Application.Features.Teams.Queries.GetTeam; | ||
using SSW.CleanArchitecture.Domain.Teams; | ||
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 AddHeroToTeamCommandTests(TestingDatabaseFixture fixture, ITestOutputHelper output) | ||
: IntegrationTestBase(fixture, output) | ||
{ | ||
[Fact] | ||
public async Task Command_ShouldAddHeroToTeam() | ||
{ | ||
// Arrange | ||
var hero = HeroFactory.Generate(); | ||
var team = TeamFactory.Generate(); | ||
await AddEntityAsync(hero); | ||
await AddEntityAsync(team); | ||
var teamId = team.Id.Value; | ||
var heroId = hero.Id.Value; | ||
var client = GetAnonymousClient(); | ||
|
||
// Act | ||
var result = await client.PostAsync($"/api/teams/{teamId}/heroes/{heroId}", null); | ||
|
||
// Assert | ||
result.StatusCode.Should().Be(HttpStatusCode.Created); | ||
|
||
var updatedTeam = await client.GetFromJsonAsync<TeamDto>($"/api/teams/{teamId}"); | ||
updatedTeam.Should().NotBeNull(); | ||
updatedTeam!.Heroes.Should().HaveCount(1); | ||
updatedTeam.Heroes.First().Id.Should().Be(heroId); | ||
} | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using SSW.CleanArchitecture.Application.Common.Interfaces; | ||
|
||
namespace SSW.CleanArchitecture.Database; | ||
|
||
public class MockCurrentUserService : ICurrentUserService | ||
{ | ||
public string UserId => "00000000-0000-0000-0000-000000000000"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters