diff --git a/LeaderboardBackend.Test/Lib/TestInitCommonFields.cs b/LeaderboardBackend.Test/Lib/TestInitCommonFields.cs
index 07d51fe0..ead004fd 100644
--- a/LeaderboardBackend.Test/Lib/TestInitCommonFields.cs
+++ b/LeaderboardBackend.Test/Lib/TestInitCommonFields.cs
@@ -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()
{
diff --git a/LeaderboardBackend/LeaderboardBackend.csproj b/LeaderboardBackend/LeaderboardBackend.csproj
index 874ae58c..88b99e4c 100644
--- a/LeaderboardBackend/LeaderboardBackend.csproj
+++ b/LeaderboardBackend/LeaderboardBackend.csproj
@@ -29,6 +29,7 @@
+
@@ -39,6 +40,8 @@
+
+
diff --git a/LeaderboardBackend/Models/Requests/LeaderboardRequests.cs b/LeaderboardBackend/Models/Requests/LeaderboardRequests.cs
index 574de82c..51ed9f14 100644
--- a/LeaderboardBackend/Models/Requests/LeaderboardRequests.cs
+++ b/LeaderboardBackend/Models/Requests/LeaderboardRequests.cs
@@ -9,12 +9,12 @@ public record CreateLeaderboardRequest
/// The display name of the `Leaderboard` to create.
///
/// Foo Bar
- public string Name { get; set; } = null!;
+ public required string Name { get; set; } = null!;
///
/// The URL-scoped unique identifier of the `Leaderboard`.
/// Must be [2, 80] in length and consist only of alphanumeric characters and hyphens.
///
/// foo-bar
- public string Slug { get; set; } = null!;
+ public required string Slug { get; set; } = null!;
}
diff --git a/LeaderboardBackend/Program.cs b/LeaderboardBackend/Program.cs
index ac166ffe..ff63aaac 100644
--- a/LeaderboardBackend/Program.cs
+++ b/LeaderboardBackend/Program.cs
@@ -11,8 +11,8 @@
using LeaderboardBackend.Authorization;
using LeaderboardBackend.Models.Entities;
using LeaderboardBackend.Services;
-using LeaderboardBackend.Swagger;
using MailKit.Net.Smtp;
+using MicroElements.Swashbuckle.NodaTime;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -24,6 +24,7 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NodaTime;
+using NodaTime.Serialization.SystemTextJson;
using Npgsql;
#region WebApplicationBuilder
@@ -133,6 +134,8 @@
});
}
+JsonSerializerOptions jsonSerializerOptions = new();
+
// Add controllers to the container.
builder.Services
.AddControllers(opt =>
@@ -146,6 +149,8 @@
opt.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
opt.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
+ opt.JsonSerializerOptions.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
+ jsonSerializerOptions = opt.JsonSerializerOptions;
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
@@ -189,8 +194,12 @@
);
c.SupportNonNullableReferenceTypes();
- c.SchemaFilter();
c.MapType(() => new OpenApiSchema { Type = "string", Pattern = "^[a-zA-Z0-9-_]{22}$" });
+ c.ConfigureForNodaTimeWithSystemTextJson(jsonSerializerOptions, null, null, true, new(DateTimeZoneProviders.Tzdb)
+ {
+ Instant = Instant.FromUtc(1984, 1, 1, 0, 0),
+ ZonedDateTime = ZonedDateTime.FromDateTimeOffset(new(new DateTime(2000, 1, 1)))
+ });
});
// Configure JWT Authentication.
diff --git a/LeaderboardBackend/Swagger/RequiredNotNullableSchemaFilter.cs b/LeaderboardBackend/Swagger/RequiredNotNullableSchemaFilter.cs
deleted file mode 100644
index f580bace..00000000
--- a/LeaderboardBackend/Swagger/RequiredNotNullableSchemaFilter.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.Reflection;
-using System.Text.Json.Serialization;
-using Microsoft.OpenApi.Models;
-using Swashbuckle.AspNetCore.SwaggerGen;
-
-namespace LeaderboardBackend.Swagger;
-
-// https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2036#issuecomment-894015122
-internal class RequiredNotNullableSchemaFilter : ISchemaFilter
-{
- public void Apply(OpenApiSchema schema, SchemaFilterContext context)
- {
- if (schema.Properties is null)
- {
- return;
- }
-
- foreach ((string propertyName, OpenApiSchema property) in schema.Properties)
- {
- if (property.Reference != null)
- {
-
- MemberInfo? field = context.Type
- .GetMembers(BindingFlags.Public | BindingFlags.Instance)
- .FirstOrDefault(x => string.Equals(x.Name, propertyName, StringComparison.InvariantCultureIgnoreCase));
-
- if (field == null)
- {
- continue;
- }
-
- Type fieldType = field switch
- {
- FieldInfo fieldInfo => fieldInfo.FieldType,
- PropertyInfo propertyInfo => propertyInfo.PropertyType,
- _ => throw new NotSupportedException(),
- };
-
- property.Nullable = fieldType.IsValueType
- ? Nullable.GetUnderlyingType(fieldType) != null // is not a Nullable<> type
- : !field.IsNonNullableReferenceType();
- }
-
- if (!property.Nullable)
- {
- schema.Required.Add(propertyName);
- }
- }
- }
-}
diff --git a/LeaderboardBackend/openapi.json b/LeaderboardBackend/openapi.json
index 83d7a44f..61aa1037 100644
--- a/LeaderboardBackend/openapi.json
+++ b/LeaderboardBackend/openapi.json
@@ -886,44 +886,6 @@
},
"components": {
"schemas": {
- "CalendarSystem": {
- "required": [
- "eras",
- "id",
- "maxYear",
- "minYear",
- "name"
- ],
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "readOnly": true
- },
- "name": {
- "type": "string",
- "readOnly": true
- },
- "minYear": {
- "type": "integer",
- "format": "int32",
- "readOnly": true
- },
- "maxYear": {
- "type": "integer",
- "format": "int32",
- "readOnly": true
- },
- "eras": {
- "type": "array",
- "items": {
- "$ref": "#/components/schemas/Era"
- },
- "readOnly": true
- }
- },
- "additionalProperties": false
- },
"CategoryViewModel": {
"required": [
"id",
@@ -1035,10 +997,16 @@
"type": "object",
"properties": {
"playedOn": {
- "$ref": "#/components/schemas/LocalDate"
+ "type": "string",
+ "description": "The date the `Run` was played on.",
+ "format": "date",
+ "example": "2000-01-01"
},
"submittedAt": {
- "$ref": "#/components/schemas/Instant"
+ "type": "string",
+ "description": "The time the request was made at.",
+ "format": "date-time",
+ "example": "1984-01-01T00:00:00Z"
},
"categoryId": {
"type": "integer",
@@ -1049,36 +1017,6 @@
"additionalProperties": false,
"description": "This request object is sent when creating a `Run`."
},
- "Era": {
- "required": [
- "name"
- ],
- "type": "object",
- "properties": {
- "name": {
- "type": "string",
- "readOnly": true
- }
- },
- "additionalProperties": false
- },
- "Instant": {
- "type": "object",
- "additionalProperties": false
- },
- "IsoDayOfWeek": {
- "enum": [
- "None",
- "Monday",
- "Tuesday",
- "Wednesday",
- "Thursday",
- "Friday",
- "Saturday",
- "Sunday"
- ],
- "type": "string"
- },
"LeaderboardViewModel": {
"required": [
"categories",
@@ -1121,53 +1059,6 @@
"additionalProperties": false,
"description": "Represents a collection of `Leaderboard` entities."
},
- "LocalDate": {
- "required": [
- "calendar",
- "day",
- "dayOfWeek",
- "dayOfYear",
- "era",
- "month",
- "year",
- "yearOfEra"
- ],
- "type": "object",
- "properties": {
- "calendar": {
- "$ref": "#/components/schemas/CalendarSystem"
- },
- "year": {
- "type": "integer",
- "format": "int32"
- },
- "month": {
- "type": "integer",
- "format": "int32"
- },
- "day": {
- "type": "integer",
- "format": "int32"
- },
- "dayOfWeek": {
- "$ref": "#/components/schemas/IsoDayOfWeek"
- },
- "yearOfEra": {
- "type": "integer",
- "format": "int32",
- "readOnly": true
- },
- "era": {
- "$ref": "#/components/schemas/Era"
- },
- "dayOfYear": {
- "type": "integer",
- "format": "int32",
- "readOnly": true
- }
- },
- "additionalProperties": false
- },
"LoginRequest": {
"required": [
"email",
@@ -1296,7 +1187,10 @@
"description": "The unique identifier of the `Run`.\n\r\nGenerated on creation."
},
"submittedAt": {
- "$ref": "#/components/schemas/Instant"
+ "type": "string",
+ "description": "The time the request was made at.",
+ "format": "date-time",
+ "example": "1984-01-01T00:00:00Z"
},
"categoryId": {
"type": "integer",
@@ -1340,9 +1234,6 @@
"additionalProperties": false
},
"ValidationProblemDetails": {
- "required": [
- "errors"
- ],
"type": "object",
"properties": {
"type": {