Skip to content

Commit

Permalink
Added voting
Browse files Browse the repository at this point in the history
  • Loading branch information
Thundernerd committed Jan 19, 2025
1 parent b7da211 commit b3077f6
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 4 deletions.
4 changes: 4 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
using TNRD.Zeepkist.GTR.Backend.Users;
using TNRD.Zeepkist.GTR.Backend.Users.Points;
using TNRD.Zeepkist.GTR.Backend.Versioning;
using TNRD.Zeepkist.GTR.Backend.Voting;
using TNRD.Zeepkist.GTR.Backend.Workshop;
using TNRD.Zeepkist.GTR.Backend.WorldRecords;
using TNRD.Zeepkist.GTR.Backend.Zeeplevel;
Expand Down Expand Up @@ -181,6 +182,9 @@
builder.Services.AddScoped<IRecordsRepository, RecordsRepository>();
builder.Services.AddScoped<IRecordsService, RecordsService>();

builder.Services.AddScoped<IVotingRepository, VotingRepository>();
builder.Services.AddScoped<IVotingService, VotingService>();

// builder.Services.AddScoped<IRemoteStorageService, GoogleCloudStorageService>();
builder.Services.AddScoped<IRemoteStorageService, WasabiStorageService>();

Expand Down
6 changes: 6 additions & 0 deletions Voting/Resources/VoteResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TNRD.Zeepkist.GTR.Backend.Voting.Resources;

public class VoteResource
{
public string Level { get; set; } = null!;
}
106 changes: 106 additions & 0 deletions Voting/VotingController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using TNRD.Zeepkist.GTR.Backend.Jwt;
using TNRD.Zeepkist.GTR.Backend.Voting.Resources;

namespace TNRD.Zeepkist.GTR.Backend.Voting;

[ApiController]
[Route("votes")]
public class VotingController : ControllerBase
{
private readonly IVotingService _service;

public VotingController(IVotingService service)
{
_service = service;
}

[HttpPost("upvote")]
public IActionResult Upvote([FromBody] VoteResource resource)
{
string? value = User.FindFirstValue(IJwtService.SteamIdClaimName);
if (string.IsNullOrEmpty(value))
{
return Unauthorized();
}

if (!ulong.TryParse(value, out ulong steamId))
{
return Unauthorized();
}

if (_service.Upvote(steamId, resource.Level).IsSuccess)
{
return Ok();
}

return Problem();
}

[HttpPost("downvote")]
public IActionResult Downvote([FromBody] VoteResource resource)
{
string? value = User.FindFirstValue(IJwtService.SteamIdClaimName);
if (string.IsNullOrEmpty(value))
{
return Unauthorized();
}

if (!ulong.TryParse(value, out ulong steamId))
{
return Unauthorized();
}

if (_service.Downvote(steamId, resource.Level).IsSuccess)
{
return Ok();
}

return Problem();
}

[HttpPost("dupvote")]
public IActionResult DoubleUpvote([FromBody] VoteResource resource)
{
string? value = User.FindFirstValue(IJwtService.SteamIdClaimName);
if (string.IsNullOrEmpty(value))
{
return Unauthorized();
}

if (!ulong.TryParse(value, out ulong steamId))
{
return Unauthorized();
}

if (_service.DoubleUpvote(steamId, resource.Level).IsSuccess)
{
return Ok();
}

return Problem();
}

[HttpPost("ddownvote")]
public IActionResult DoubleDownvote([FromBody] VoteResource resource)
{
string? value = User.FindFirstValue(IJwtService.SteamIdClaimName);
if (string.IsNullOrEmpty(value))
{
return Unauthorized();
}

if (!ulong.TryParse(value, out ulong steamId))
{
return Unauthorized();
}

if (_service.DoubleDownvote(steamId, resource.Level).IsSuccess)
{
return Ok();
}

return Problem();
}
}
16 changes: 16 additions & 0 deletions Voting/VotingRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using TNRD.Zeepkist.GTR.Backend.DataStore;
using TNRD.Zeepkist.GTR.Database.Data.Entities;

