From 8c494bee5c86e9b353ed6fd8eb307df7185fc5ea Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 5 May 2026 12:10:03 +0200 Subject: [PATCH 1/2] Remove usage of `required` keyword in resource properties --- test/OpenApiTests/AttributeTypes/TypeContainer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/OpenApiTests/AttributeTypes/TypeContainer.cs b/test/OpenApiTests/AttributeTypes/TypeContainer.cs index 6e18f8fc60..b3496f988c 100644 --- a/test/OpenApiTests/AttributeTypes/TypeContainer.cs +++ b/test/OpenApiTests/AttributeTypes/TypeContainer.cs @@ -128,7 +128,7 @@ public sealed class TypeContainer : Identifiable public char? TestNullableChar { get; set; } [Attr] - public required string TestString { get; set; } + public string TestString { get; set; } = null!; [Attr] public string? TestNullableString { get; set; } @@ -186,13 +186,13 @@ public sealed class TypeContainer : Identifiable public Guid? TestNullableGuid { get; set; } [Attr] - public required Uri TestUri { get; set; } + public Uri TestUri { get; set; } = null!; [Attr] public Uri? TestNullableUri { get; set; } [Attr] - public required IPAddress TestIPAddress { get; set; } + public IPAddress TestIPAddress { get; set; } = null!; [Attr] public IPAddress? TestNullableIPAddress { get; set; } @@ -204,7 +204,7 @@ public sealed class TypeContainer : Identifiable public IPNetwork? TestNullableIPNetwork { get; set; } [Attr] - public required Version TestVersion { get; set; } + public Version TestVersion { get; set; } = null!; [Attr] public Version? TestNullableVersion { get; set; } From 263a848523b6911e78564923145925d91399f318 Mon Sep 17 00:00:00 2001 From: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Date: Tue, 5 May 2026 13:42:40 +0200 Subject: [PATCH 2/2] Add OpenAPI tests for the presence of meta --- test/OpenApiTests/Meta/JigsawPuzzle.cs | 22 ++++ test/OpenApiTests/Meta/JigsawPuzzlePicture.cs | 13 +++ test/OpenApiTests/Meta/JigsawPuzzlePiece.cs | 16 +++ test/OpenApiTests/Meta/MetaDbContext.cs | 12 ++ test/OpenApiTests/Meta/MetaTests.cs | 103 ++++++++++++++++++ .../OpenApiTests/Meta/OperationsController.cs | 13 +++ test/OpenApiTests/OpenApiTests.csproj | 2 +- 7 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 test/OpenApiTests/Meta/JigsawPuzzle.cs create mode 100644 test/OpenApiTests/Meta/JigsawPuzzlePicture.cs create mode 100644 test/OpenApiTests/Meta/JigsawPuzzlePiece.cs create mode 100644 test/OpenApiTests/Meta/MetaDbContext.cs create mode 100644 test/OpenApiTests/Meta/MetaTests.cs create mode 100644 test/OpenApiTests/Meta/OperationsController.cs diff --git a/test/OpenApiTests/Meta/JigsawPuzzle.cs b/test/OpenApiTests/Meta/JigsawPuzzle.cs new file mode 100644 index 0000000000..63e419dccd --- /dev/null +++ b/test/OpenApiTests/Meta/JigsawPuzzle.cs @@ -0,0 +1,22 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.Meta; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.Meta")] +public sealed class JigsawPuzzle : Identifiable +{ + [Attr] + public string Title { get; set; } = null!; + + [HasOne] + public JigsawPuzzlePicture FrontPicture { get; set; } = null!; + + [HasOne] + public JigsawPuzzlePicture? BackPicture { get; set; } + + [HasMany] + public ISet Pieces { get; set; } = new HashSet(); +} diff --git a/test/OpenApiTests/Meta/JigsawPuzzlePicture.cs b/test/OpenApiTests/Meta/JigsawPuzzlePicture.cs new file mode 100644 index 0000000000..a6d8ba60bb --- /dev/null +++ b/test/OpenApiTests/Meta/JigsawPuzzlePicture.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.Meta; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.Meta")] +public sealed class JigsawPuzzlePicture : Identifiable +{ + [Attr] + public string ImageUrl { get; set; } = null!; +} diff --git a/test/OpenApiTests/Meta/JigsawPuzzlePiece.cs b/test/OpenApiTests/Meta/JigsawPuzzlePiece.cs new file mode 100644 index 0000000000..f369333929 --- /dev/null +++ b/test/OpenApiTests/Meta/JigsawPuzzlePiece.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using JsonApiDotNetCore.Resources; +using JsonApiDotNetCore.Resources.Annotations; + +namespace OpenApiTests.Meta; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +[Resource(ControllerNamespace = "OpenApiTests.Meta")] +public sealed class JigsawPuzzlePiece : Identifiable +{ + [Attr] + public string ImageUrl { get; set; } = null!; + + [HasOne] + public JigsawPuzzle Puzzle { get; set; } = null!; +} diff --git a/test/OpenApiTests/Meta/MetaDbContext.cs b/test/OpenApiTests/Meta/MetaDbContext.cs new file mode 100644 index 0000000000..776a06c9f2 --- /dev/null +++ b/test/OpenApiTests/Meta/MetaDbContext.cs @@ -0,0 +1,12 @@ +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using TestBuildingBlocks; + +namespace OpenApiTests.Meta; + +[UsedImplicitly(ImplicitUseTargetFlags.Members)] +public sealed class MetaDbContext(DbContextOptions options) + : TestableDbContext(options) +{ + public DbSet Puzzles => Set(); +} diff --git a/test/OpenApiTests/Meta/MetaTests.cs b/test/OpenApiTests/Meta/MetaTests.cs new file mode 100644 index 0000000000..8159fa521b --- /dev/null +++ b/test/OpenApiTests/Meta/MetaTests.cs @@ -0,0 +1,103 @@ +using System.Text.Json; +using JsonApiDotNetCore.Configuration; +using Microsoft.Extensions.DependencyInjection; +using TestBuildingBlocks; +using Xunit; +using Xunit.Abstractions; + +namespace OpenApiTests.Meta; + +public sealed class MetaTests : IClassFixture, MetaDbContext>> +{ + private readonly OpenApiTestContext, MetaDbContext> _testContext; + + public MetaTests(OpenApiTestContext, MetaDbContext> testContext, ITestOutputHelper testOutputHelper) + { + _testContext = testContext; + + testContext.UseController(); + testContext.UseController(); + testContext.UseController(); + testContext.UseController(); + + testContext.SetTestOutputHelper(testOutputHelper); + + var options = (JsonApiOptions)testContext.Factory.Services.GetRequiredService(); + options.IncludeJsonApiVersion = true; + } + + [Fact] + public async Task Includes_meta_definition() + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath("components.schemas.meta").With(metaElement => + { + metaElement.Should().BeJson(""" + { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + """); + }); + } + + [Theory] + [InlineData("atomicOperation")] + [InlineData("atomicResult")] + [InlineData("createJigsawPuzzlePictureRequestDocument")] + [InlineData("createJigsawPuzzlePieceRequestDocument")] + [InlineData("createJigsawPuzzleRequestDocument")] + [InlineData("errorObject")] + [InlineData("errorResponseDocument")] + [InlineData("identifierInRequest")] + [InlineData("jigsawPuzzleCollectionResponseDocument")] + [InlineData("jigsawPuzzleIdentifierInResponse")] + [InlineData("jigsawPuzzleIdentifierResponseDocument")] + [InlineData("jigsawPuzzlePictureCollectionResponseDocument")] + [InlineData("jigsawPuzzlePictureIdentifierInResponse")] + [InlineData("jigsawPuzzlePictureIdentifierResponseDocument")] + [InlineData("jigsawPuzzlePieceCollectionResponseDocument")] + [InlineData("jigsawPuzzlePieceIdentifierCollectionResponseDocument")] + [InlineData("jigsawPuzzlePieceIdentifierInResponse")] + [InlineData("jsonapi")] + [InlineData("nullableJigsawPuzzlePictureIdentifierResponseDocument")] + [InlineData("nullableSecondaryJigsawPuzzlePictureResponseDocument")] + [InlineData("nullableToOneJigsawPuzzlePictureInRequest")] + [InlineData("nullableToOneJigsawPuzzlePictureInResponse")] + [InlineData("operationsRequestDocument")] + [InlineData("operationsResponseDocument")] + [InlineData("primaryJigsawPuzzlePictureResponseDocument")] + [InlineData("primaryJigsawPuzzlePieceResponseDocument")] + [InlineData("primaryJigsawPuzzleResponseDocument")] + [InlineData("resourceInCreateRequest")] + [InlineData("resourceInResponse")] + [InlineData("resourceInUpdateRequest")] + [InlineData("secondaryJigsawPuzzlePictureResponseDocument")] + [InlineData("secondaryJigsawPuzzleResponseDocument")] + [InlineData("toManyJigsawPuzzlePieceInRequest")] + [InlineData("toManyJigsawPuzzlePieceInResponse")] + [InlineData("toOneJigsawPuzzleInRequest")] + [InlineData("toOneJigsawPuzzleInResponse")] + [InlineData("toOneJigsawPuzzlePictureInRequest")] + [InlineData("toOneJigsawPuzzlePictureInResponse")] + [InlineData("updateJigsawPuzzlePictureRequestDocument")] + [InlineData("updateJigsawPuzzlePieceRequestDocument")] + [InlineData("updateJigsawPuzzleRequestDocument")] + public async Task Includes_meta_property(string schemaId) + { + // Act + JsonElement document = await _testContext.GetSwaggerDocumentAsync(); + + // Assert + document.Should().ContainPath($"components.schemas.{schemaId}.properties").With(propertiesElement => + { + JsonElement metaElement = propertiesElement.Should().ContainPath("meta.allOf[0]"); + metaElement.Should().HaveProperty("$ref", "#/components/schemas/meta"); + }); + } +} diff --git a/test/OpenApiTests/Meta/OperationsController.cs b/test/OpenApiTests/Meta/OperationsController.cs new file mode 100644 index 0000000000..adae0e1309 --- /dev/null +++ b/test/OpenApiTests/Meta/OperationsController.cs @@ -0,0 +1,13 @@ +using JsonApiDotNetCore.AtomicOperations; +using JsonApiDotNetCore.Configuration; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Middleware; +using JsonApiDotNetCore.Resources; +using Microsoft.Extensions.Logging; + +namespace OpenApiTests.Meta; + +public sealed class OperationsController( + IJsonApiOptions options, IResourceGraph resourceGraph, ILoggerFactory loggerFactory, IOperationsProcessor processor, IJsonApiRequest request, + ITargetedFields targetedFields, IAtomicOperationFilter operationFilter) + : JsonApiOperationsController(options, resourceGraph, loggerFactory, processor, request, targetedFields, operationFilter); diff --git a/test/OpenApiTests/OpenApiTests.csproj b/test/OpenApiTests/OpenApiTests.csproj index 5700b77798..c38a0f88e6 100644 --- a/test/OpenApiTests/OpenApiTests.csproj +++ b/test/OpenApiTests/OpenApiTests.csproj @@ -1,4 +1,4 @@ - + net10.0;net9.0;net8.0 True