Skip to content

Commit

Permalink
Merge next -> main (#221)
Browse files Browse the repository at this point in the history
* Fix NodaTime Serialization (#216)

* Add NodaTime and related packages.

* Configure NodaTime serialization.

* Remove RequiredNotNullableSchemaFilter.

* Update openapi.json.

* Configure JsonSerializerOptions for tests.

* Fix Instant example values.

* Choose an example ZonedDateTime.

* Update Models (#217)

* Configure NodaTime serialization.

* Update openapi.json.

* Add CreatedAd, DeletedAt to run models.

* Add migrations for new Run properties.

* Add Run Info.

* Rename Leaderboard Rules to Info.

* Update Leaderboard comments.

* Fix RunViewModel formatting.

* Require all timestamps for RunViewModel.

* Add timestamp properties to Leaderboard.

* Add LB timestamps to DB schema.

* Rename Category Rules to Info.

* Remove unnecessary LB request defaults.

* Add Info to CreateLeaderboardRequest.

* Remove unnecessary default value.

* MAke CreateCategoryRequest.Info required.

* Use `required` keyword instead of attribute.

* Remove unnecessary JsonIgnore attribute.

* Include Info in Category equality check.

* Remove min and max player counts.

* Add timestamps to Category.

* Use Info when comparing Leaderboards.

* Add Category Sort Direction.

* Add EFCore.CheckConstraints package.

* Enable constraints.

* Remove Required attribute.

* Add DB constraints to LeaderBoard slugs.

* Fix bad Leaderboard slug regex.

* Use realistic test data.

* Add constraints to Category Slug.

* Add RunType enum.

* Add Category Type.

* Update Leaderboard GetHashCode()

* Update Category GetHashCode().

* Enable swagger inheriance and polymorphism.

* Add Run TimeOrScore.

* Use the default discriminator property name.

* Allow instantiation of RunViewModel.

* Load related Categories for Runs.

* Add computed Type property.

* Add explicit discriminator property to RunViewModel.

* Add computer Time property to Run.

* Use the default discriminator name.

* Add uniqueness constraint to Leaderboard Slug.

* Fix bad rebase.

* Make a Run belong to a User.

* Update openapi.json.

* Remove unnecessary JsonConverter Attribute.

* Add CreatedAt timestamp to User.

* Update registration tests.

* Add Type to CreateCategoryRequest.

* Add validation to CreateCategoryRequest.

* Create reusable slug validation.

* Create a unique index on Category Slug.

* Add CreateLeaderboardRequest Validation.

* Update CreateRunRequest.

* Remove unnecessary imports.

* Add a setter for Run Time.

* Disallow URL encoded characters in slugs.
  • Loading branch information
TheTedder authored Aug 11, 2024
1 parent 3b6433c commit b08efce
Show file tree
Hide file tree
Showing 68 changed files with 7,848 additions and 404 deletions.
16 changes: 11 additions & 5 deletions LeaderboardBackend.Test/Categories.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Net;
using System.Threading.Tasks;
using LeaderboardBackend.Models;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Models.Requests;
using LeaderboardBackend.Models.ViewModels;
using LeaderboardBackend.Test.Lib;
Expand Down Expand Up @@ -55,8 +57,9 @@ public static async Task CreateCategory_GetCategory_OK()
{
Body = new CreateLeaderboardRequest()
{
Name = Generators.GenerateRandomString(),
Slug = Generators.GenerateRandomString()
Name = "Super Mario Bros.",
Slug = "super_mario_bros",
Info = null
},
Jwt = _jwt
}
Expand All @@ -68,9 +71,12 @@ public static async Task CreateCategory_GetCategory_OK()
{
Body = new CreateCategoryRequest()
{
Name = Generators.GenerateRandomString(),
Slug = Generators.GenerateRandomString(),
LeaderboardId = createdLeaderboard.Id
Name = "1 Player",
Slug = "1_player",
LeaderboardId = createdLeaderboard.Id,
Info = null,
SortDirection = SortDirection.Ascending,
Type = RunType.Time
},
Jwt = _jwt
}
Expand Down
6 changes: 4 additions & 2 deletions LeaderboardBackend.Test/Features/Users/RegistrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using LeaderboardBackend.Models.ViewModels;
using LeaderboardBackend.Services;
using LeaderboardBackend.Test.Fixtures;
using LeaderboardBackend.Test.Lib;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -45,12 +46,13 @@ public async Task Register_ValidRequest_CreatesAndReturnsUser()
HttpResponseMessage res = await client.PostAsJsonAsync(Routes.REGISTER, request);

res.Should().HaveStatusCode(HttpStatusCode.Created);
UserViewModel? content = await res.Content.ReadFromJsonAsync<UserViewModel>();
UserViewModel? content = await res.Content.ReadFromJsonAsync<UserViewModel>(TestInitCommonFields.JsonSerializerOptions);
content.Should().NotBeNull().And.BeEquivalentTo(new UserViewModel
{
Id = content!.Id,
Username = request.Username,
Role = UserRole.Registered
Role = UserRole.Registered,
CreatedAt = Instant.FromUnixTimeSeconds(1)
});
emailSenderMock.Verify(x =>
x.EnqueueEmailAsync(
Expand Down
15 changes: 12 additions & 3 deletions LeaderboardBackend.Test/Lib/TestInitCommonFields.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using LeaderboardBackend.Models.Entities;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;

namespace LeaderboardBackend.Test.Lib;

internal record TestInitCommonFields
{
public static JsonSerializerOptions JsonSerializerOptions { get; } =
new()
public static JsonSerializerOptions JsonSerializerOptions { get; private set; }

static TestInitCommonFields()
{
JsonSerializerOptions = new(JsonSerializerDefaults.Web)
{
ReferenceHandler = ReferenceHandler.IgnoreCycles,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
}

public static User Admin { get; } =
new()
{
Expand Down
17 changes: 12 additions & 5 deletions LeaderboardBackend.Test/Runs.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Threading.Tasks;
using LeaderboardBackend.Models;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Models.Requests;
using LeaderboardBackend.Models.ViewModels;
using LeaderboardBackend.Test.Lib;
Expand Down Expand Up @@ -38,8 +40,9 @@ public async Task SetUp()
{
Body = new CreateLeaderboardRequest()
{
Name = Generators.GenerateRandomString(),
Slug = Generators.GenerateRandomString(),
Name = "Super Mario 64",
Slug = "super_mario_64",
Info = null
},
Jwt = _jwt,
}
Expand All @@ -51,9 +54,12 @@ public async Task SetUp()
{
Body = new CreateCategoryRequest()
{
Name = Generators.GenerateRandomString(),
Slug = Generators.GenerateRandomString(),
Name = "120 Stars",
Slug = "120_stars",
LeaderboardId = createdLeaderboard.Id,
Info = null,
SortDirection = SortDirection.Ascending,
Type = RunType.Time
},
Jwt = _jwt,
}
Expand Down Expand Up @@ -96,7 +102,8 @@ private static async Task<RunViewModel> CreateRun()
Body = new CreateRunRequest
{
PlayedOn = LocalDate.MinIsoValue,
SubmittedAt = Instant.MaxValue,
Info = null,
TimeOrScore = Duration.FromHours(2).ToInt64Nanoseconds(),
CategoryId = _categoryId
},
Jwt = _jwt
Expand Down
3 changes: 1 addition & 2 deletions LeaderboardBackend/Controllers/CategoriesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,12 @@ public async Task<ActionResult<CategoryViewModel>> CreateCategory(
[FromBody] CreateCategoryRequest request
)
{
// NOTE: Check that body.PlayersMax > body.PlayersMin? - Ero
Category category =
new()
{
Name = request.Name,
Slug = request.Slug,
Rules = request.Rules,
Info = request.Info,
LeaderboardId = request.LeaderboardId,
};

Expand Down
2 changes: 1 addition & 1 deletion LeaderboardBackend/Controllers/LeaderboardsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public async Task<ActionResult<LeaderboardViewModel>> CreateLeaderboard(
[FromBody] CreateLeaderboardRequest request
)
{
Leaderboard leaderboard = new() { Name = request.Name, Slug = request.Slug };
Leaderboard leaderboard = new() { Name = request.Name, Slug = request.Slug, Info = request.Info };

await _leaderboardService.CreateLeaderboard(leaderboard);

Expand Down
43 changes: 21 additions & 22 deletions LeaderboardBackend/Controllers/RunsController.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,29 @@
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Models.Requests;
using LeaderboardBackend.Models.ViewModels;
using LeaderboardBackend.Result;
using LeaderboardBackend.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using OneOf;
using Swashbuckle.AspNetCore.Annotations;

namespace LeaderboardBackend.Controllers;

public class RunsController : ApiController
public class RunsController(
IRunService runService,
ICategoryService categoryService,
IUserService userService
) : ApiController
{
private readonly IRunService _runService;
private readonly ICategoryService _categoryService;

public RunsController(
IRunService runService,
ICategoryService categoryService
)
{
_runService = runService;
_categoryService = categoryService;
}

[AllowAnonymous]
[HttpGet("{id}")]
[SwaggerOperation("Gets a Run by its ID.")]
[SwaggerResponse(200)]
[SwaggerResponse(404)]
public async Task<ActionResult<RunViewModel>> GetRun(Guid id)
{
Run? run = await _runService.GetRun(id);
Run? run = await runService.GetRun(id);

if (run is null)
{
Expand All @@ -51,32 +45,37 @@ public async Task<ActionResult> CreateRun([FromBody] CreateRunRequest request)
// FIXME: Should return Task<ActionResult<Run>>! - Ero
// NOTE: Return NotFound for anything in here? - Ero

Run run =
new()
GetUserResult res = await userService.GetUserFromClaims(HttpContext.User);

if (res.TryPickT0(out User user, out OneOf<BadCredentials, UserNotFound> _))
{
Run run = new()
{
PlayedOn = request.PlayedOn,
SubmittedAt = request.SubmittedAt,
CategoryId = request.CategoryId
CategoryId = request.CategoryId,
User = user,
};

await _runService.CreateRun(run);
await runService.CreateRun(run);
return CreatedAtAction(nameof(GetRun), new { id = run.Id }, RunViewModel.MapFrom(run));
}

return CreatedAtAction(nameof(GetRun), new { id = run.Id }, RunViewModel.MapFrom(run));
return Unauthorized();
}

[HttpGet("{id}/category")]
[SwaggerResponse(200)]
[SwaggerResponse(404)]
public async Task<ActionResult<CategoryViewModel>> GetCategoryForRun(Guid id)
{
Run? run = await _runService.GetRun(id);
Run? run = await runService.GetRun(id);

if (run is null)
{
return NotFound("Run not found");
}

Category? category = await _categoryService.GetCategoryForRun(run);
Category? category = await categoryService.GetCategoryForRun(run);

if (category is null)
{
Expand Down
4 changes: 4 additions & 0 deletions LeaderboardBackend/LeaderboardBackend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="brevo_csharp" Version="1.0.0" />
<PackageReference Include="DotNetEnv" Version="3.0.0" />
<PackageReference Include="EFCore.CheckConstraints" Version="8.0.1" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="FluentValidation" Version="11.9.2" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="MailKit" Version="4.6.0" />
<PackageReference Include="MicroElements.Swashbuckle.NodaTime" Version="4.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
Expand All @@ -40,6 +42,8 @@
<PackageReference Include="Microsoft.FeatureManagement.AspNetCore" Version="3.4.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.17.2" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
<PackageReference Include="nodatime" Version="3.1.11" />
<PackageReference Include="NodaTime.Serialization.SystemTextJson" Version="1.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="8.0.4" />
<PackageReference Include="OneOf" Version="3.0.271" />
Expand Down
Loading

0 comments on commit b08efce

Please sign in to comment.