Refactor endpoint services to accept narrow inputs
This commit is contained in:
@@ -7,11 +7,11 @@ namespace GameList.Endpoints;
|
||||
|
||||
internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFactory httpFactory)
|
||||
{
|
||||
public async Task<IResult> GetMineAsync(Player player)
|
||||
public async Task<IResult> GetMineAsync(Guid playerId)
|
||||
{
|
||||
var mine = await db.Suggestions
|
||||
.AsNoTracking()
|
||||
.Where(s => s.PlayerId == player.Id)
|
||||
.Where(s => s.PlayerId == playerId)
|
||||
.Select(s => new
|
||||
{
|
||||
s.Id,
|
||||
@@ -36,35 +36,45 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
return Results.Ok(ordered);
|
||||
}
|
||||
|
||||
public async Task<IResult> CreateAsync(Player player, SuggestionRequest request)
|
||||
public async Task<IResult> CreateAsync(Guid playerId, SuggestionInput input)
|
||||
{
|
||||
var validationError = await SuggestionValidator.ValidateAsync(request, httpFactory);
|
||||
var validationError = await SuggestionValidator.ValidateAsync(input, httpFactory);
|
||||
if (validationError is not null)
|
||||
return EndpointHelpers.BadRequestError(validationError);
|
||||
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, player.Id);
|
||||
var usingJoker = phase == Phase.Vote && player.HasJoker;
|
||||
var playerState = await db.Players
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == playerId)
|
||||
.Select(p => new
|
||||
{
|
||||
p.DisplayName,
|
||||
p.HasJoker
|
||||
})
|
||||
.FirstAsync();
|
||||
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId);
|
||||
var usingJoker = phase == Phase.Vote && playerState.HasJoker;
|
||||
if (phase != Phase.Suggest && !usingJoker)
|
||||
return EndpointHelpers.PhaseMismatch(Phase.Suggest, phase);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(player.DisplayName))
|
||||
if (string.IsNullOrWhiteSpace(playerState.DisplayName))
|
||||
return EndpointHelpers.BadRequestError("Set a display name before submitting suggestions.");
|
||||
|
||||
var existingCount = await db.Suggestions.CountAsync(s => s.PlayerId == player.Id);
|
||||
var existingCount = await db.Suggestions.CountAsync(s => s.PlayerId == playerId);
|
||||
if (!usingJoker && existingCount >= 5)
|
||||
return EndpointHelpers.BadRequestError("You have reached the 5 suggestion limit.");
|
||||
|
||||
var suggestion = new Suggestion
|
||||
{
|
||||
PlayerId = player.Id,
|
||||
Name = request.Name.Trim(),
|
||||
Genre = EndpointHelpers.TrimTo(request.Genre, 50),
|
||||
Description = EndpointHelpers.TrimTo(request.Description, 500),
|
||||
ScreenshotUrl = EndpointHelpers.TrimTo(request.ScreenshotUrl, 2048),
|
||||
YoutubeUrl = EndpointHelpers.TrimTo(request.YoutubeUrl, 2048),
|
||||
GameUrl = EndpointHelpers.TrimTo(request.GameUrl, 2048),
|
||||
MinPlayers = request.MinPlayers,
|
||||
MaxPlayers = request.MaxPlayers
|
||||
PlayerId = playerId,
|
||||
Name = input.Name.Trim(),
|
||||
Genre = EndpointHelpers.TrimTo(input.Genre, 50),
|
||||
Description = EndpointHelpers.TrimTo(input.Description, 500),
|
||||
ScreenshotUrl = EndpointHelpers.TrimTo(input.ScreenshotUrl, 2048),
|
||||
YoutubeUrl = EndpointHelpers.TrimTo(input.YoutubeUrl, 2048),
|
||||
GameUrl = EndpointHelpers.TrimTo(input.GameUrl, 2048),
|
||||
MinPlayers = input.MinPlayers,
|
||||
MaxPlayers = input.MaxPlayers
|
||||
};
|
||||
|
||||
await using var tx = await db.Database.BeginTransactionAsync();
|
||||
@@ -73,7 +83,9 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
|
||||
if (usingJoker)
|
||||
{
|
||||
player.HasJoker = false;
|
||||
await db.Players
|
||||
.Where(p => p.Id == playerId)
|
||||
.ExecuteUpdateAsync(p => p.SetProperty(x => x.HasJoker, false));
|
||||
await db.Players.ExecuteUpdateAsync(p => p.SetProperty(x => x.VotesFinal, false));
|
||||
}
|
||||
|
||||
@@ -83,18 +95,28 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
return Results.Created($"/api/suggestions/{suggestion.Id}", new SuggestionCreatedResponse(suggestion.Id));
|
||||
}
|
||||
|
||||
public async Task<IResult> DeleteAsync(Player player, bool isAdmin, int suggestionId)
|
||||
public async Task<IResult> DeleteAsync(Guid playerId, int suggestionId)
|
||||
{
|
||||
var actor = await db.Players
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == playerId)
|
||||
.Select(p => new
|
||||
{
|
||||
p.IsAdmin
|
||||
})
|
||||
.FirstAsync();
|
||||
|
||||
var isAdmin = actor.IsAdmin;
|
||||
if (!isAdmin)
|
||||
{
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, player.Id);
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId);
|
||||
if (phase != Phase.Suggest)
|
||||
return EndpointHelpers.PhaseMismatch(Phase.Suggest, phase);
|
||||
}
|
||||
|
||||
var suggestion = isAdmin
|
||||
? await db.Suggestions.FirstOrDefaultAsync(s => s.Id == suggestionId)
|
||||
: await db.Suggestions.FirstOrDefaultAsync(s => s.Id == suggestionId && s.PlayerId == player.Id);
|
||||
: await db.Suggestions.FirstOrDefaultAsync(s => s.Id == suggestionId && s.PlayerId == playerId);
|
||||
if (suggestion == null)
|
||||
return EndpointHelpers.NotFoundError("Suggestion not found.");
|
||||
|
||||
@@ -112,40 +134,50 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
return Results.NoContent();
|
||||
}
|
||||
|
||||
public async Task<IResult> UpdateAsync(Player player, bool isAdmin, int suggestionId, SuggestionRequest request)
|
||||
public async Task<IResult> UpdateAsync(Guid playerId, int suggestionId, SuggestionInput input)
|
||||
{
|
||||
var validationError = await SuggestionValidator.ValidateAsync(request, httpFactory);
|
||||
var validationError = await SuggestionValidator.ValidateAsync(input, httpFactory);
|
||||
if (validationError is not null)
|
||||
return EndpointHelpers.BadRequestError(validationError);
|
||||
|
||||
var actor = await db.Players
|
||||
.AsNoTracking()
|
||||
.Where(p => p.Id == playerId)
|
||||
.Select(p => new
|
||||
{
|
||||
p.IsAdmin
|
||||
})
|
||||
.FirstAsync();
|
||||
|
||||
var suggestion = await db.Suggestions.FirstOrDefaultAsync(s => s.Id == suggestionId);
|
||||
if (suggestion == null)
|
||||
return EndpointHelpers.NotFoundError("Suggestion not found.");
|
||||
|
||||
var isAdmin = actor.IsAdmin;
|
||||
if (!isAdmin)
|
||||
{
|
||||
if (suggestion.PlayerId != player.Id)
|
||||
if (suggestion.PlayerId != playerId)
|
||||
return EndpointHelpers.UnauthorizedError();
|
||||
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, player.Id);
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId);
|
||||
if (phase == Phase.Results)
|
||||
return EndpointHelpers.PhaseMismatch(Phase.Suggest, phase);
|
||||
|
||||
if (phase == Phase.Suggest)
|
||||
{
|
||||
suggestion.Name = request.Name.Trim();
|
||||
suggestion.Name = input.Name.Trim();
|
||||
}
|
||||
else if (phase != Phase.Vote)
|
||||
{
|
||||
return EndpointHelpers.PhaseMismatch(Phase.Suggest, phase);
|
||||
}
|
||||
|
||||
ApplyEditableFields(suggestion, request);
|
||||
ApplyEditableFields(suggestion, input);
|
||||
}
|
||||
else
|
||||
{
|
||||
suggestion.Name = request.Name.Trim();
|
||||
ApplyEditableFields(suggestion, request);
|
||||
suggestion.Name = input.Name.Trim();
|
||||
ApplyEditableFields(suggestion, input);
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
@@ -163,9 +195,9 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
));
|
||||
}
|
||||
|
||||
public async Task<IResult> GetAllAsync(Player player)
|
||||
public async Task<IResult> GetAllAsync(Guid playerId)
|
||||
{
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, player.Id);
|
||||
var phase = await EndpointHelpers.GetCurrentPhaseAsync(db, playerId);
|
||||
if (phase < Phase.Vote)
|
||||
return EndpointHelpers.PhaseMismatch(Phase.Vote, phase);
|
||||
|
||||
@@ -186,7 +218,7 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
Author = s.Player!.DisplayName,
|
||||
s.CreatedAt,
|
||||
s.ParentSuggestionId,
|
||||
IsOwner = s.PlayerId == player.Id
|
||||
IsOwner = s.PlayerId == playerId
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
@@ -219,14 +251,14 @@ internal sealed class SuggestionWorkflowService(AppDbContext db, IHttpClientFact
|
||||
return Results.Ok(ordered);
|
||||
}
|
||||
|
||||
private static void ApplyEditableFields(Suggestion suggestion, SuggestionRequest request)
|
||||
private static void ApplyEditableFields(Suggestion suggestion, SuggestionInput input)
|
||||
{
|
||||
suggestion.Genre = EndpointHelpers.TrimTo(request.Genre, 50);
|
||||
suggestion.Description = EndpointHelpers.TrimTo(request.Description, 500);
|
||||
suggestion.ScreenshotUrl = EndpointHelpers.TrimTo(request.ScreenshotUrl, 2048);
|
||||
suggestion.YoutubeUrl = EndpointHelpers.TrimTo(request.YoutubeUrl, 2048);
|
||||
suggestion.GameUrl = EndpointHelpers.TrimTo(request.GameUrl, 2048);
|
||||
suggestion.MinPlayers = request.MinPlayers;
|
||||
suggestion.MaxPlayers = request.MaxPlayers;
|
||||
suggestion.Genre = EndpointHelpers.TrimTo(input.Genre, 50);
|
||||
suggestion.Description = EndpointHelpers.TrimTo(input.Description, 500);
|
||||
suggestion.ScreenshotUrl = EndpointHelpers.TrimTo(input.ScreenshotUrl, 2048);
|
||||
suggestion.YoutubeUrl = EndpointHelpers.TrimTo(input.YoutubeUrl, 2048);
|
||||
suggestion.GameUrl = EndpointHelpers.TrimTo(input.GameUrl, 2048);
|
||||
suggestion.MinPlayers = input.MinPlayers;
|
||||
suggestion.MaxPlayers = input.MaxPlayers;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user