Skip to content

Commit

Permalink
Merge pull request #58 from Atralupus/feat/caching-blockindex
Browse files Browse the repository at this point in the history
Use cached block index, season, round
  • Loading branch information
Atralupus authored Jan 15, 2025
2 parents 624a622 + 03ebe93 commit 4d30d89
Show file tree
Hide file tree
Showing 19 changed files with 452 additions and 168 deletions.
File renamed without changes.
8 changes: 8 additions & 0 deletions ArenaService/Constants/BattleStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace ArenaService.Constants;

public enum BattleStatus
{
PENDING,
TRACKING,
COMPLETED,
}
10 changes: 10 additions & 0 deletions ArenaService/Constants/TxStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace ArenaService.Constants;

public enum TxStatus
{
INVALID,
STAGING,
SUCCESS,
FAILURE,
INCLUDED,
}
59 changes: 32 additions & 27 deletions ArenaService/Controllers/AvailableOpponentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,66 +11,61 @@ namespace ArenaService.Controllers;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;

[Route("seasons/{seasonId}/available-opponents")]
[Route("available-opponents")]
[ApiController]
public class AvailableOpponentController : ControllerBase
{
private readonly IBackgroundJobClient _jobClient;
private readonly IAvailableOpponentRepository _availableOpponentRepo;
private readonly IParticipantRepository _participantRepo;
private readonly ISeasonRepository _seasonRepo;
private readonly ISeasonCacheRepository _seasonCacheRepo;

public AvailableOpponentController(
IAvailableOpponentRepository availableOpponentRepo,
IParticipantRepository participantRepo,
ISeasonRepository seasonRepo,
ISeasonCacheRepository seasonCacheRepo,
IBackgroundJobClient jobClient
)
{
_availableOpponentRepo = availableOpponentRepo;
_participantRepo = participantRepo;
_seasonRepo = seasonRepo;
_seasonCacheRepo = seasonCacheRepo;
_jobClient = jobClient;
}

[HttpGet]
[Authorize(Roles = "User", AuthenticationSchemes = "ES256K")]
[ProducesResponseType(typeof(AvailableOpponentsResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<
Results<UnauthorizedHttpResult, NotFound<string>, Ok<AvailableOpponentsResponse>>
> GetAvailableOpponents(int seasonId, long blockIndex)
Results<
UnauthorizedHttpResult,
NotFound<string>,
StatusCodeHttpResult,
Ok<AvailableOpponentsResponse>
>
> GetAvailableOpponents()
{
var avatarAddress = HttpContext.User.RequireAvatarAddress();

var season = await _seasonRepo.GetSeasonAsync(seasonId);
var currentSeason = await _seasonCacheRepo.GetSeasonAsync();
var currentRound = await _seasonCacheRepo.GetRoundAsync();

if (season == null)
if (currentSeason is null || currentRound is null)
{
return TypedResults.NotFound("No season found.");
}

var currentRound = season.Rounds.FirstOrDefault(ai =>
ai.StartBlock <= blockIndex && ai.EndBlock >= blockIndex
);

if (currentRound == null)
{
return TypedResults.NotFound(
$"No active arena interval found for block index {blockIndex}."
);
return TypedResults.StatusCode(StatusCodes.Status503ServiceUnavailable);
}

var availableOpponents = await _availableOpponentRepo.GetAvailableOpponents(
avatarAddress,
seasonId,
currentRound.Id
currentSeason.Value.Id,
currentRound.Value.Id
);

if (availableOpponents == null)
{
return TypedResults.NotFound($"Available opponents not found.");
return TypedResults.NotFound("No available opponents found for the current round.");
}

var opponents = new List<Participant>();
Expand All @@ -96,13 +91,23 @@ public async Task<
[Authorize(Roles = "User", AuthenticationSchemes = "ES256K")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(typeof(UnauthorizedHttpResult), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public Results<UnauthorizedHttpResult, NotFound<string>, Ok> RequestResetOpponents(int seasonId)
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<
Results<UnauthorizedHttpResult, NotFound<string>, StatusCodeHttpResult, Ok>
> RequestResetOpponents()
{
var avatarAddress = HttpContext.User.RequireAvatarAddress();

var currentSeason = await _seasonCacheRepo.GetSeasonAsync();
var currentRound = await _seasonCacheRepo.GetRoundAsync();

if (currentSeason is null || currentRound is null)
{
return TypedResults.StatusCode(StatusCodes.Status503ServiceUnavailable);
}

_jobClient.Enqueue<CalcAvailableOpponentsProcessor>(processor =>
processor.ProcessAsync(avatarAddress, seasonId)
processor.ProcessAsync(avatarAddress, currentSeason.Value.Id, currentRound.Value.Id)
);

return TypedResults.Ok();
Expand Down
59 changes: 39 additions & 20 deletions ArenaService/Controllers/BattleController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,53 @@ namespace ArenaService.Controllers;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;

[Route("seasons/{seasonId}/battle")]
[Route("battle")]
[ApiController]
public class BattleController : ControllerBase
{
private readonly IBackgroundJobClient _jobClient;
private readonly IAvailableOpponentRepository _availableOpponentRepo;
private readonly IParticipantRepository _participantRepo;
private readonly IBattleLogRepository _battleLogRepo;
private readonly ISeasonCacheRepository _seasonCacheRepo;

public BattleController(
IAvailableOpponentRepository availableOpponentService,
IParticipantRepository participantService,
IBattleLogRepository battleLogRepo,
ISeasonCacheRepository seasonCacheRepo,
IBackgroundJobClient jobClient
)
{
_availableOpponentRepo = availableOpponentService;
_participantRepo = participantService;
_battleLogRepo = battleLogRepo;
_jobClient = jobClient;
_seasonCacheRepo = seasonCacheRepo;
}

[HttpGet("token")]
[Authorize(Roles = "User", AuthenticationSchemes = "ES256K")]
[ProducesResponseType(typeof(BattleTokenResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(UnauthorizedHttpResult), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status503ServiceUnavailable)]
public async Task<
Results<UnauthorizedHttpResult, NotFound<string>, Ok<BattleTokenResponse>>
> CreateBattleToken(int seasonId, string opponentAvatarAddress)
Results<
UnauthorizedHttpResult,
NotFound<string>,
StatusCodeHttpResult,
Ok<BattleTokenResponse>
>
> CreateBattleToken(string opponentAvatarAddress)
{
var avatarAddress = HttpContext.User.RequireAvatarAddress();

var currentSeason = await _seasonCacheRepo.GetSeasonAsync();
var currentRound = await _seasonCacheRepo.GetRoundAsync();

if (currentSeason is null || currentRound is null)
{
return TypedResults.StatusCode(StatusCodes.Status503ServiceUnavailable);
}

var defenderAvatarAddress = new Address(opponentAvatarAddress);

var battleLog = await _battleLogRepo.AddBattleLogAsync(
seasonId,
currentSeason.Value.Id,
avatarAddress,
defenderAvatarAddress,
"token"
Expand All @@ -59,33 +70,41 @@ public async Task<
[HttpPost("request")]
[Authorize(Roles = "User", AuthenticationSchemes = "ES256K")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(UnauthorizedHttpResult), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public Results<UnauthorizedHttpResult, NotFound<string>, Ok> RequestBattle(
string txId,
int logId,
int seasonId
public async Task<Results<UnauthorizedHttpResult, NotFound<string>, Ok>> RequestBattle(
[FromBody] string txId,
[FromBody] int battleLogId
)
{
_jobClient.Enqueue<BattleTaskProcessor>(processor => processor.ProcessAsync(txId, logId));
var battleLog = await _battleLogRepo.GetBattleLogAsync(battleLogId);

if (battleLog is null)
{
return TypedResults.NotFound($"Battle log with ID {battleLogId} not found.");
}

_jobClient.Enqueue<BattleTaskProcessor>(processor =>
processor.ProcessAsync(txId, battleLogId)
);

return TypedResults.Ok();
}

[HttpGet("{battleLogId}")]
[Authorize(Roles = "User", AuthenticationSchemes = "ES256K")]
[ProducesResponseType(typeof(BattleLogResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(UnauthorizedHttpResult), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(string), StatusCodes.Status404NotFound)]
public async Task<
Results<UnauthorizedHttpResult, NotFound<string>, Ok<BattleLogResponse>>
> GetBattleLog(int battleLogId, int seasonId)
> GetBattleLog(int battleLogId)
{
var battleLog = await _battleLogRepo.GetBattleLogAsync(battleLogId);

if (battleLog is null)
{
return TypedResults.NotFound("Not battleLog.");
return TypedResults.NotFound($"Battle log with ID {battleLogId} not found.");
}

return TypedResults.Ok(battleLog.ToResponse());
Expand Down
18 changes: 2 additions & 16 deletions ArenaService/Controllers/LeaderboardController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ IRankingRepository rankingRepository
_rankingRepository = rankingRepository;
}

// [HttpGet]
// public async Task<Results<NotFound<string>, Ok<AvailableOpponentsResponse>>> GetLeaderboard(
// int seasonId,
// int offset,
// int limit
// )
// {
// var leaderboard = await _leaderboardRepository.GetLeaderboard(seasonId, offset, limit);
// }

[HttpGet("participants/{avatarAddress}")]
[ProducesResponseType(typeof(LeaderboardEntryResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
Expand All @@ -52,16 +42,12 @@ string avatarAddress
return TypedResults.NotFound("Not participant user.");
}

var rankingKey = $"ranking:season:{seasonId}";

var rank = await _rankingRepository.GetRankAsync(
rankingKey,
participant.AvatarAddress,
new Address(participant.AvatarAddress),
seasonId
);
var score = await _rankingRepository.GetScoreAsync(
rankingKey,
participant.AvatarAddress,
new Address(participant.AvatarAddress),
seasonId
);

Expand Down
9 changes: 9 additions & 0 deletions ArenaService/Controllers/SeasonController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,13 @@ int blockIndex

return TypedResults.Ok(season?.ToResponse());
}

[HttpGet]
[ProducesResponseType(typeof(List<SeasonResponse>), StatusCodes.Status200OK)]
public async Task<Ok<List<SeasonResponse>>> GetSeasonByBlock()
{
var seasons = await _seasonRepo.GetAllSeasonsAsync();

return TypedResults.Ok(seasons.Select(s => s.ToResponse()).ToList());
}
}
2 changes: 2 additions & 0 deletions ArenaService/Dtos/BattleLogResponse.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using ArenaService.Constants;
using ArenaService.Models;

namespace ArenaService.Dtos;
Expand All @@ -11,6 +12,7 @@ public class BattleLogResponse

public required string DefenderAvatarAddress { get; set; }

public BattleStatus BattleStatus { get; set; }
public string? TxId { get; set; }
public string? TxStatus { get; set; }
public bool? IsVictory { get; set; }
Expand Down
6 changes: 6 additions & 0 deletions ArenaService/Extensions/BattleLogExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace ArenaService.Extensions;

using ArenaService.Constants;
using ArenaService.Dtos;
using ArenaService.Models;

Expand All @@ -14,6 +15,11 @@ public static BattleLogResponse ToResponse(this BattleLog battleLog)
AttackerAvatarAddress = battleLog.AttackerAvatarAddress,
DefenderAvatarAddress = battleLog.DefenderAvatarAddress,
TxId = battleLog.TxId,
BattleStatus = battleLog.TxStatus is null
? BattleStatus.PENDING
: battleLog.IsVictory is null
? BattleStatus.TRACKING
: BattleStatus.COMPLETED,
TxStatus = battleLog.TxStatus.ToString(),
IsVictory = battleLog.IsVictory,
ParticipantScore = battleLog.Attacker.Score,
Expand Down
1 change: 1 addition & 0 deletions ArenaService/Extensions/TxStatusExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace ArenaService.Extensions;

using ArenaService.Constants;
using ArenaService.Models;

public static class TxStatusExtensions
Expand Down
10 changes: 1 addition & 9 deletions ArenaService/Models/BattleLog.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using ArenaService.Constants;

namespace ArenaService.Models;

public enum TxStatus
{
INVALID,
STAGING,
SUCCESS,
FAILURE,
INCLUDED,
}

[Table("battle_logs")]
public class BattleLog
{
Expand Down
Loading

0 comments on commit 4d30d89

Please sign in to comment.