Skip to content

Commit

Permalink
Fix NodaTime Serialization (#216)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
TheTedder authored Jul 21, 2024
1 parent be5d650 commit eaabe27
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 178 deletions.
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
3 changes: 3 additions & 0 deletions LeaderboardBackend/LeaderboardBackend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<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 @@ -39,6 +40,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
4 changes: 2 additions & 2 deletions LeaderboardBackend/Models/Requests/LeaderboardRequests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ public record CreateLeaderboardRequest
/// The display name of the `Leaderboard` to create.
/// </summary>
/// <example>Foo Bar</example>
public string Name { get; set; } = null!;
public required string Name { get; set; } = null!;

/// <summary>
/// The URL-scoped unique identifier of the `Leaderboard`.<br/>
/// Must be [2, 80] in length and consist only of alphanumeric characters and hyphens.
/// </summary>
/// <example>foo-bar</example>
public string Slug { get; set; } = null!;
public required string Slug { get; set; } = null!;
}
13 changes: 11 additions & 2 deletions LeaderboardBackend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +24,7 @@
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NodaTime;
using NodaTime.Serialization.SystemTextJson;
using Npgsql;

#region WebApplicationBuilder
Expand Down Expand Up @@ -133,6 +134,8 @@
});
}

JsonSerializerOptions jsonSerializerOptions = new();

// Add controllers to the container.
builder.Services
.AddControllers(opt =>
Expand All @@ -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
Expand Down Expand Up @@ -189,8 +194,12 @@
);
c.SupportNonNullableReferenceTypes();
c.SchemaFilter<RequiredNotNullableSchemaFilter>();
c.MapType<Guid>(() => 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.
Expand Down
50 changes: 0 additions & 50 deletions LeaderboardBackend/Swagger/RequiredNotNullableSchemaFilter.cs

This file was deleted.

133 changes: 12 additions & 121 deletions LeaderboardBackend/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -1340,9 +1234,6 @@
"additionalProperties": false
},
"ValidationProblemDetails": {
"required": [
"errors"
],
"type": "object",
"properties": {
"type": {
Expand Down

0 comments on commit eaabe27

Please sign in to comment.