From 3d0eb99490e8a208a2005d35b219da4fdc55a7fe Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 12:38:55 -0400
Subject: [PATCH 1/7] Add NodaTime and related packages.
---
LeaderboardBackend/LeaderboardBackend.csproj | 3 +++
1 file changed, 3 insertions(+)
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 @@
+
+
From 990a3a1d66bd26010c1a7ecd4b2857d0133dda52 Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 12:39:16 -0400
Subject: [PATCH 2/7] Configure NodaTime serialization.
---
LeaderboardBackend/Program.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/LeaderboardBackend/Program.cs b/LeaderboardBackend/Program.cs
index ac166ffe..19f0249c 100644
--- a/LeaderboardBackend/Program.cs
+++ b/LeaderboardBackend/Program.cs
@@ -13,6 +13,7 @@
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 +25,7 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NodaTime;
+using NodaTime.Serialization.SystemTextJson;
using Npgsql;
#region WebApplicationBuilder
@@ -133,6 +135,8 @@
});
}
+JsonSerializerOptions jsonSerializerOptions = new();
+
// Add controllers to the container.
builder.Services
.AddControllers(opt =>
@@ -146,6 +150,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
@@ -191,6 +197,7 @@
c.SupportNonNullableReferenceTypes();
c.SchemaFilter();
c.MapType(() => new OpenApiSchema { Type = "string", Pattern = "^[a-zA-Z0-9-_]{22}$" });
+ c.ConfigureForNodaTimeWithSystemTextJson(jsonSerializerOptions);
});
// Configure JWT Authentication.
From 8379d2af81a209734d8a5c9efb0e2cfef3c7e1b9 Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 12:43:28 -0400
Subject: [PATCH 3/7] Remove RequiredNotNullableSchemaFilter.
---
.../Models/Requests/LeaderboardRequests.cs | 4 +-
LeaderboardBackend/Program.cs | 2 -
.../RequiredNotNullableSchemaFilter.cs | 50 -------------------
3 files changed, 2 insertions(+), 54 deletions(-)
delete mode 100644 LeaderboardBackend/Swagger/RequiredNotNullableSchemaFilter.cs
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 19f0249c..3d38e70e 100644
--- a/LeaderboardBackend/Program.cs
+++ b/LeaderboardBackend/Program.cs
@@ -11,7 +11,6 @@
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;
@@ -195,7 +194,6 @@
);
c.SupportNonNullableReferenceTypes();
- c.SchemaFilter();
c.MapType(() => new OpenApiSchema { Type = "string", Pattern = "^[a-zA-Z0-9-_]{22}$" });
c.ConfigureForNodaTimeWithSystemTextJson(jsonSerializerOptions);
});
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);
- }
- }
- }
-}
From 1c03707c9afab7864b83302350f84e02d1a416ed Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 12:43:49 -0400
Subject: [PATCH 4/7] Update openapi.json.
---
LeaderboardBackend/openapi.json | 133 +++-----------------------------
1 file changed, 12 insertions(+), 121 deletions(-)
diff --git a/LeaderboardBackend/openapi.json b/LeaderboardBackend/openapi.json
index 83d7a44f..67341ae2 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": "2024-07-18"
},
"submittedAt": {
- "$ref": "#/components/schemas/Instant"
+ "type": "string",
+ "description": "The time the request was made at.",
+ "format": "date-time",
+ "example": "2024-07-18T16:42:35.6691199Z"
},
"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": "2024-07-18T16:42:35.6691199Z"
},
"categoryId": {
"type": "integer",
@@ -1340,9 +1234,6 @@
"additionalProperties": false
},
"ValidationProblemDetails": {
- "required": [
- "errors"
- ],
"type": "object",
"properties": {
"type": {
From 8c12b2012bc86fcf50acb1b4555f5fcbbfdbf031 Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 12:55:49 -0400
Subject: [PATCH 5/7] Configure JsonSerializerOptions for tests.
---
.../Lib/TestInitCommonFields.cs | 15 ++++++++++++---
1 file changed, 12 insertions(+), 3 deletions(-)
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()
{
From 6c499392f114c19206fa70f72a8d0e81c208d1a8 Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Thu, 18 Jul 2024 13:19:03 -0400
Subject: [PATCH 6/7] Fix Instant example values.
---
LeaderboardBackend/Program.cs | 5 ++++-
LeaderboardBackend/openapi.json | 4 ++--
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/LeaderboardBackend/Program.cs b/LeaderboardBackend/Program.cs
index 3d38e70e..598180bb 100644
--- a/LeaderboardBackend/Program.cs
+++ b/LeaderboardBackend/Program.cs
@@ -195,7 +195,10 @@
c.SupportNonNullableReferenceTypes();
c.MapType(() => new OpenApiSchema { Type = "string", Pattern = "^[a-zA-Z0-9-_]{22}$" });
- c.ConfigureForNodaTimeWithSystemTextJson(jsonSerializerOptions);
+ c.ConfigureForNodaTimeWithSystemTextJson(jsonSerializerOptions, null, null, true, new(DateTimeZoneProviders.Tzdb)
+ {
+ Instant = Instant.FromUtc(1984, 1, 1, 0, 0),
+ });
});
// Configure JWT Authentication.
diff --git a/LeaderboardBackend/openapi.json b/LeaderboardBackend/openapi.json
index 67341ae2..952b7f1c 100644
--- a/LeaderboardBackend/openapi.json
+++ b/LeaderboardBackend/openapi.json
@@ -1006,7 +1006,7 @@
"type": "string",
"description": "The time the request was made at.",
"format": "date-time",
- "example": "2024-07-18T16:42:35.6691199Z"
+ "example": "1984-01-01T00:00:00Z"
},
"categoryId": {
"type": "integer",
@@ -1190,7 +1190,7 @@
"type": "string",
"description": "The time the request was made at.",
"format": "date-time",
- "example": "2024-07-18T16:42:35.6691199Z"
+ "example": "1984-01-01T00:00:00Z"
},
"categoryId": {
"type": "integer",
From b91965c42408bd24aa9fbd43956921deaf3c60d9 Mon Sep 17 00:00:00 2001
From: Ted Wollman <25165500+TheTedder@users.noreply.github.com>
Date: Sat, 20 Jul 2024 14:57:40 -0400
Subject: [PATCH 7/7] Choose an example ZonedDateTime.
---
LeaderboardBackend/Program.cs | 1 +
LeaderboardBackend/openapi.json | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/LeaderboardBackend/Program.cs b/LeaderboardBackend/Program.cs
index 598180bb..ff63aaac 100644
--- a/LeaderboardBackend/Program.cs
+++ b/LeaderboardBackend/Program.cs
@@ -198,6 +198,7 @@
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)))
});
});
diff --git a/LeaderboardBackend/openapi.json b/LeaderboardBackend/openapi.json
index 952b7f1c..61aa1037 100644
--- a/LeaderboardBackend/openapi.json
+++ b/LeaderboardBackend/openapi.json
@@ -1000,7 +1000,7 @@
"type": "string",
"description": "The date the `Run` was played on.",
"format": "date",
- "example": "2024-07-18"
+ "example": "2000-01-01"
},
"submittedAt": {
"type": "string",