diff --git a/src/JsonSchema.Api.Tests/JsonSchema.Api.Tests.csproj b/src/JsonSchema.Api.Tests/JsonSchema.Api.Tests.csproj
index 281130903e..aaa17b2d3b 100644
--- a/src/JsonSchema.Api.Tests/JsonSchema.Api.Tests.csproj
+++ b/src/JsonSchema.Api.Tests/JsonSchema.Api.Tests.csproj
@@ -10,6 +10,13 @@
IDE0290
enable
false
+ false
+ false
+ $(MSBuildProjectDirectory)\openapi
+
+
+
+ true
@@ -24,15 +31,25 @@
+
+
+
+
+
+
+
diff --git a/src/JsonSchema.Api.Tests/OpenApiSchemaComparisonTests.cs b/src/JsonSchema.Api.Tests/OpenApiSchemaComparisonTests.cs
new file mode 100644
index 0000000000..e7a179409b
--- /dev/null
+++ b/src/JsonSchema.Api.Tests/OpenApiSchemaComparisonTests.cs
@@ -0,0 +1,48 @@
+#if NET9_0_OR_GREATER
+
+using System.Net;
+using System.Text.Json.Nodes;
+using System.Threading.Tasks;
+using Json.Schema.Api.Tests.TestHost;
+using NUnit.Framework;
+using TestHelpers;
+
+namespace Json.Schema.Api.Tests;
+
+public class OpenApiSchemaComparisonTests
+{
+ [Test]
+ public async Task OpenApiSchemas_MatchGeneratedSchemas()
+ {
+ using var fixture = new ApiTestFixture();
+ using var client = fixture.Client;
+
+ var response = await client.GetAsync("/openapi/v1.json");
+ Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
+
+ var openApi = JsonNode.Parse(await response.Content.ReadAsStringAsync()) as JsonObject;
+ Assert.That(openApi, Is.Not.Null);
+
+ var schemas = openApi!["components"]?["schemas"] as JsonObject;
+ Assert.That(schemas, Is.Not.Null);
+
+ AssertSchemaEquivalent(schemas!, nameof(SimpleModel), GeneratedJsonSchemas.SimpleModel);
+ AssertSchemaEquivalent(schemas!, nameof(StrictModel), GeneratedJsonSchemas.StrictModel);
+ AssertSchemaEquivalent(schemas!, nameof(MultiWordModel), GeneratedJsonSchemas.MultiWordModel);
+ }
+
+ private static void AssertSchemaEquivalent(JsonObject openApiSchemas, string schemaName, JsonSchema generatedSchema)
+ {
+ var openApiSchema = openApiSchemas[schemaName] as JsonObject;
+ Assert.That(openApiSchema, Is.Not.Null, $"OpenAPI schema '{schemaName}' was not found.");
+
+ var generated = JsonNode.Parse(generatedSchema.Root.Source.GetRawText()) as JsonObject;
+ Assert.That(generated, Is.Not.Null, $"Generated schema for '{schemaName}' was null.");
+
+ generated!.Remove("$schema");
+
+ JsonAssert.AreEquivalent(generated, openApiSchema!);
+ }
+}
+
+#endif
diff --git a/src/JsonSchema.Api.Tests/TestHost/TestProgram.cs b/src/JsonSchema.Api.Tests/TestHost/TestProgram.cs
index 26ed8af1aa..1e973859b2 100644
--- a/src/JsonSchema.Api.Tests/TestHost/TestProgram.cs
+++ b/src/JsonSchema.Api.Tests/TestHost/TestProgram.cs
@@ -3,6 +3,9 @@
using Json.Schema.Api.Tests.TestHost;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
+#if NET9_0_OR_GREATER
+using Microsoft.AspNetCore.OpenApi;
+#endif
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
@@ -15,9 +18,16 @@
builder.Services.AddJsonSchemaValidation();
+#if NET9_0_OR_GREATER
+builder.Services.AddOpenApi();
+#endif
+
var app = builder.Build();
app.MapControllers();
+#if NET9_0_OR_GREATER
+app.MapOpenApi();
+#endif
var minimal = app.MapGroup("/minimal/test");
minimal.MapPost("/simple", (SimpleModel model) => Results.Ok(model));
diff --git a/src/JsonSchema.Api.Tests/openapi/JsonSchema.Api.Tests.json b/src/JsonSchema.Api.Tests/openapi/JsonSchema.Api.Tests.json
new file mode 100644
index 0000000000..66a295bf87
--- /dev/null
+++ b/src/JsonSchema.Api.Tests/openapi/JsonSchema.Api.Tests.json
@@ -0,0 +1,251 @@
+{
+ "openapi": "3.1.1",
+ "info": {
+ "title": "JsonSchema.Api.Tests | v1",
+ "version": "1.0.0"
+ },
+ "paths": {
+ "/minimal/test/simple": {
+ "post": {
+ "tags": [
+ "JsonSchema.Api.Tests"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SimpleModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/minimal/test/strict": {
+ "post": {
+ "tags": [
+ "JsonSchema.Api.Tests"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StrictModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/minimal/test/multiword": {
+ "post": {
+ "tags": [
+ "JsonSchema.Api.Tests"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MultiWordModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/minimal/test/unvalidated": {
+ "post": {
+ "tags": [
+ "JsonSchema.Api.Tests"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UnvalidatedModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/test/simple": {
+ "post": {
+ "tags": [
+ "Test"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SimpleModel"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SimpleModel"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/SimpleModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/test/strict": {
+ "post": {
+ "tags": [
+ "Test"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StrictModel"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/StrictModel"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/StrictModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/test/multiword": {
+ "post": {
+ "tags": [
+ "Test"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MultiWordModel"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/MultiWordModel"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/MultiWordModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ },
+ "/api/test/unvalidated": {
+ "post": {
+ "tags": [
+ "Test"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UnvalidatedModel"
+ }
+ },
+ "text/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UnvalidatedModel"
+ }
+ },
+ "application/*+json": {
+ "schema": {
+ "$ref": "#/components/schemas/UnvalidatedModel"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "MultiWordModel": { },
+ "SimpleModel": { },
+ "StrictModel": { },
+ "UnvalidatedModel": {
+ "required": [
+ "description"
+ ],
+ "type": "object",
+ "properties": {
+ "description": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "tags": [
+ {
+ "name": "JsonSchema.Api.Tests"
+ },
+ {
+ "name": "Test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/JsonSchema.Api/JsonSchema.Api.csproj b/src/JsonSchema.Api/JsonSchema.Api.csproj
index aa5013a54a..57195d7dc4 100644
--- a/src/JsonSchema.Api/JsonSchema.Api.csproj
+++ b/src/JsonSchema.Api/JsonSchema.Api.csproj
@@ -15,8 +15,8 @@
true
true
snupkg
- 1.1.0
- 1.1.0
+ 1.0.3
+ 1.0.3
1.0.0.0
Greg Dennis
Extends JsonSchema.Net to provide API-centric functionality like automatic request validation