namespace TNRD.Zeepkist.GTR.Backend.Voting;

public interface IVotingRepository : IBasicRepository<Vote>
{
}

public class VotingRepository : BasicRepository<Vote>, IVotingRepository
{
public VotingRepository(IDatabase database, ILogger logger)
: base(database, logger)
{
}
}
84 changes: 84 additions & 0 deletions Voting/VotingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using FluentResults;
using TNRD.Zeepkist.GTR.Backend.Levels;
using TNRD.Zeepkist.GTR.Backend.Users;
using TNRD.Zeepkist.GTR.Database.Data.Entities;

namespace TNRD.Zeepkist.GTR.Backend.Voting;

public interface IVotingService
{
Result Upvote(ulong steamId, string levelHash);
Result Downvote(ulong steamId, string levelHash);
Result DoubleUpvote(ulong steamId, string levelHash);
Result DoubleDownvote(ulong steamId, string levelHash);
}

public class VotingService : IVotingService
{
private readonly ILogger<VotingService> _logger;
private readonly IVotingRepository _repository;
private readonly IUserService _userService;
private readonly ILevelService _levelService;

public VotingService(ILogger<VotingService> logger, IVotingRepository repository, IUserService userService,
ILevelService levelService)
{
_logger = logger;
_repository = repository;
_userService = userService;
_levelService = levelService;
}

public Result Upvote(ulong steamId, string levelHash)
{
return Upsert(steamId, levelHash, 1);
}

public Result Downvote(ulong steamId, string levelHash)
{
return Upsert(steamId, levelHash, -1);
}

public Result DoubleUpvote(ulong steamId, string levelHash)
{
return Upsert(steamId, levelHash, 2);
}

public Result DoubleDownvote(ulong steamId, string levelHash)
{
return Upsert(steamId, levelHash, -2);
}

private Result Upsert(ulong steamId, string levelHash, int value)
{
if (!_userService.TryGet(steamId, out User? user))
{
_logger.LogWarning("Unable to get user with steam id {SteamId}", steamId);
return Result.Fail($"Unable to get user with steam id '{steamId}'");
}

if (!_levelService.TryGetByHash(levelHash, out Level? level))
{
_logger.LogWarning("Unable to get level with hash '{Hash}'", levelHash);
return Result.Fail($"Unable to get level with hash '{levelHash}'");
}

Vote vote = _repository.Upsert(vote => vote.IdUser == user.Id && vote.IdLevel == level.Id,
() => new Vote
{
IdUser = user.Id,
IdLevel = level.Id,
Value = value
},
vote =>
{
vote.Value = value;
return vote;
});

return Result.OkIf(vote.IdUser == user.Id &&
vote.IdLevel == level.Id &&
vote.Value == value,
"Failed to upsert vote");
}
}
8 changes: 4 additions & 4 deletions Zeepkist.GTR.Backend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
<PackageReference Include="LinqKit.Microsoft.EntityFrameworkCore" Version="8.1.5"/>
<PackageReference Include="MathNet.Numerics" Version="5.0.0"/>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.11"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.11"/>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1"/>
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0"/>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.11"/>
<PackageReference Include="RabbitMQ.Client" Version="6.8.1"/>
<PackageReference Include="Refit" Version="7.2.22"/>
<PackageReference Include="Refit.HttpClientFactory" Version="7.2.22"/>
Expand All @@ -42,6 +42,6 @@
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
<PackageReference Include="SteamWebAPI2" Version="4.4.1"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
<PackageReference Include="TNRD.Zeepkist.GTR.Database" Version="5.1.2" />
<PackageReference Include="TNRD.Zeepkist.GTR.Database" Version="5.2.0"/>
</ItemGroup>
</Project>

0 comments on commit b3077f6

Please sign in to comment.