From 1a2b67b1549f964b51c77abebde954591f117fe8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 12:53:34 +0100 Subject: [PATCH 001/680] Configure Visual Studio Code extensions Recommend extensions for developing in Visual Studio Code. --- .editorconfig | 1 + .gitignore | 3 +-- .vscode/extensions.json | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .vscode/extensions.json diff --git a/.editorconfig b/.editorconfig index 1d82f314c9..49abe2ae52 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ root = true [*] end_of_line = CRLF +insert_final_newline = true [*.{config,cs,xml}] indent_style = space diff --git a/.gitignore b/.gitignore index 6eed564936..dc6eec25b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ [Oo]bj/ [Bb]in/ .vs/ -.vscode/ .idea* node_modules/ *.user @@ -13,4 +12,4 @@ Thumbs.db test/WebSites/CliExample/wwwroot/api-docs/v1/*.json test/WebSites/CliExampleWithFactory/wwwroot/api-docs/v1/*.json test/WebSites/NswagClientExample/NSwagClient/ -*ncrunch* \ No newline at end of file +*ncrunch* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000..005404a3f3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "EditorConfig.EditorConfig", + "github.vscode-github-actions", + "ms-dotnettools.csharp" + ] +} From fb677eb970c30d897f323aa0704b7cdb6cf70997 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:29:43 +0100 Subject: [PATCH 002/680] Fix/suppress build warnings - Suppress obsolete warnings on Swashbuckle types. - Fix some `#if define`s to use "or greater". - Fix xunit analyser warnings. --- .../AnnotationsSwaggerGenOptionsExtensions.cs | 4 +++ .../ReDocMiddleware.cs | 4 +-- .../ISerializerDataContractResolver.cs | 4 ++- .../SchemaGenerator/SchemaGenerator.cs | 2 ++ .../SwaggerUIMiddleware.cs | 2 +- .../ToolTests.cs | 2 +- .../NewtonsoftSchemaGeneratorTests.cs | 24 +++++++-------- .../JsonSerializerSchemaGeneratorTests.cs | 24 +++++++-------- .../SwaggerGenerator/SwaggerGeneratorTests.cs | 30 +++++++++---------- .../XmlCommentsDocumentFilterTests.cs | 8 ++--- test/WebSites/OAuth2Integration/Startup.cs | 2 ++ 11 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSwaggerGenOptionsExtensions.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSwaggerGenOptionsExtensions.cs index f241474982..a7829c0b6a 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSwaggerGenOptionsExtensions.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSwaggerGenOptionsExtensions.cs @@ -64,9 +64,11 @@ private static IEnumerable AnnotationsSubTypesSelector(Type type) return subTypeAttributes.Select(attr => attr.SubType); } +#pragma warning disable CS0618 // Type or member is obsolete var obsoleteAttribute = type.GetCustomAttributes(false) .OfType() .FirstOrDefault(); +#pragma warning restore CS0618 // Type or member is obsolete if (obsoleteAttribute != null) { @@ -87,9 +89,11 @@ private static string AnnotationsDiscriminatorNameSelector(Type baseType) return discriminatorAttribute.PropertyName; } +#pragma warning disable CS0618 // Type or member is obsolete var obsoleteAttribute = baseType.GetCustomAttributes(false) .OfType() .FirstOrDefault(); +#pragma warning restore CS0618 // Type or member is obsolete if (obsoleteAttribute != null) { diff --git a/src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs b/src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs index 849182c377..cd28dbdfb0 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs +++ b/src/Swashbuckle.AspNetCore.ReDoc/ReDocMiddleware.cs @@ -41,7 +41,7 @@ public ReDocMiddleware( _jsonSerializerOptions = new JsonSerializerOptions(); -#if NET6_0 +#if NET6_0_OR_GREATER _jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; #else _jsonSerializerOptions.IgnoreNullValues = true; @@ -126,4 +126,4 @@ private IDictionary GetIndexArguments() }; } } -} \ No newline at end of file +} diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISerializerDataContractResolver.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISerializerDataContractResolver.cs index 936482eada..68d19005ef 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISerializerDataContractResolver.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISerializerDataContractResolver.cs @@ -109,7 +109,9 @@ private DataContract( UnderlyingType = underlyingType; DataType = dataType; DataFormat = dataFormat; +#pragma warning disable CS0618 // Type or member is obsolete EnumValues = enumValues; +#pragma warning restore CS0618 // Type or member is obsolete ArrayItemType = arrayItemType; DictionaryValueType = dictionaryValueType; DictionaryKeys = dictionaryKeys; @@ -176,4 +178,4 @@ public DataProperty( public Type MemberType { get; } public MemberInfo MemberInfo { get; } } -} \ No newline at end of file +} diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index 5ad1494d1d..de2ac497fc 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -275,6 +275,7 @@ private OpenApiSchema CreatePrimitiveSchema(DataContract dataContract) Format = dataContract.DataFormat }; +#pragma warning disable CS0618 // Type or member is obsolete // For backcompat only - EnumValues is obsolete if (dataContract.EnumValues != null) { @@ -286,6 +287,7 @@ private OpenApiSchema CreatePrimitiveSchema(DataContract dataContract) return schema; } +#pragma warning restore CS0618 // Type or member is obsolete if (dataContract.UnderlyingType.IsEnum) { diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs index 391702722b..e5a5ae46b9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs @@ -42,7 +42,7 @@ public SwaggerUIMiddleware( _staticFileMiddleware = CreateStaticFileMiddleware(next, hostingEnv, loggerFactory, options); _jsonSerializerOptions = new JsonSerializerOptions(); -#if NET6_0 +#if NET6_0_OR_GREATER _jsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; #else _jsonSerializerOptions.IgnoreNullValues = true; diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs index dba0a076e1..ae6b54df25 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs +++ b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs @@ -35,7 +35,7 @@ public void Can_Generate_Swagger_Json() } } -#if NET6_0 +#if NET6_0_OR_GREATER [Fact] public void Can_Generate_Swagger_Json_ForTopLevelApp() { diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs index 1ef837d221..58b91c440b 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs @@ -421,11 +421,11 @@ public void GenerateSchema_SupportsOption_UseAllOfForInheritance() Assert.Equal("object", schema.Type); Assert.Equal(new[] { "Property1" }, schema.Properties.Keys); Assert.NotNull(schema.AllOf); - Assert.Equal(1, schema.AllOf.Count); - Assert.NotNull(schema.AllOf[0].Reference); - Assert.Equal("BaseType", schema.AllOf[0].Reference.Id); + var allOf = Assert.Single(schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); // The base type schema - var baseTypeSchema = schemaRepository.Schemas[schema.AllOf[0].Reference.Id]; + var baseTypeSchema = schemaRepository.Schemas[allOf.Reference.Id]; Assert.Equal("object", baseTypeSchema.Type); Assert.Equal(new[] { "BaseProperty" }, baseTypeSchema.Properties.Keys); } @@ -490,18 +490,18 @@ public void GenerateSchema_SupportsOption_UseAllOfForPolymorphism() var subType1Schema = schemaRepository.Schemas[schema.OneOf[1].Reference.Id]; Assert.Equal("object", subType1Schema.Type); Assert.NotNull(subType1Schema.AllOf); - Assert.Equal(1, subType1Schema.AllOf.Count); - Assert.NotNull(subType1Schema.AllOf[0].Reference); - Assert.Equal("BaseType", subType1Schema.AllOf[0].Reference.Id); + var allOf = Assert.Single(subType1Schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); Assert.Equal(new[] { "Property1" }, subType1Schema.Properties.Keys); // The second sub type schema Assert.NotNull(schema.OneOf[2].Reference); var subType2Schema = schemaRepository.Schemas[schema.OneOf[2].Reference.Id]; Assert.Equal("object", subType2Schema.Type); Assert.NotNull(subType2Schema.AllOf); - Assert.Equal(1, subType2Schema.AllOf.Count); - Assert.NotNull(subType2Schema.AllOf[0].Reference); - Assert.Equal("BaseType", subType2Schema.AllOf[0].Reference.Id); + allOf = Assert.Single(subType2Schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); Assert.Equal(new[] { "Property2" }, subType2Schema.Properties.Keys); } @@ -517,7 +517,7 @@ public void GenerateSchema_SupportsOption_UseAllOfToExtendReferenceSchemas() Assert.Null(schema.Reference); Assert.NotNull(schema.AllOf); - Assert.Equal(1, schema.AllOf.Count); + Assert.Single(schema.AllOf); } [Fact] @@ -827,7 +827,7 @@ public void GenerateSchema_GeneratesOpenSchema_IfDynamicJsonType(Type type) Assert.Null(schema.Type); } - private SchemaGenerator Subject( + private static SchemaGenerator Subject( Action configureGenerator = null, Action configureSerializer = null) { diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index fb7a219506..30da476cec 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -456,11 +456,11 @@ public void GenerateSchema_SupportsOption_UseAllOfForInheritance() Assert.Equal("object", schema.Type); Assert.Equal(new[] { "Property1" }, schema.Properties.Keys); Assert.NotNull(schema.AllOf); - Assert.Equal(1, schema.AllOf.Count); - Assert.NotNull(schema.AllOf[0].Reference); - Assert.Equal("BaseType", schema.AllOf[0].Reference.Id); + var allOf = Assert.Single(schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); // The base type schema - var baseTypeSchema = schemaRepository.Schemas[schema.AllOf[0].Reference.Id]; + var baseTypeSchema = schemaRepository.Schemas[allOf.Reference.Id]; Assert.Equal("object", baseTypeSchema.Type); Assert.Equal(new[] { "BaseProperty" }, baseTypeSchema.Properties.Keys); } @@ -525,18 +525,18 @@ public void GenerateSchema_SupportsOption_UseAllOfForPolymorphism() var subType1Schema = schemaRepository.Schemas[schema.OneOf[1].Reference.Id]; Assert.Equal("object", subType1Schema.Type); Assert.NotNull(subType1Schema.AllOf); - Assert.Equal(1, subType1Schema.AllOf.Count); - Assert.NotNull(subType1Schema.AllOf[0].Reference); - Assert.Equal("BaseType", subType1Schema.AllOf[0].Reference.Id); + var allOf = Assert.Single(subType1Schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); Assert.Equal(new[] { "Property1" }, subType1Schema.Properties.Keys); // The second sub type schema Assert.NotNull(schema.OneOf[2].Reference); var subType2Schema = schemaRepository.Schemas[schema.OneOf[2].Reference.Id]; Assert.Equal("object", subType2Schema.Type); Assert.NotNull(subType2Schema.AllOf); - Assert.Equal(1, subType2Schema.AllOf.Count); - Assert.NotNull(subType2Schema.AllOf[0].Reference); - Assert.Equal("BaseType", subType2Schema.AllOf[0].Reference.Id); + allOf = Assert.Single(subType2Schema.AllOf); + Assert.NotNull(allOf.Reference); + Assert.Equal("BaseType", allOf.Reference.Id); Assert.Equal(new[] { "Property2" }, subType2Schema.Properties.Keys); } @@ -552,7 +552,7 @@ public void GenerateSchema_SupportsOption_UseAllOfToExtendReferenceSchemas() Assert.Null(schema.Reference); Assert.NotNull(schema.AllOf); - Assert.Equal(1, schema.AllOf.Count); + Assert.Single(schema.AllOf); } [Fact] @@ -860,7 +860,7 @@ public void GenerateSchema_GeneratesSchema_IfParameterHasTypeConstraints() Assert.Equal("integer", schema.Type); } - private SchemaGenerator Subject( + private static SchemaGenerator Subject( Action configureGenerator = null, Action configureSerializer = null) { diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs index de8015624b..81e3260d2e 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs @@ -420,8 +420,8 @@ public void GetSwagger_GeneratesParameters_ForApiParametersThatAreNotBoundToBody var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(1, operation.Parameters.Count); - Assert.Equal(expectedParameterLocation, operation.Parameters.First().In); + var parameter = Assert.Single(operation.Parameters); + Assert.Equal(expectedParameterLocation, parameter.In); } [Fact] @@ -449,7 +449,7 @@ public void GetSwagger_IgnoresParameters_IfActionParameterHasBindNeverAttribute( var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(0, operation.Parameters.Count); + Assert.Empty(operation.Parameters); } [Fact] @@ -510,8 +510,8 @@ public void GetSwagger_SetsParameterRequired_IfActionParameterHasRequiredOrBindR var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(1, operation.Parameters.Count); - Assert.Equal(expectedRequired, operation.Parameters.First().Required); + var parameter = Assert.Single(operation.Parameters); + Assert.Equal(expectedRequired, parameter.Required); } [Theory] @@ -519,7 +519,7 @@ public void GetSwagger_SetsParameterRequired_IfActionParameterHasRequiredOrBindR [InlineData(true)] public void GetSwagger_SetsParameterRequired_ForNonControllerActionDescriptor_IfApiParameterDescriptionForBodyIsRequired(bool isRequired) { - void Execute(object obj) { } + static void Execute(object obj) { } Action action = Execute; @@ -577,8 +577,8 @@ public void GetSwagger_SetsParameterTypeToString_IfApiParameterHasNoCorrespondin var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(1, operation.Parameters.Count); - Assert.Equal("string", operation.Parameters.First().Schema.Type); + var parameter = Assert.Single(operation.Parameters); + Assert.Equal("string", parameter.Schema.Type); } [Fact] @@ -1033,8 +1033,8 @@ public void GetSwagger_SupportsOption_DescribeAllParametersInCamelCase( var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(1, operation.Parameters.Count); - Assert.Equal(expectedOpenApiParameterName, operation.Parameters.First().Name); + var parameter = Assert.Single(operation.Parameters); + Assert.Equal(expectedOpenApiParameterName, parameter.Name); } [Fact] @@ -1057,8 +1057,8 @@ public void GetSwagger_SupportsOption_Servers() var document = subject.GetSwagger("v1"); - Assert.Equal(1, document.Servers.Count); - Assert.Equal("http://tempuri.org/api", document.Servers.First().Url); + var server = Assert.Single(document.Servers); + Assert.Equal("http://tempuri.org/api", server.Url); } [Fact] @@ -1179,7 +1179,7 @@ public void GetSwagger_SupportsOption_ParameterFilters() var document = subject.GetSwagger("v1"); var operation = document.Paths["/resource"].Operations[OperationType.Post]; - Assert.Equal(2, operation.Parameters[0].Extensions.Count()); + Assert.Equal(2, operation.Parameters[0].Extensions.Count); Assert.Equal("bar", ((OpenApiString)operation.Parameters[0].Extensions["X-foo"]).Value); Assert.Equal("v1", ((OpenApiString)operation.Parameters[0].Extensions["X-docName"]).Value); } @@ -1277,7 +1277,7 @@ public void GetSwagger_SupportsOption_DocumentFilters() Assert.Contains("ComplexType", document.Components.Schemas.Keys); } - private SwaggerGenerator Subject( + private static SwaggerGenerator Subject( IEnumerable apiDescriptions, SwaggerGeneratorOptions options = null, IEnumerable authenticationSchemes = null) @@ -1298,4 +1298,4 @@ private SwaggerGenerator Subject( } }; } -} \ No newline at end of file +} diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs index 9d391fe3bd..4f3b28a3ad 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs @@ -40,11 +40,11 @@ public void Apply_SetsTagDescription_FromControllerSummaryTags() Subject().Apply(document, filterContext); - Assert.Equal(1, document.Tags.Count); - Assert.Equal("Summary for FakeControllerWithXmlComments", document.Tags[0].Description); + var tag = Assert.Single(document.Tags); + Assert.Equal("Summary for FakeControllerWithXmlComments", tag.Description); } - private XmlCommentsDocumentFilter Subject() + private static XmlCommentsDocumentFilter Subject() { using (var xmlComments = File.OpenText($"{typeof(FakeControllerWithXmlComments).Assembly.GetName().Name}.xml")) { @@ -52,4 +52,4 @@ private XmlCommentsDocumentFilter Subject() } } } -} \ No newline at end of file +} diff --git a/test/WebSites/OAuth2Integration/Startup.cs b/test/WebSites/OAuth2Integration/Startup.cs index b208bb8164..3c37912feb 100644 --- a/test/WebSites/OAuth2Integration/Startup.cs +++ b/test/WebSites/OAuth2Integration/Startup.cs @@ -118,7 +118,9 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) resourceServer.UseAuthentication(); +#pragma warning disable ASP0001 // Authorization middleware is incorrectly configured resourceServer.UseAuthorization(); +#pragma warning restore ASP0001 // Authorization middleware is incorrectly configured resourceServer.UseEndpoints(endpoints => { From ea6f2d5a8b3edda904fecfef013404d234258e2f Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:31:13 +0100 Subject: [PATCH 003/680] Run npm audit fix Run `npm audit fix` to resolve warnings about vulnerable dependencies. --- .../package-lock.json | 1186 +++++++++-------- 1 file changed, 644 insertions(+), 542 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json index 80a728b69c..b57080dcd8 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json @@ -12,26 +12,28 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "peer": true, "dependencies": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "peer": true, "dependencies": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" @@ -50,50 +52,34 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "peer": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "peer": true, - "dependencies": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "peer": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "peer": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -112,44 +98,54 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "peer": true, "dependencies": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "peer": true, "bin": { "parser": "bin/babel-parser.js" @@ -170,34 +166,34 @@ } }, "node_modules/@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "peer": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -205,12 +201,13 @@ } }, "node_modules/@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -249,6 +246,64 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.0.0-rc.6.tgz", "integrity": "sha512-dDnQizD94EdBwEj/fh3zPRa/HWCS9O5au2PuHhZBbuM3xWHxuaKzPBOEWze7Nn0xW68MIpZ7Xdyn1CoCpjKCuQ==" }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@redocly/ajv": { "version": "8.6.4", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.4.tgz", @@ -305,9 +360,9 @@ } }, "node_modules/@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "peer": true }, "node_modules/@types/json-schema": { @@ -321,148 +376,148 @@ "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "peer": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "peer": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "peer": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -479,9 +534,9 @@ "peer": true }, "node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "peer": true, "bin": { "acorn": "bin/acorn" @@ -491,9 +546,9 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "peer": true, "peerDependencies": { "acorn": "^8" @@ -590,26 +645,35 @@ } }, "node_modules/browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" } }, "node_modules/buffer-from": { @@ -630,14 +694,24 @@ "peer": true }, "node_modules/caniuse-lite": { - "version": "1.0.30001309", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", - "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - } + "version": "1.0.30001605", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", + "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true }, "node_modules/chalk": { "version": "2.4.2", @@ -697,7 +771,7 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "peer": true }, "node_modules/colorette": { @@ -770,9 +844,9 @@ "integrity": "sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ==" }, "node_modules/electron-to-chromium": { - "version": "1.4.66", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.66.tgz", - "integrity": "sha512-f1RXFMsvwufWLwYUxTiP7HmjprKXrqEWHiQkjAYa9DJeVIlZk5v8gBGcaV+FhtXLly6C1OTVzQY+2UQrACiLlg==", + "version": "1.4.724", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz", + "integrity": "sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==", "peer": true }, "node_modules/emoji-regex": { @@ -781,9 +855,9 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -794,9 +868,9 @@ } }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", + "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==", "peer": true }, "node_modules/es6-promise": { @@ -815,7 +889,7 @@ "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "peer": true, "engines": { "node": ">=0.8.0" @@ -923,9 +997,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "peer": true }, "node_modules/has-flag": { @@ -1039,10 +1113,10 @@ "node": ">=4" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "peer": true }, "node_modules/json-pointer": { @@ -1254,9 +1328,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "peer": true }, "node_modules/oas-kit-common": { @@ -1567,9 +1641,9 @@ } }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -1585,9 +1659,9 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "peer": true, "dependencies": { "randombytes": "^2.1.0" @@ -1656,9 +1730,9 @@ } }, "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "peer": true, "engines": { "node": ">=0.10.0" @@ -1674,15 +1748,6 @@ "source-map": "^0.6.0" } }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/stickyfill": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stickyfill/-/stickyfill-1.1.1.tgz", @@ -1805,13 +1870,14 @@ } }, "node_modules/terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.30.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.2.tgz", + "integrity": "sha512-vTDjRKYKip4dOFL5VizdoxHTYDfEXPdz5t+FbxCC5Rp2s+KbEO8w5wqMDPgj7CtFKZuzq7PXv28fZoXfqqBVuw==", "peer": true, "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -1819,27 +1885,19 @@ }, "engines": { "node": ">=10" - }, - "peerDependencies": { - "acorn": "^8.5.0" - }, - "peerDependenciesMeta": { - "acorn": { - "optional": true - } } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "peer": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -1863,24 +1921,6 @@ } } }, - "node_modules/terser-webpack-plugin/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "peer": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -1895,6 +1935,36 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1909,9 +1979,9 @@ "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" }, "node_modules/watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -1927,34 +1997,34 @@ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "node_modules/webpack": { - "version": "5.68.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.68.0.tgz", - "integrity": "sha512-zUcqaUO0772UuuW2bzaES2Zjlm/y3kRBQDVFVCge+s2Y8mwuUTdperGaAv65/NtRL/1zanpSJOq/MD8u61vo6g==", - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -2086,23 +2156,25 @@ }, "dependencies": { "@babel/code-frame": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz", - "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "peer": true, "requires": { - "@babel/highlight": "^7.16.7" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" } }, "@babel/generator": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz", - "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", + "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", "peer": true, "requires": { - "@babel/types": "^7.17.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" } }, "@babel/helper-annotate-as-pure": { @@ -2115,41 +2187,28 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "peer": true, - "requires": { - "@babel/types": "^7.16.7" - } + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "peer": true }, "@babel/helper-function-name": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz", - "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==", - "peer": true, - "requires": { - "@babel/helper-get-function-arity": "^7.16.7", - "@babel/template": "^7.16.7", - "@babel/types": "^7.16.7" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz", - "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "peer": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz", - "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "peer": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -2162,35 +2221,42 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz", - "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "peer": true, "requires": { - "@babel/types": "^7.16.7" + "@babel/types": "^7.22.5" } }, + "@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "peer": true + }, "@babel/helper-validator-identifier": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz", - "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "peer": true }, "@babel/highlight": { - "version": "7.16.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz", - "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "peer": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/parser": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz", - "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "peer": true }, "@babel/runtime": { @@ -2202,41 +2268,42 @@ } }, "@babel/template": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", - "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "peer": true, "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/parser": "^7.16.7", - "@babel/types": "^7.16.7" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" } }, "@babel/traverse": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz", - "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==", - "peer": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.17.0", - "@babel/helper-environment-visitor": "^7.16.7", - "@babel/helper-function-name": "^7.16.7", - "@babel/helper-hoist-variables": "^7.16.7", - "@babel/helper-split-export-declaration": "^7.16.7", - "@babel/parser": "^7.17.0", - "@babel/types": "^7.17.0", - "debug": "^4.1.0", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", + "peer": true, + "requires": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.17.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", - "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "peer": true, "requires": { - "@babel/helper-validator-identifier": "^7.16.7", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -2272,6 +2339,55 @@ "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.0.0-rc.6.tgz", "integrity": "sha512-dDnQizD94EdBwEj/fh3zPRa/HWCS9O5au2PuHhZBbuM3xWHxuaKzPBOEWze7Nn0xW68MIpZ7Xdyn1CoCpjKCuQ==" }, + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "peer": true + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "peer": true + }, + "@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@redocly/ajv": { "version": "8.6.4", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.6.4.tgz", @@ -2321,9 +2437,9 @@ } }, "@types/estree": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", - "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "peer": true }, "@types/json-schema": { @@ -2337,148 +2453,148 @@ "integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ==" }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "peer": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "peer": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "peer": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "peer": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "peer": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "peer": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "peer": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "peer": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "peer": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "peer": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -2495,15 +2611,15 @@ "peer": true }, "acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "peer": true }, "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", "peer": true, "requires": {} }, @@ -2585,16 +2701,15 @@ } }, "browserslist": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", - "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "peer": true, "requires": { - "caniuse-lite": "^1.0.30001286", - "electron-to-chromium": "^1.4.17", - "escalade": "^3.1.1", - "node-releases": "^2.0.1", - "picocolors": "^1.0.0" + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" } }, "buffer-from": { @@ -2615,9 +2730,9 @@ "peer": true }, "caniuse-lite": { - "version": "1.0.30001309", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz", - "integrity": "sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==", + "version": "1.0.30001605", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", + "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", "peer": true }, "chalk": { @@ -2669,7 +2784,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "peer": true }, "colorette": { @@ -2726,9 +2841,9 @@ "integrity": "sha512-kD+f8qEaa42+mjdOpKeztu9Mfx5bv9gVLO6K9jRx4uGvh6Wv06Srn4jr1wPNY2OOUGGSKHNFN+A8MA3v0E0QAQ==" }, "electron-to-chromium": { - "version": "1.4.66", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.66.tgz", - "integrity": "sha512-f1RXFMsvwufWLwYUxTiP7HmjprKXrqEWHiQkjAYa9DJeVIlZk5v8gBGcaV+FhtXLly6C1OTVzQY+2UQrACiLlg==", + "version": "1.4.724", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz", + "integrity": "sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==", "peer": true }, "emoji-regex": { @@ -2737,9 +2852,9 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "enhanced-resolve": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz", - "integrity": "sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", "peer": true, "requires": { "graceful-fs": "^4.2.4", @@ -2747,9 +2862,9 @@ } }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz", + "integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw==", "peer": true }, "es6-promise": { @@ -2765,7 +2880,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "peer": true }, "eslint-scope": { @@ -2851,9 +2966,9 @@ "peer": true }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "peer": true }, "has-flag": { @@ -2941,10 +3056,10 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "peer": true }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "peer": true }, "json-pointer": { @@ -3086,9 +3201,9 @@ } }, "node-releases": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", - "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "peer": true }, "oas-kit-common": { @@ -3328,9 +3443,9 @@ } }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "peer": true, "requires": { "@types/json-schema": "^7.0.8", @@ -3339,9 +3454,9 @@ } }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "peer": true, "requires": { "randombytes": "^2.1.0" @@ -3407,9 +3522,9 @@ "integrity": "sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==" }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "peer": true }, "source-map-support": { @@ -3420,14 +3535,6 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true - } } }, "stickyfill": { @@ -3511,43 +3618,28 @@ "peer": true }, "terser": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", - "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", + "version": "5.30.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.2.tgz", + "integrity": "sha512-vTDjRKYKip4dOFL5VizdoxHTYDfEXPdz5t+FbxCC5Rp2s+KbEO8w5wqMDPgj7CtFKZuzq7PXv28fZoXfqqBVuw==", "peer": true, "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" - }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "peer": true - } } }, "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "peer": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "peer": true - } + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" } }, "to-fast-properties": { @@ -3561,6 +3653,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" }, + "update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "peer": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3575,9 +3677,9 @@ "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" }, "watchpack": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz", - "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "peer": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -3590,34 +3692,34 @@ "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" }, "webpack": { - "version": "5.68.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.68.0.tgz", - "integrity": "sha512-zUcqaUO0772UuuW2bzaES2Zjlm/y3kRBQDVFVCge+s2Y8mwuUTdperGaAv65/NtRL/1zanpSJOq/MD8u61vo6g==", - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.0", - "@types/estree": "^0.0.50", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.4.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "peer": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-better-errors": "^1.0.2", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.3.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" } }, From d3ec037accc37d2e4782cc0067c96f8536b9ba7a Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:32:01 +0100 Subject: [PATCH 004/680] Delete build.txt Remove build.txt which presumably was committed by mistake. --- .../build.txt | Bin 3392094 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/build.txt diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/build.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/build.txt deleted file mode 100644 index 709fe9c6e74c713869e20eafb5872058bfed2598..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3392094 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�^E zEgrj5)%y*xXTwDkn^$IvJ)pj(d!M%RE6>Sb?(8-8Qg&~9@)dAu8+#^y-7o%nIQa(u z$L>gVjOvc8yQC$brp&_L0|&B~O*#4|yQVTAc87S*&Kc0;$b)a2uru4xLelk5c71Jv zaBA1t()tMaCi`WtKPiPg-Yk#I4x0>?R9Pd#VVjKXW_UKOR@o=YwYd-MNw-&wU6@m* z)HC5~Us`ExPITr~w4&HOqkMLF^}SdwY#P6cm_k=+>fyrKE$uR5Y#pMi5mQre^HqHN zOv#qAmY#Q|u5w*eeK4O(JXUt~h+68xmGxZ{e7Zb!!#Ljcjk}5a`zWbItl!6X*y{^$ zs?C}7x%h3e_j+ zC;41k+qKSJO7zREFO#mXtRh#Cei~ZiyZDxF0F-Bp;WCn;RL>YLy}Pl}Xb6f=zp7l* zl-dB%f^|nce^4wbk@>N!Ha4n!2)#LFKUJRZ!pQo?{3rD)(Wltuiy8B@C{HKu5cFZj#k(EIz5kVEb#J-MQuN7n1C7k~ajKhK}bgDKB=S?p{#jekfV=5}D^2k0BI({|Y;Iwaf23GXB_fVZnLgbw;xo~x`UK0+NsT3gDXr1BH9a3oL1Mv(aPtaY5T zqMFpj3wuP-kyiziNRQ2UrlMts7SI;3sq?-Dt0$u;Q zwRC3J@H0V37)z~zg_-gVXAx@BMhpsL#RRdCq5KE=zI?l39HNzMzB)6aEbeau5L zD1P0M>p{|5*HuL>dv6WJ*{{x^3B<>U4ArMaID1A)gZ67??&HwJ`{-Q{Mz4Fv$W;IC z+OG;_+OPZxdCac^d@Wj+OZiNjK*K3dCstpHc!bJceZE`vtP`CymKyJQs`q(>y*ut4 z#fzv>YTpb)o}27V9-lWkw5Men8Ar6G-BZ1WA5(M8881A!jrb_vE+)Fu%**AS5KUaF zNHe!Kubu0;l`8c3XrR0D%Z&ZBqwBH6A;zg&RHOe@E;N)=f3l0@@~6~2q^{q1E;TPx zB>eF_vf`eM!1F<*%OR4vo5-X(lfMkh=Mw9q) z*M0?|(VrCa(oP8Sn-$^Yf^tTjMDRl+mgoDTSPAyvWXwFIQ__BuEHPU@Rdf#B+s7w@ z_@OJSdq&FZvR}-)J1ACFyVJ6>#1nk8ckM-x=U#!jxlTTQt;d|8?5C7lWN>z$f19Aaf^{pyga%oAr6maIp`(2T-it}OFUyRI=IKFTOe z4&5r`q$@ecg~IEiw{N2lUCKIZ)!dB~w53kG-GswgH-w_~`z!LF(Fd_0+l_0eHZ%jHV}w4ri@8X5*Z8^AKVK~U){n3r zE3`iRc|U2^gKGtM%gqq|{(yGZ z%Cp2=n?`oH+}ni*+z~SW+jO1zU+hEkbDaFYF8av>bTLR-cVeU7Uf~^Nx8o)+v@Jd2 z?Z;*vrYeO)bKyC}@se5Pi=48^E5!n!pV*x55V~ia=2Diuaii9+Pe}SGKc>EK8tIgG zwti>V1(O~j)||3qI1z@O>3M1uqi=3RLr-(y8RXPnsV{3xLo5x=DYVb|_z~kUc6l{O zABhl;*3$sxYUpa?+gMKgL@X}Z4_|CBY-PC`rcXz$U!nSULhDw=o-$>LEHizr`j6<+ zDzRC6)@!^i<{qSI=1E)^)N1p>9$tT2cBivRi* zs()u(lA_FIdj;pLg)S@TV9Z(fBu%TjKJ~W|;EUq& zduZ3meXN&M=5jsh=gG9j~Dx1GXI)L6pJ3KHRmx^=xO$> zo^0#mlW&stuwzV5_T07t*+)tADpCTi!OSt&SciI!IRLja!-zKJAwD<5Z|bGGeyhzp z;n}Yr7p07_6P=IedoB7NbI6!)#+ZRfnb)vkcA>95gRcJ+Kfg!5#gEaLYOWra9-wE= zJt=U8H7{3Q=|4neyYODS{n$b7@iRnXo=je1zn)>oUg69CYP%By%?`TzKPJwK-_4Y@ z1^JwSd`>_2~rRfT2p95vQ zUsGW7Tdcw#_@3U@OzvCxPy1M(5A!Vcsg-x^zp|`h$LeYN#9HSxgI)FuGypki^9cWS zvH!YVwHyk#!u~JrbT(b@{uDN@6A$#cH%c6OWMThPQ*bh4|NR3RG5kP_0tseHeF@ z5#+kxF#Z{E{26`|HHqsw!Yj=58a@ZssxZRyvG5!I0Xbvts56r)FZEAQ7HvgaRpWoo zA+3kyaYNczV!Zv(To>F;I(NIOD$~C+U81?ti3P}W3njyrw_gc_R^E}3P?@g zreyG+L-UphyhL-%C}nLrYcH<$qU+ILi#6TTmxI#TPSO}%J{j&D+Sx_LtGn29bCt6; zSE8*i(ik1LnjP`7=%#TEOM$2C(Bpps{oh^-S_8C)LN0(!u%RI-OJ|QCLmX-OV-XHbNY|2`Ei)wJL2mL`?_`ZbJe}uob@qKq5p_eL3 z$xj#kUwbCIl%zVk!J4{Qyz5=Mi@o?9U$zJ3{)998&dR~k9kM-CmzeKav6Jj_dyT)u zvn4(@c1a`(UI6=CsjH*EF6^8WeC7tvduOocH5wr^y=iHs^a9#ZoU&JH3tmBTdz0^N zGiY7tfU=Mt*-PvL>Ed(HS=BR4x?rB5>=JoH=!<^f%gHWqLpml&KLDlsJD!oA;`8L6 zEqwol?}H+jMQNG4)#W}uPBC|4t(Ww3v+tw%F^i$|s?R$mjyFHQ$wt!WcX7(_-eypK zwT^jFD9)N)Y8-|(LtNnI+qkH$J!EX|EPsBh;HOh?IHNI#IAM1SKR*;4NU23UWbPPi zthT_7Nk9iJ)FPxil8l~E280$YK zkBbr6J)AS)Ym~Vk3+^W}{8`ND1Aa;k*w*|VnTdU!e=M6H@+#G>czKmv${ZdwcR_co zhAd|$Hv2S(!cCh_lwwoJ1~=m9A}{qI%DYtjX>*%zU4{Q1&-t${-;ujngGVWCeB$?^ zr`|&roV-f}p^o{sK%57~d%TZ(Paoykb5F7ES;zbUJpZH6u4AiRQt}q%1}UXKFKKo& zca+u`C7EPgtDQ!971mrSy_g3;-SsIX&lT2&RVE^3h&olxH(pYQNB2>^8Gm@ZH5{=x zZB#X$G~8`d0(DtxFWG)%L^@o_mYTP_+K*)owGYpg$0~CesgIPWHf8zqX{GPFXvbkB z>$7L!(aXEI7`?QLEsIAMh$X{e&|sHstQD?b$Gz zko)|FMdqn~$c9_@B>^L;TNz27r4qZBO zY17R*W9%bN3w$(KlwCQh>I-TAEYuWS&+&B5+l-zt>i+@n^MqcL)xntl46h&Hi(EW| z_v8aU!%x{Z7j*%yg3*Ny#*8t7I4q^UK`vQ(N+hjhq?EiYu>i-5l>M~Over)hlleMd zE*u@@u0!S^eF@Y_g^t-jyxiDNjDJ`u%1D|R3%fnu4rN!DdDE7n)n^;G6its}!%9v= zQ(;e@Yem_+XzOg*cDD0%%B$r1D`NPVdKvqsPy=;!cUQ01d_t2)I}|^Qrr5;Qs+U!z zlw8ZKTVG}{&r4xWgk7bQW0!Gx3B&5|H`o_DN5Q2;Qd--b1Lno4opcA4^kQpCZ#9m` zI|`PVTx7n&96ULNlU5tAWOjnv&A`YIw+AQdJ+5&*^ZVSc29IG!JcNC>UF700SBIZr zR?-<}!kq9U_-dHF!|YvB=~?j!Q$ceukuwaL>-z^jcY)8p!#EFMKR(3wSNPt>*CGD9 zhwC5UJH`X)u8Zp>Qrva9N>}x;=uDyfQ@yec_PetvqZ;QyUN_k9e!hBdUa=Xy&#KR! z*IoJ0A4}+`L;0lBV)ww+ldAV7?PqUupG=!!WnK26Tuy$6Y*HV}!$u#9D|?PgI;zp= zTy6Ck8E-R4aly^Yg-dkz1HRwwc=fD zUfeV96>)H$9Z5c0=O_IEyGcJPvwT?f^8=pztngg13&4<1-W}gV+|`7Xi$i;xhO0Vi z=owbK`fnFiyH8=KNOiA^5Y@lF5WMZrPzZJa~%CT^U9(r6vjSq*Nbp=N@$MVC|+ zhvqr$nP^R-{hHf#qut)y`hc(cau=NF#xRDmtJ=-GZVxHlhMJ+GhP`ISb_j8m#Eg7v zoOP+q)ur|Z&xN(AwMu;wMX9Th_IpdosVOsTm9Jc$Ha?Nf==|)C@7Eam#aC^Ntk+cE$6!IPF)1#c7G{9Np&4tiQD3snt6dlG@|l!gHp%04AbX z)nB;#My|xh$GXzH(QFs1zqn7C^%q@zp_OszFT^ZY8%jmBF}^)rxmAz#8cxgEDRH^p z>2;|a#4DcJ`A&Yd7!w5bJe@*@bMIcF*d!HSQKU6!hx3mhQi?cc+v8M*jK{;#qRa zIb&Lzk%j%4r)wdtGP7D`iKP(kX7@{hEW{Zz%QPd`?MgX{-euNq9z}`~oAd_Ah+ucU(uF z*|{A@N8uiyg5#O_BAsd2&qAJrSOpV!%ZVa>+=`~MV((sm7+sod;} z$g?(?%0@Of-J|V%ZB$b1Bu&ZrmS(>7QN&&BDY=c;yam+|?H53 zC99~d=TFzK3Z>g$ldp>oG4?q74zBu7vHcZuVHXtUOA#YT73`NX1t%Tji~1jRK~jyu zXx~a2i#_BCZm0d&y3}xXma$D%#hOZ^lXLfSUtrQN^NkkY7g(gOps(>_+I#Ejqr+4K zxHY&w{;Rk8W`!qqU9GOT=Wb_#sf>t`5VOMeXlLiIo`2UT4_vFOex0jc1;%)~Stzb0 zx6T?Cah>|Eiyq6-c>Qx}(!^wkyLx7f*3@*5FipB^m$kjcX?yi(U`v}cnx8Fe>+f4d zA=kGldekcFhAtJy=?_^e97i{>ZZRdB;=5DF;%S}S$+>S?naiZq6TBkUuU*f$y56dG z+FVRcai{`3%=O`{4(e;U$EH-x(i?CWt1;G`XH-D?*>&Kf`(n;L~byG(OHdG%Q!GhNp=1BvJKh zuQtCI#MLmy*f(OReXXvS(QraO@}?dV%KK)t*leSAxR6!@^ve_5#?$BWV_F$?%en03 zC~58_L8@qX=H*bFAo`Ra;X?b$YujcPxhS@{4hwbuw;?+Y*t&{A?!`T`8`)mxCvEaJ z4%<~ik+D7Ehq$W=H5ZNlHYJC(?lS)8-V%(OxZNA#Pm!l)P*nj@*Fpbjh;!@e@xz{e?#H9}*IVGc_@e(kty(bes89KyNvwPAm&tH;=WN!1u!uaf(D zzn-z)-6=@(T;;6wXlE%|k*xL9X+7U8S?l>`P_2ifXRYT5c?ZkOY93Z@#cDmIgiXNk5Hfw0mr5FsB}+H$1y&|W$5qJWxC;awobQyqOXpp%Q#%8-0?!rQFUpi zoHNj6b({CS+Z}SpizdztMWXD^6S?CBvmtWF3pvvycf2_9_IojwQOLVY*&mAMsdw4! z9yXj6W^9(#vbEA^={Z)oI|yY?IZmIeEc7X)O1{xS51HjuGz}cXN?sIr^}FF3lE?;S=Gb z6XdY5Q`pvR*G6`oJG8u3jItb}C!OuHX}U+4Cf&93+0n|?cK?pt{fzkm{dB>4G_Voa z&PnL57us^S{=QWd@>PzmuIA{{a-2qyb;i}v8Dk@@ZkA8~I>>}HqW|rRL8+#PL(@ zuj?_!xrXoPIs8K+t#}^5X`)eoEygjDF{V>*(=1;UbzOZ&^~nIPAa#jub?qT)x1Xtc z_vYO*XGFB)Me{`L#!p>@r$$`Ov$(WQ-(Lyqqt1A{MdAkSX3G^4)U-WUD(B6;W3Bdb z1hshw>U4lBtlK5d&lvu+wAI(S+Rd=-dUNkj3;H|)5>0_9*H^6 zm7NoK7R3R+j_}tEsPzG#;iqg0YB%e0RT(vip=PGEZLgGxx=G&iFL)+e5T`)sBQHh| z-MMP7h#N4TK@p;Zfk?p=Q8mX_3>`D zd}zCiad^iLGKQAIaWA)Dg1v*bKEHZ8h|ki_8i?|@=&j^IiR~H_@`qI!JdN#88ou8)4sHTTU@ru42^Co<84m zWgSFUUnE4^U0k#d!aZ~~nsuPeEp)+@=PKiNchAyr7Nj}XFZ&2j;2ZYadbR}e`v_fY zCuJm^RHPcZi`$Z}|NAKR)>$~Sikb97&> zP|O=z;;6+GdqR=T{Z()43_CYNB;p1%caFb^*o%&kR98NZieq}ov@W4bG5xd|ML>;V8{K z78}v9=fl5K=H@CQx}24xB_FW6XP~c5_EpGPhkjp0S8VRuzm;^lUF6kQt*HLlx@L5n zV~r+tQ|f2>U%(%(i_vC=jG|;6IOO}UkCT`4gIt_*^Q`+w6R~Ao$>rXwOf#xCS2e4D z6q4-&qzo;2MD`zr2o;)tD^Uw4% zy!R60^%u&KR+~3`TbCjOlCz4OS05$#vzBDpe2!TxU4M3)K}G(1 zTg*YuBeAJDX4cY~k5~Q2oO3?|cll`6UPF;%ZFadl&+2Xu=kXaXXTV+)A6;FPuqJz) z;yq5}HD+asmm`F`{jE3QU7?j{KqTfCUVFS0-DIiY(3^n$=5obvg+zXjd3*qu{|de& zpMHmL8BJ&GB0val;xR_fHZu3*|J%5?A0OJuv>~u#`I7J7kPAE+it>%#t}EoZvNC{v z%PKxpb&+vOy);xUdZWstcq&yXSC%ksC8162<+;Sj!!kubhqS)}elho%nso@l%H1f( zpPx-X8|@5c*WXy4-z){=rJ(4LtW7A>lPfE_$v?MS*)SAL=_Jvd<3gq~|DLbV_B@9l zRH`#Hb4TpPHhqc8y`}qUdsE1`eNd~`XzxR}CW6yOrj;U--7YTwhbE_;kv9hbe+ja#}>+@d6h zS`TiiaGp=sZU^BQo@_sxR*OlTHg>eSC z%RUl!<}vB9cL~BWb10V4q5C&+f|4dW`D653LpSqq{}%O}%cw z)n#psyRMQ0%KQvjXLk=$dKa0kY~SZ?L6L9Kf%^!)5uS8&RPi#zXPUSk2ETuyj3lY6 z8jKGi*PAU1Wn%guDUY}jvmZf!&Gx#_Z=@bR?mg`u@%9~q*DgWP!)8<1np)qChQ3c< zjc#$3>pDX-=lpc?-;hd%_vyXdj82cfKD_Q~*+^BDbaM8VZo^yNU~QCEE1D!O0N8+HO-^q>3~$_%nk zA6rL+v$YT(7Z`kR$59#K{ugMj{AJ|9A`mXLw?sI9p8j<56Xer7ARE!`(`3h+P52Vp zG?9-P6Q?zw*@Z@+ndY=0h?P@}_9wJoc1f03hCq(c(;Lc8fgDvYl^K8Caii(kGit== z&iS}t@p_TXH`Sf3(~A1a`R2PCUh2lHRUNWf)~z4Mwq1eDx8vg5xcwc5d*kly<6-0F zHuw?FtRK#AwJ0y8@41Z!LoF89o|O3J=JfPWy~~2>8kfHdz4UV@56g54G_)A9SEtc|Xc7q6^c(T=RYl?Oab!^3R4ENbbRuvUfZEf<%DaFPf)N(Q_>8v5i$> z#AB6uFx?-tpEO4&J}4>?BQ*QQMgMX?og8af)rf9$422QEPemM>I~bS6WxPnXED8Hd z%QT}`^UiM8Q@4rJc(LiFg;d1n$y!Qq#7OUg> z5w4uO-mM4IaCxr%>a?5Al(W&kYBxQHH`VO`mMdCL;4!|2ulS(wB+JQ_q~_{bac+i6 zh!Z8ZqYnbB@0_Ouj;Sk;dflbcFOJkID zxqm`j4p@w2Ygd!rR;IamUP8 z%U8!({)_NwyY_!O`pj~mn;dAf%6;&?&3*dfcAI-G8jtIJ@xw?PH@=>#tB8@OWjo~+ zFz^y6$eQt4loxL3t^mVZA~C*3dS=Xtta5@>j;XFzOx2Y?Wqy1LFPWZIAg5Qv?7L8R zmQLNIQ@TCqBfHYg!bmnF2VAP_il3Rnx=~oS_3lGW-R>Bs93x@G`%f?JKOu`}h;d~U zl5=Id(MV`z`%pt@Y_so1=W64_=tgae){4!umG#Nt`pb^X>s{`uOS*A0^k&FSqZH8m z9^v`pLLX-bl&j(|?dr5?RXp~}$g{Wa#M(BWfwU~S1-a|A$yQ2}Yh2dJ%KSl$^!L>E zaSk&<#KT$Tl9UxUJ~S^QUKdZ>aNb$Fw~@_58C{w(fVFl;q`SYT8#4~uR$v%8*+vKG zPEpI}hW0C&c_H_gl%6-;xTPz_Es9r+=OBgTShRn*35$qnxx;e5SYE%YV>NY^JV!XHxa{6NyYmO`;F`i5wvd;s%{xbBASv)cTiMEnDZQN*cSv z@&Q(rxc(NNZ0!^j?o{aRhHq!4j zzRuT*b(@|&>nr2s=jy)JX)7stwR%^>N!>WLs@XNmp!MU|wkwdic1&Cw zx4U(cSo#Fe>qh&A)t$6({AV5bs`~Yc^fEl|xqRqgyxInt!WDPJ`Ki=)5v!bf^3Zu5 z=DCghr_byJ-U8Pu-Lzb+)VsXsj>@F%uRC=|Y#%j~jvZA#l>8g?`n;2v_?GT_Ke}#96N{&7^L$yC z)YT`C-tDY;gmp%0JKNCmT7J@y)i5f z+*me`-%4|3Nt5;&tJy0DxK-qQ&hkkNAjFCdke9rZ^clj8yXc~rN{egE+oi@%uk9n~ir`uU+kly9$5_3q8@W4J(eGuS zOK=}L%suRT^@)rf6_=&dZnk_HK~SSZ8ToJSUDcgA|EfQ=ti6O*%dZ<;VPun!NV(J} z@*!AKwhqkmsyy1NJx|P)dhQClHIb{KGqzpz)tL2Op#cyH8F3O1>@YJk6mG8+=}fc` ziD3K!1Q29C$IN_4J+yav_y3Ql9+GkYau2iB)I!%*6U_;YT&NN9Vo)!B=2MpJa8! zljrwUwMFeX9~C{RL-?rld8+4^wtbFay(z;>9~(Kx9B~~M9l}v1Ls4xs_U_cC{Fx>* zNmj33KleRdl`pe2(et3&?rR(x(qnI^D`bn7wUIh&YxT4jU&c$;UHm*sFQag$M3S=Z z;@4f8&SoEmTUk+E#mN5gHQ8-ny>{E@$hymFoWmfT-7goW9vxzJm$n^Jx>MUSbQkyO zs(hJsmvQJWD(h@b=pDR-ZdQqH)%5czE`wI7=h3fJ_3d}@Xwj3nRYbPV;N>zt9-;UX z+~qA!f84^U3tU6y`|cwm#y*&moy@alct-gCL{7w>7P%w$@c%ky?hcE^WxDK|0Z+-@ z$5Y9wp^JG|&kbnpaeF6%JV}r?-uG z*?3Tmqt38@GsH)3z`y7Ci}bPRxJ`BaU770chRK?asa47O)N%RL%SJDEkDr1X>*`7R zpjX+mp}qmbQ~c{kNV7|z8qXZo+BweV8eAfEE#G=#{W?Z7zdfGI#^yMSj%9v7oBV}1 z(;4&?KEJzK?pft>&L!(|cn-s3{FL*S^;r!@x?E=Ddye7ceB){i=fME^#;*#ls!F9e zujYe6{UjXS=4JASNUCzC<9~tAs1<2;9%X3Oo!fM)TZZ&0XFIuQ=w=4>kvZb+C?mF= zBUPp!QXO@$sxH>ABDHA_=6T(^RCiT$rmP%&2`R)ZyuZPZ+-2mp6|b(8;~m}eNI2eO zd~r_US+^OjTqi3Ml=xumnXo6JEp}W%Zm&ijUHR+__86Q&6Jgf~(KC*qmAG9c+2ajXO`MDt)HP~d=2XN&072{yvm+M**^A5wl=$5o@aHphm(K9Wj2K9>Y{`= zImhlJsPzP2w2efDmootvWh7189!XO08NMU~A%w)@ljtLXmAB}Ne1@42SFe*vSHYw= zj;>fmC6_~f1A@>;CVA?=LbE2%epf=CGM9z0CmwG!V_omb|FKCL_cVC&#OAhIOSj(+7``BB~(1Es@yAcLtEKs(|ma@F%q&& z*Wzb611GcF32j2VLt=IBU(UVe*Z4HiZ2bA>Pw^DTck~)prwdLY?e;;rTCKi6 z_st#@`hj--7TNfPx2LUW7E>!*r(4I>A$CLky~c-ZtkqBk;1KiiVXT=^*nS>jPU@#y ztbSoME8F5AU$jGjG1^$du~T?Xvg|PWquTWGy-}87Q@IkP9j#``&`mP5S>-;6-{wAj zk-W{lmV?knW|k3p7%4PN#zp;8h~{-rSRu10%hi_|(k&YC+2lXr6N<5xyZckqctBH~ z@jOHhNVkIVFY{sv3z8Ws+<#*<{;Pe$ARH~9HY5d(B1 za`cLosfilD|D5!xQ>D+ds>}S}u0yVi0q!aAMgwnSfxJPD^Lt?C0sP266gFb~)A_iF z-aygeSH=G5LZ8D~AvsG}B7NPh0*yJ7`eucv;~Ia7^w$&P=vQrfE_^rs=}uj%!XNF| zGjqqP=K$28}=W%h$Zv4}g`WDMQ^;Sn0{?C@tA0ExBtQo~e(_F2{ zAMdT&y!G*V>Q5h`Q8HutEvSF!s8-eq(&z-^LA@r_XrjKwqMkwYX6}jcdt@GBG>BFf zbA-lGzp~nzgUds0X>hTeRIfj9jae7u>Sbby(~1#fbax`9-;$Zm_I>PpkDnp^*lBRF zl36suv{;@kmes+Xa0dp%EpBu!Ty;P$a{YDEM^Y;f79Fv(HM{SD|5BIVEo#>H>8p`6 zu5(>iZsxx;wjPv4H$GY+e!9>$YJ;BaW4iNji(;bQ%C@)IQLJ0KoW?^CO@iC-Yk`k=5HuP_Gd z=C2Cx6w&j#(@0MXgnf(bybF9Weo4fY6OCpVaSt=3v|^4c^%bJd4gNCKl2tl3amQ{L z6SreQR2U$k9;%){OCkbS)2q918l>prWylET)jj_RZKVK_PN-adXdu>yl7;rfT+ ze0_?_i|h#Qc26+=9(o^7;A?Rur>PmDbwxdny#{}Q4t9WyEcRq#ufc2l*Ul>Mc9lOL zr`?_XW4K-c<`Z;3akef?A+EPh?ig+?dh-{piQI*2C`uPx%-zT@U^^ZF&lHyh2V`}= zM1*Mzb_zSQ(iUaD5zmcY9#VWcXcsPyZtD@b3zyw@fN@>=HO3&vZY*uk-o>)*ZFfCl zw6$$+szGbjy?D2QC3J$$f4XTDQ6vYN1_wWx3jr&Xl=ao8EMaC@qxlJaRg6qe^qiO(ohxkq97b0L~^`vYzvgPZZ@E%5oD_={bprN9snwC|>VqC6jW-u$nrKcP?lrRcmhM%kD9Wwg)6^I6a2eH%O4^&n^~ z<=h+)gHq3-sf6U}-n=j18Myp=aAZ~EsrtByUVK8@DLk=-HdOAt#2%A(fLhfE4>d|z zE1p}dHNC^_PIU&xW(mdr7csxk8ZAasvnii6ehw=B6Tf${SK4XO$R6&lWLvBB(UT6? z$1+x?{Qych!}v##Z$^&UBZbyx5&N@F^fOE!0`0=uj)vI5da*R5UfgzGi*(x0Vs&@( zv_xE6{u-{4`i*kO!;qBB-84nQSNC)Cvd*dfRJ%+sN5e}=%~_-CR{b#qwxp53>8QhK zB?e=SP8rT78DfoA&1c>Y0kU_1w$QhTC2n4h4UyK>PU<@zRITrBo^aPk(2iEKWauUt z+N^RP#BXz-zDVBYUdut~dJq0EQpkd*wdb$^6gI9mf`4^x7Wt_>xS;|FkCEF{n0T`Nu)F<153S3j?J!aY}S=XW!`)W zo?s;vb8aO%6hi|-=~-TNlUM2XppP_5Hw%NNMkcsa*A+`Mg>|E_ZtLBLn!4RFOgTo{ zNX&q~-G4$Z&kz~QDAcE$MrdXGP(x^Jv+v#f9NnmGIeM~r!m@@rT<783n>_nGX#ddn zGrZ4(bgglBJElBLOpqDZW9?1eKf`a#y~%wUU|r~QIG?WFt%|y*dJjd@Fc~;kg>OK6 zZ3~}eAFE_#Bn)3h_bU8Z+7 zci|?bzelIr(0M1T-QmSYZ^ru;s&MOz62?1baKg@h9NJ}~uiB{dl+S z3S48 zzg=lM;mAG9*h+e{=k8x`YEZnpNQ$TU?+V|X34b1dI{7}=G{-ttC)Pn~Nx?_l*EabG z{V}X9y)7203{rM(Hr#v=*mjA&tTzv<@HB3jr$Zf z$P6p}4i@fF(W94I7JGmk;hUq{okH8q`De`T2}V08I?eVQ>p8qPJoo1s^WuzH*Lws# zd#_jlo`6Q1s^2}h;s=ElJj3{`rsjCBaZjFb@ALFEt*rDsYunfEeN+da5j++ z=+7`8YRNncuI=!!_(UR&*R4ZrJ%hF%vOAB+#6%}vJ)+{N8e+XU*nq=H?3dW z%diDc+$CslR*~LZS$=WGZ?PJBkFtz3GHG?|S^MeTZce=qMb~AWL{sdyT0U*FZp~iL zaj8tVq_r#1DSb%nDo$P_NA$06qgUxY=vlBu&eSGn@HO%f5smdUL{8qByh0A*$&Wmd z-~j*W`P=-TGv6wH{x?2*TKvVc3+Q_ijyuPb5Gg4$_&UduD-5Tu!upJBWhi&g@kM;& zj`;A8Iev93+xL2AsjzpqQm9Bnp)JyEJ!S17zw{I|!877z*3`0mV|@fQ@%iL)Qsu?| zNAFP$K+R*-t9dC`en(eFS7J=THRgGz_yjw3U3hd~qH12}n?lDV&Xj#h`&hUBD@>&O zKRC_p%7aQ<(7gn&*m+B}NNN?-PYsp7D=>{+y6EdC(g}*{sb=G%*ZxwbtT9|bHc+_neEpw4Tgb5)L0!V{IZVDWv8J$(ebZ)~ zy_)^B*KSdX;ROrv<*hg;f6q za^r2Gfl-UuhsOM2e%7h|_q32+Iy3)s=uBh=ub}l@VnljDc#_Z$g|rdh!d;w#{WZAl z^V0pO&FMY%y;;S-Via~-rH#D@9R5&Pw3p!Bmsqu9yh?tij1?rB6}W@`Wae+o*hu2Va2ra@rii7e~$ZKDD%78%dF ztD`cjAT>IMA{r+4p>~0ALe7-BTlk<1HN-`q7}b~EW`7-aU>0j@52yD23%KI1K9C$k zYT7&-RO-aaG)K;oyeH*ul@=O{-KpyRhS;;=I*02T`d6mB_uJU_bbC*0PNN92d)u%T?txNw@Xf#O z7k@pRe1rdEcceN-bw?Q4BRb8%55E-D^&UTu@ZSut9^iKy3D@navd2QKhIV)MkKyXg zKT2pyG30~PWsEcv<-Ss$6c;W-Nh=O%_XyHP0UQ8 z{M-Syj>TWvhA(5d!?h<)9BHTP(qHBp`_FgMLX1YBb6I8NQmti?dsIb@A$N;d`ejlX zn7MQeT&mpHpSId--%p^CuwVYn$?X@5t&Ar7(2E&Ur51ey34M&8)N}k={%gEP%kdQ? z12tdq&YlAU+uSo&((4*#1Y3rbVTH=F?Z`G%wG2uox~H{pGP-AUKbYh9Wr4=x^JjL3 z_*Pb(Co*blY?`QD%>mE4`&)PexJK7SgsWUjv26QF={IX`KKd3S_QZmh1wrfgTTyJ_zB(t)|Dx$GuuIGeoTGk4m^5#zK@+n;peN1rjT`vX5u@W07J zPM(kM?H6PT)w~-)`d_?T>*n63r(eWe{QoiYuC4z z?jGwG@zb@*+EZ0~(zWSNo2#rd#Y%WxjZ7&S!Az~Li?rGPTefG_-4Es2bw@CxyH{&* zw6fP;8R@3^q^S9&E#Vj1wM``8cTm1Ds zUXhHPBgZ;p4Hd^_rtN9r?c=qY-J#VG{dcckHDA8yORy7Zx9@52O-J>-$-W zCC{eMr5zrw3SCa~P}!|jdZ9{J7O9_>x@pNZru`{()4EsEv+p75Sma4{Ebh|g*$Sj1 z<`YQOIcwmyurJ&v{>>cg51=zvk=PW?{9jYQV^-AoA|%Vq?v=yd9jvM#6|zqVbLgdF zLRQ|cEwQV*%;>K%8@H3`9>%$`>VL>ntYTp$i1{p^k@I2a5$!0Ud)=SfeW$H-_i1vj z$Cy9+aBR&jmrS8|9qJ>!7)s-ckyedwOevaOKB{MYlJiZ)%~!qXW}K0b`M|&AXYX10badRs$ur^E9G^&kaFx7 zsCA}{!jvnfr31g|?Y0O`F=uF-r_;Y|EvzbW9v8Y=-H8UOuj^Rydwo~o06L#kyPI=a z-8EqCz38jdb#jF~EBj3znaDj-e6Em}-Uv4L${E^GcN z^a10?R4)g61F<6)`*G3tto;n$6`sQVBHN0-A@*SVV)A!nUsdu-4+Z*EjMe?(n0e9c1cv(#Kb6 zcG5TUX6|H4RDR>h68*K{U5yH9lvFysvyT^jAA;t@(4#pI-$yR%y=cCa(-OzEe=1Y$ zxUL&v@?&et%;YzgMBGEqCiZTUXr5R%hrn3kyy`nfdx^tKTR%##@A#yjyMk`6olA*+ z+4W^oWRq2t(_YxQ{Tlv0_7h`wG0HTakiZD3Qa#VfVJtIN9t}b9=~tC&no=VmTChXO zCq&>RA3)@O?5d58Dj!03PT6Pg4c1L+63UecjEORfgkI>=$^R5ul|=L1?r!3dCa-BH zVcCJ*LMOXiGx~ji)n{HyI_qV)9gq&qv7BJ`YYgZt`;gM&&;3}&(2pgSnl)2#mAznU zL8kVGHPy!tp(TW~0n?#n=F%~6sdAs_yo>Ef?OIByGRSS8uj!pvwi_}!`SR6Qt9!=~ z2H71>5dNW6mUBFVIh}vrMM>o+<~2y>gN-2ZQccGZ>FMHyJ)+3?Rly|E zV>8|~uh!4NNai^mw2T5T}VBlIP_Tdw(j>^)IVSU;T{h-${eM<64bP!-=nO zT4*|)AlD9^vu$#*CBHkCO3ihgrJD!doFhI43(2i_n$GhrwF`)5&(mUceYRo~rHmRZ zg9gn0EruoxE$RK%pGHzv>=e}@+`lPu+UU`_j?QW```->r%ymLHRk~ewI(m>a z%WhTa$fm8o7d2y-li!z4UG}UD#jiibrOYQB7sc5-K7(a&v63`s&(h|W^{33S>)vJg zW$&aSjQyI~UHes`O#78TA&>cWfUlwT`;bhw=hgO^#$iqNu^k>HjZ(TEp|V$>?=B*q zQtuHlUKx$~WN-4P!qd;%+^|Q>^#!$!FSyjPOmA|?8|Th+ES}AFPxTspOzrB)c;U%y z#7FsdG0~mOtwg1*qTcJK@EN-psk@<3X;Ne6R#`LoIZkdJr*2V={#T{C*^lO2j_stn zhg3=%@AbbS&*&2p34c6~to#CB(M6GA!WhmT{*vx*%2jc>Q&YtGFLAA8E3q%BjuWfx zHfLvzlQycJ>93Pk#WF^Aja@VO9kk}#XXCs<&fI%kX?nt&5q|@n5M9~F%e)$rtj#ja zjKkBz&WfsN=AY^L3PbB%qP3Y>JS(!D$OWP2GMvo(s4Azoow<%@BG2t+@Gs1QHRYE@ zO+I7Sq3hkaYUtCFr$8#ZnUE5g&vS{>)Mn^6_w+Uz8ye-`xbD2jq>+5L!wFv@<(^Y9%hf$3f*%^OJl_|^N}Lv!AD=x;Gm<4{ z>!*s&p?mx66G8mYmDN2Xt#;WjX5F#s;;6_HAeXUL{|Uai&KE(7eFci+I{Emu9%n+& zSKbvfx{aZljj7pQ-A8rhtY(d|7X*2qbS62TdZ$@KPDZRutzRALiROtj3QN{wMqxvT zZ7IE7ACA!I8HL4?8#@ZGi{8GC&R!{VN>p<~Gqx$JCYHL9)v!_UZQ49XEuc_4_OGpV0@gAlr>=s5UeMqho|VX}T4}&!ztPV(GVj zg!NdV_2JL^sB}t~*Xdfp-T1Sac1Xphl7|1eUhJ^d=Z?_c(x*nIR{I02UfJ#xKcJrA z5NFG3rAYO8e3r4q!>XSRKf9a?Pj8~wMn|w0c@mlCgVUyz)1<;6yF+6^I{Z7wdn8 znk?}Lw7XWGCFa^Rvcu)xE&*XRADW-zS@y<_TE9La>7)FZ`o3wTQ{LJ7 zon049dW2YW${x%_820);f$mK%bt4+md61qb<`GDgM`IddX=tw&`|N}tF%DywSA+DC z2=Qn=4N$Izt~S1n<-||K;*$OF#RkKck*i_)bkzD4s(&Z6ZdL3lQGqVhU&^FE}(^Uw;_?^dRCsrav7q55~m zB`L~WwpZZp6{wZj9n-?Yq4}?JF4Fy^wQX+qO;;u`&xE?)MIm9xowM64?6?fH8J^+* z=UKJsh?VyIZ0&;;%hBfZ>Fa{pcY3`lB=Hs2ofXs%lSaF2>6Fj^oD~tfeb|drL=~st zv3vL;(&1>Kj|vvnM#_K zOuGLC`ktILbcFwwkLW|}(st4~R?~@=b}x8y9+rKQ5UGZ^-+WHB+3oalYB`&q1aqXYV(`x?1Ie78H>pvi_rqoLF*`Ka|hV_Ce*H`SreD-JBvp<=m z%hOZOt=Oo^>rpT9*t}d-O@%c&zLUyRSW&b23Z4RKB)UAFPW7R=kIAZ1Pm=K+anD^; z`X+G|vvQA}6TzEJ^itFPP8A*hyDaV2|4L4ctaz-n%O`f%HQBS8}3YGbINg?k8TKC zBK@iA`BdtyTRBC4ce!T<<3H87&`|r3vYx_??d}3q&gOkaH0x(Jj~(bKVXN%-S8rSj z&Gi82K_5H&r8b>-tm`5u)aNYP`g4wBrMk`~?-9>2lig!vddt?w=uY`xHOFM+c=JqdN(ktB2UX;bZtu z3J)s%OjqDT7o)fzL-gPmPe{A{*a12`L(S+OzIXBWQ~dk|KkHt<-zsWkcG0DQG5I+D zfA-$BH;yCO7VXamIRAko%s}?WT#jg2_KXD?BTKT)8C%j7Wqad-fFSWDjYx`zY+1wE zIRE{wmBCWJ4&CscyIM7)VCljQgYr3 z4Ue3iy9w#)d%#MU*?p5{zc=4KR?D)i+h*ki*H5sZBKbPO2q!B_srk;dyw@>CAJ0O| zfpbcnguXFn^5vW&zlb%jf=#Wl_%cIYBY6II-%H@^Zb9JL<2p7|puI+u*JXY|nIV6S zm{nJ;X-N4p)uhajSM5s92(z`j&mH?`OmX_y2aNG|&HUE<`P3`a@2$H2=FSnRTb#4o z_4(1je0r|TkQWX43O&>$QNmH%(5I8S?*yDbh9z)6ec)#Tr44I!L6g%#d%d_id{UZQ}|1+|A*UoYgmR z?qp|_J!s*N@VprJl6bJux_&nM?2)}!=eP6Z^XD={o-r=T$P6RSyXd4$n}~=4we}&b zqs^AwOo{p&@qH-K>3dSDiC`UM(}^*%K&UBO#nN zM(?a)JlveUBnVrdY$GCWB63eH`Hoq$xT#rRih1ENNTD* z+*93lvCNR?M0obl=w~t1PkMJo19!POmfHP!lOS!dYK^~+c|kH)cHDD^&_a?f6rD+3a_S5SC8_R8S-(jZGHB) z%#dgAa?!+HQmM?4Ulj_i&-pAfpmjxlW?*qF|XJ-v=uy zGvs{_AjIkTTNo1A zZ;vT7qs)17OzLx-caZ(k7VEh5CSY{yE{!rV-hJRc$?xZ`3V0u0rrfLRo{~q@CByPl zcoj3q>zGxUA>YM0&8Lj|tn1Sj+j$0=ckH|~*1O9jh9jW!T~~nIXS{ zPK;Fl9DUQ8=`L#X9+X;ThJ4|4_48KCdaa&`#m_2SyUDJbt1GdoT~zCSbSHQ8gGt5kWj@Z1q7$WvXH zK;v3keI!agS;X!ETUlqoR=prT7cC2RPHf5yHwk(le$-Fk+1JSR)XFDA^Q;TPefY-W zaZO#1cCDGJS3)oYod}TNf%3WEaQ$P{jy=KmJ$O@o)zx#X^gPlp&cCCsOlr^6d!w(t zkvvmj^`CQ0glci8;ogEKCbid|uyXkvyxMOf%&rt zaa???*rfOQPKhc!*KFY^ZHkXYtALT8VG?xMr{XemeP@B??uGGT)8$F!M>oKY|IV_yyN23KXyMAyoV)5M<6d3w5g z74E{jwyq^zJ(SdUehDxC1HAAm0!2jPZ0m?^zvyic%6P80XNS;8VAmARwUzAW8SDz}zmKXkt6BVeU3fQmi@_WB&?j66^H z>fJJP6f!lYwU~dKPivJKp-nodplAg}qm@IQLn|cCC-cNjLi=}}d{|oJXO7ChL(@0) z&C(|>h(^g&D47bHQ@1z?R5BGV5s4QM^#Z=B#nB|VumrMnsigpLMxlsNLimx zF0iMYlc5~;0=r^UjOuy*{Cl;#(YkThP#UZUXQk}9QK)&!#VlMXa<0y6I-N&5&@jt| z5`sLxjuNgf1y-8_A4A?>U`O8|OXvW3LzHTqjmE5}{Yb6Qe7LZF9@d@fx}LajSv4vb<{Y#{tl;=TlbxJ(O2v3naLQ6(%Iluny2ltncu=dtXE{;#FRp;)Io7y{n z%)+ki_&hn~m)L6bN6yd}i}}Xky{OgCA;0z@w}@-8yBsrv zIc0s_q|>alxG6lgh3GK5@PAv;VTMO6bB;@H42-9M{Q-m~M2RR(M*7dcpLnxKzRPK7boP06}B#lMn|aK1y6t(b6jIjDNK!-9{pZnV-z5Ti-(m zzjoflC*1EG`4x4PVf{4oP#UbZMpm>qWzUU51=I7-VeY6!{j{~Cnybx$1=D+v>@}$k zw7j-hR4A1Gxw)<`0YVhBa9K{Dczl#NHM~{5KC{ok9X?hu64nAZw9Btz+ossIB7M1EzNS@;Q0xx+PIVus*d0%y%ewl7#AMC#*c}#G zD|SZ?yTj=D*jak?Fb*dF#{WHM9jRq!7F^tpF~8enlU1Hq zDH-$UgxE716=U)^1-;P@V`9^W_H174#$RDfM$-N#j`z;_4a}+t(Hf14tNs1ZAxgw! z#HS#l!>$tH6Un=NhBf?eT=@k2a8j|_3iB%EioW|%3p=; zvT?RgL)E(JyIfsoeYbf^=qnYUU(gfMc=`K~=9e(E;wgEnk}~8q+BNg=`9mqEUag#S z$zw2tH?Pck71+6TsTH1}-*9eNbHpxPtYQ?&@-0r3FPW;RuuC>EuPqIm{=#7nlp}Ia z{=S8K*E8H`!S~gY;oisnCEXM9j@9RcRN@YaoLMzpDZ1oG?C%Bkmv*4cAIi&fzKpMP z+_uBXhUrP!6XnJqvHFY3OkBTGB-Fk`VkN5ksvMuDA8ob%`~7v8h72V{yVN4qcL6Ja z(H-fT!;0z{BAG946;2**-b2&>j5+ZH>26im=X1$$3nsKV{CkFf@g+zfT8Fln$m(rS zpD_f+*;s@A2|6}Uj8JLys$v5+-M5LuX;D6feAMe{r3}|h>^SyR;S>FQ^-hrw;r>qeuqI{YYWYT*KE3vr;5$}8GR@PRZ zUm`O)lkbJ&K&h)aaOchTl=Aq}MfFJ4w75%n>eU7lj%mABm47I=_-WobhkVsD+O`IJ z-tff7Ii077UASktGOv+pxA8taUCgr(dBJB1-D_W-^0M32zA&xpd_J$@@>FhfE2M;0JC`>Lh9(e8dfCMXjxnz}if%Dqo zJie=BN-08>D${Pm^Njm~z9iH4v|(euPg`9}OltYm74#7pNDd9sk5`Lk7^WHeP**um=hi`A!&JzMb^hxv@->HUze)l*R{hBH%sbN=h8!!VAr;_!ch zgm@J>13Vp7ZsIwPpP@~30kd2%Mk=Zjr60B)N)>)KCIw8cbo(^pNr**wG@(>YdwSn$ z^>uAsKXg}^E#jGD8SF#OoR8{9oyZCF65^b_J3_vAESvKQqJi2^dmBH~u02Oq6f5-K;V=B%&juql z!Q94b_Eg=P81obj<-tXqA?j6MH}=ohR!#}y2&)=@@@Y}G6Uc*M&O`AnhWQqC>y)&e zN1nEM>D=p>@3BgG+Iso~_sW;0t+8~?($?SOeu;l-C_%=P+eKS1ueM(FneJOJFgBOY zSD!Z{vi&ge5Qoi$7n{`W=w5nEd$;#@l zb<@zFD)-FG3bs?(p1ak>&C_ce`VgME%q1*YS^cxJ+Ucy(chjO(ozhQJR%_kr8Z(c^ zVWqpj(wIkWQ54qdG*{GD*G!EQ%NDP`ugfP2qq(V7tj$@$+P6{PQC&~C?jd?Jb+5(l zE0v)6_f#!Sp)mK*>LB@>@_^O1rCCJvovk97Vy9K>ENNHdJF%q>l*qT(Jf#j)yU+HI zXV*``@H$ZQ8oM%TsuoqdX0<50Swz%WJWa96jP|Rg=;?knXr+F28RJs@BRqZLO&XI? z9_)?~m-^MNp4iJ@zq(p-&E#=PE>1t0wo@uPTFcit|)lI*C8f|&An%Q}X-9kQ~ zo5lt5Q<+`-9`Slk-c)Pr)OD;m$d>UZ)vVHS;Wymp@CxIM*(T3`U}!zPe!^d|;I*tm z3@h|~D?ioOh6bOc|DNVWJacY zp1wuHh@i$28ewcP7e>{eBH=m~O_i|g z)~)NA5r#kTCrW_$Y>EfYa-!9E6#1;#m0dP$bsdI;G)Eb7^e7oml8OyEv|>LdhqVU3 z#6QQ0fA-5v=_8qmp)^G{w;}nvs+f1KI;0*$A5d4+BUklGGfKGWHFjuPxWq&Kc&Pcn zKF(T|MvA0{Qn4EMc;GC>OGwM-*zNjf@=HV2ZRV4Y$4&Y?MzP$MtxYU4I{Uj~wnQ%N zE&jOV>~prR_D?q(-(qQhm`L~6mw4A}m#;+KaRjV@bD1u1LUrO^^?IMfLr?X@-#Aoa z*VC%ntL4I-X6q0vU7mJbkIUif9dbWDkLl<6?$vjW)(6i1Os&7D*FtK1G<{Ep-nY-* ze?ZOjztE>A^fLQ$pj~B=`$Yd>p8M1Hena+!7@=6Y{OBpHN@LrL)qIB4T6MRj5#lMiEJHohepSX08=#so6|S}!{V^_!ozo#J z38f0JI{Yf(>V{uO?bg|)S|8bC5wciroR9a%WPtyg(ZPRJpK5hanrUaVAG#yMx&1+& zWx?m_k%Rp6d#bjHU^cULLGJQs{T?%8dAF}%g~)szqDRDuPJrhAj_(g)duU&0i;S_i zUXxr(J!Q9nQ0aVn>Q9Zb*~#o{A)K!1Vbm}Q_QX6 zv~FT#^lw6B`x*Xb>kTauUpAzoWpUZ!uZdl|k0Iv{0#wDnS4{5vsN+2R;ySikmxNj) zNgA^I;SQc@>U>S2kYo4ED^l^uakcRGygJUPR<<)UzI06Iv-wKORl>BYr?)Q41OVpK$*SRZ`ma!)9)_d6=&%t~$223GcGqUC4x$6d?lY*owH^e_Q3+fC_!%N z*bfh+bvV1Nmj@3N8TbyG{dDq^!-n4@8cNCe8kQ>&hu7I^bu$X!T_-&a4-Kt;%{5J-RfuP51-XB24OJB{E6-HE@y_~q{(i~kA19); zm~8%>+?*SV_xLVdbSYu(;2-5TUmser zQf5pF{>EsGuPZpYFaJDUlhVU1!7%;jU`46de|$@JpO6UyJpVF-1F-DVB=z z(e4{(ilxbe(8le@t3~&Oc6#c3<33altz>$)?cQt*K??Vn-6d)1OD$9JS=48#(j}xk z{gZUv(PxKaI>heu2A-Pqy{2YRt6^6CGGBZ&^b!5Fk7HqQN8h=KHDlHJ`@vT=yja!! zyVtC;u8+rSUT zHnZI}9n<9rw4hOj0|E7bzq*r&!Kue>8MF54+3}mrj&%nU&(vj^B~KkDV|CYZvZX zmh;h{p5=7(&nlRgHuif;Yy(24-ozqP*TIJeJ!;q@H){SwOnYhdyR4rbR z6RXBsoc=KB6h=|{a`W^WmocP_NR3}hXgeXKmJFq`uUc1t;&fqlTIrhfv}v z_SWxeW!>k<{b3ITshFtGNH=&60kKHDqQ7vXhYnxWoco=~MaHLM6?T5VtT3V%i_maa z$Mc6{l~qLd%5)-1j?xF?-BNET51v6rw`D*Os<9{wDF`K=zGKrDa+Xi-tIx5yJv(F?(vr?)Qf90l1up^$- zkk+NC#BHl{O<3*z1}BLzyGy6H$CL20)u7d%Nay-7mc{C}l0h)6Ur49}*n5WBhCP;~ zk1V~(DY&?A6r!&t$j!s^)ey}X746sAHAG%4EoKc8523wv1{qTC?^(A9ts0#jWXH7* zi|jane0zvfSncW@kL1rQ-nl%oBMY?-Nx^$onx}B=B+BmXk9=l z(Q|ky+HI*!mOY)^cfLS>ifGi5XZWbGXIK7SROa`Fk&5aRMZYUTdn_U*D-1&nGuOV#%qOQbTCY^gm5(rFw@8fx|2He{?v0rdl=wDweE z?=r^Oq*wL$6U0qF`Ek-FDQxfNPgN{z9y5Mz1^#;T+gz(O{dVt4%fE((IJ@dxHS46* z=GnJRcMGR%&Uc??pRz`5`L)xoMC((h84tbhkO`|{9HH-OZ3f#S_h}mxPR;19MqK(n zF(co7?>A&$XqhoecZ8ZbcAT8THWOavcIX->cW<0LeJV)L4)mpVb{UxqnLzE*o{^aN157B%?E-nxsp?$lD zh)1~E=KkQuWwCQQWF?_);Z=uUC0yO`>nKmoF4a<}3{h6ww6}O{-JUKc1N_(8-?_Rc z&Ek@MYE^Rkjy%hPUM)+6#m_r3{L$8Vi1o7CEoc684Ev<_TyorKfsU2q7Va+bM@1r; znM3S?yg3D`ZV1P)Q`5y$^X2_e$NHJO_0w%MD)UF%v$bAddD*vts1BV!>hFb7{u!=Z zrhoRIS+S~JTh1YgZ1?-(@qIJZ?M~f%w!CtxOrUGZBlW#2(_Ym)QK|Eg=;M`%1PxJY6IkBk+Q2((;vj z6p0qC&eUMdS@L;MxS$^C)=2aTFhE*YMbdHKORoH|T=`{?bi;UjStOsF$1>+}1GAaF z!T*>n>*liYEVP1AvF_!3LVa?InCN^gD58Kv{XxTd!u+h1Nk}Dc^SwiOQ)s0bRz`0_ z)Vq2Gq+H)BEA0;9j#fJkl--lvtI}nbovL2TcE#ki?fVD-mY zW5kB@S70bITB_rlGQ(2(U5Dqgy~<%sS&^HE=WB16?NpT^j768KbL+5R)pPY7TK+7g z`FP9Ab0+ifZhfco(=q19RL^1`x{ilH<=2Fv)MgI4W&o91#D0Ax`fJR*wTNbmhR8@C z&YdDoDb?;KYxzp)np($AC?T{nKst@<*D=JT3n@##OH88KeY%mE^gAskchz{kDyJVJ zhWjrdPn^|2X=Z)Dsx^)NC#B>0Q@no~=^4jP=m*GYXKeWGGOU-oe&U4T4l=1#_X?aED$oy)Q7c~klwEPua)zFA25PQY?{1F3f0DxKou7tAxpZSs#6MHlUdI)y;{Uf9!gB?fianY`(JCc-FVu$+c}~ zM>47mK@5Ag`!gtJ3!&&KmuN%gKM7Ej)HCwV+2+=dGQC+N)R@6LQnoTEgNI zCZqZy2Yooc$P?8LM)`JGSEX(piC}IOD|L%xIYBO4ox@7hXzFtOR$sED@qV2H#*8MV z+AJYTrgFXAb{UEzawszwrIDpYkSZsA03t(Lp5bjb*dsI%c>)q;SLbtE-}*a zdQz-{F3NifMAqnup-(%eR!X%lySTm?g(^5_b^cbdGsVsnoh}t_p`M`q7Vngsj%ylc z5BFGS<@#kg%R?zW-q|gg{HvbHZ`OPmHyFxmsgZ>k=UCh$MCU0-Xuq${T!dlNQ>@7L zgg=Ecb;D62w7zLwY~tT+)tj?@x}GWb*5y)dKm&Y$=eL1=1PA!X?ql3bt3TSu%Ii;V z(!MK(GK|)C&tV#7*j*QsStU;x&Zq05YMaF_&#n=e7$vJ1=TK_R+Y^2TD_!PiPb=q+ z^axMg1>&@q*E1)WPsVPD9PmZ1yF17k1@d=DGxMKcxqdO-Hj=+*^_*dm{lwkRp)~}1ELKyy?XrqFF5KsF ztS0l_Li8fEk;qfiYpbfg3Myt-o3*t%xPnJLHD^6x!PLSbKBSk=~`o<-4XA&>sl3KrFjDT@y%rPi&MU zXt38f-y7F>&jrxdeVq3xPCJn>>gD~(dwjnOJo66zZ{h15hspAFzCWg0!pf>$r>E5R z=V!zdo#IX(kfFg>VpGe$aQFL5>m8c3Sn6!f7&TS0hzz@f9SCi@OHk|_^r18*T5yEb zvucoeI^W=LvrW%=0ByFsQCOS9C1>Q!QG)v60}v8&^Y5Swvu+L?EpFj;ncH;>$O-4? z5jD@ng^zHbbGHK0KF*6Fe#rH*3YIJVY;q5sJO7NYpI>A3rcDfg=t7laJYlqA-V@Z1mEGecla(ylHjycH*+wQPoKl9yoFBD`YQ9sc=qmN=A6DJT&~$2 z`i@`Sei9RtN@AjQse50)-xyqwzgj@$S36}k!ACwfZGn?QZc;0#YB!}w5aOabolJIb zYw|T@?-uqLjvx)D%WqLoGxAheFU`Ku4V+wD;Jo;4IM%S{O z7N&Zt^VyfWbtnGUET~NL4$hF*^@%xPt=Gv;417q8TBYMe;&Z;LLvfZZY8FDZu^h1tBSK@6v6hW4@8r@qbFg|5`?fW_#d zyr*EkS}E1K4CPKA$`?CRFcn6Eq;7F}{a$*G^P9ycHqE&8n>llJ`*Zz#&$3t$Ibz`j zx1kP`bGvBVM)8>EZauRg5B($YkK8?(LE&4al<35-d|k~rGMHSjoi{{xeaI^K}UgCXu^AW;~(IUl*6z2r;J`!~i zQd~9M&5byOnzU%O<F|{Vk?p#ZIYJtf2`ej4SQduH**f8+m_AOx(|;)`=h+{a)0YJ{k)I$ z>ypp0j_nxr!cXvh50gp#D?Gb zs7#MwEN>P?PLJk3>PMIPN#~o7ue6xmX7+8`P}}9ZNAck-7n=j?~YW2qNo4q?^he;KQo{IFgJso#)l6gtKr%kRI`V{l-E-_9==hCyD z`(Le~$zvnWH@}}Ua-8m~;n~rCnT(QKpY77Ko+s%D-j|?`?EXe<(eLTLG5G|vkC_7p z*FE6`q1?x7@|VwCfBRMMS#PxwFQEeu@N~X?M5)U;p8N}a)$c*8bJw9wI_0sm8e`N8Wc3;H=yUUW5Ip-Z{^iXt8$W z)1HH4`V^9_bkFtP+Cp~n=k7V!Z<1%rX6mrto;;gA*Yt|6p%!I6hjd0KuW^3#F<#+B z9)6A;xuinSpT#2<+gH~;9es_wyja+{vQ=q9`<8Sk4K;d8zGB~B<&Wj`dGf`p1dsRv zy(_oS^OO|f$rRpXM$`tnlaY_PBh%n~#Udf0B=`)p{T%BR@TW|J>Qk)!3zUMWx zPb#}>N@qTixUw&P4@~+6b?>B4_k(dPukhq@-LveOoQJS3#eZ_6Yt{Zu+c&e@7iBS~ zN3pq9%ayCsd3mK;&RB+=Tj{}jjtN#9uyQ}(1hFsd=Qz!JCX6%soQb|%Q{RPIxIKl> z?P)BwtCHk7H2#OlYG}IrJdw6{U6R_6o~lIAsCY=_dRDFfxw@{9Pcv#?riRcrm|tv1 zbjfUdC)cQ+Q`!ktPk{S8j^7$`sQRs8%=^~095gSNKapEkXU;=-(w5sc9hbXw&x_^$ zFXu~s2J4)b`i`mA>C1w~U?T8pPCIV#o5&h{f_>3SsfX~M&d`PZSL_?-00`Rf0O*6vfw|paN7#D4$IcOtdxP&pJ=@nn z%;~n1CR51nuOPwid%tncfy{^b5)%;opZ)iJ4cTvh`zx;3MKfZn#0be{#6pR)`kqpK z?_{0J6?tubPEKT@_q#E9=+2B_qV?xom8VLVTzSu4yA*v>!%E3d+8dcVdmbL*S-`nt z<7_XLIj7b+?Tk<>%p=dc(lqQ=_ z_pL+5L&yWOog=d%$!SekQPqbW+6nqkuv1@QirJnajY*f95xcU$hO$OADG3?pJ)jfW1K5cn5d-h*xP_ zH?N-&Y_2}Ma@TV(&UhQ2`$KJ(IITgAnafq1))eZ(@OqpA$$5~xhh2>no^_wY``mWc zYg!(O&e`IcnrEnsx3f88XMmiTfxk~aJta3L{w&HB{xf&&^%ndp-7|e}d>@z1zcD^0 zKbXEOQjA@H>(a1DF_Ww@DW=jP&izG-#cif)zoF2MU4#^C-^cZmVv1#H#OK=)Q3cDE zFbP|5UZcf^Lo?NC&WkmL`%F(awEMd)zu4|^?LWZgWECTA&uaCw%r36!|mY?=lJQh!;b|k)+h4mml|;uJe7%%P|U{l)r^@aJHQ z-b{>LskEr`%~cwDpC_6?4@bNM;-UM|cmij<(~r>S4{2vBD)9uKFOAPsb#Y#Qq-ifo zJfT{LH!QAc9*muKi*0n{nd=%)h)sc_zANzriNY?=H1_q1C-kiZ9nO<=D`w1ig6Ps5 zeL(rdmO)Hxmhp1IHJJr;;d)0AIhFoW%Nq%^9%qa?eFi&~^5u7j!hXbau)=~C@<+V- z4Su`jY-V;6t#_dos>~9f4DGD=G?Or1DICNu5M2E|V0vNqXS1JVb(&GkjCirknLQ(! z04Ht_r|vn4FZ(wB#Z{f!OIYA1z^bL6nAod(SNOQ_z|_^Ug&n$d3bVfY5>)&Esk-AK4B|ERS>la^*jcC)?!8o538fHfmLKZ%CUjAbaxd6??7(2B&_Az$do4JoUCGx9C7vL~{tGzQg3zAxP5C5l5CR59D zXGLD$g`aUJ`BX}rVzu^9^{L~d*&(1j5L;Pp|Y;wKKd}j8t za{US3#T@EXZ8QeQ=cg)Zn$sGfPG>irOZe!5g9vw~Qf<|^@*eEwdl~I!_6HICSH9A& zlKPn)P?o)gh@ZWi{Kvg22u|p$O0=10O?w{64m^asV6T(ZomdV2y$kBI+fhExWxO4D z2J8GVnf|t4t00|aA98y8VMg;qaQ*R>=2KMD{Kr*5#Ak^`gxO$=pAnsAVU$k3XOP$* zU`=1()aO6Nem!m79{n<_UMi=N(@~C`4qmR)sHbdp+$yxl@DQG}Wuc}DjB7LXGHbM2q-q*6P7nGr7%yIU~{#p0&$`5pnIBc6PK$cuE}5F~CD(2CU9>}+-`tK*p+ zLu%2hQhKFIQ?;HKz1sX1Z~hzt&3}_ApQISG4%z>nU-y96zrat@S%gTjV9Qmni>D`V zrik73cidmkkLF$RuVGHZg^P>n+Won!@yZI#*=+x7xQg&h$JTJap{7Rf#uHE4GCRU1 z5zKyFMg^vkNUlcaIlrv^N#zRlXR1;@{fc}lt0Isnvh```T2Dy%9qjHuBURVk&&AxL zGe{6peb`m{t+eYrNgSpxHC|1Aym|)qR|)T-T0~$%#x$O%zExC{ zwT>u!kv)rQ-aKIYHy@rbUef!mKab?h4_$mos;6!J zc{pc0GDL)`s6PKXk9bNmB4VfvtC<9~^825}R@jBy6f}AD(B!T?4f!~XgwTS}2;g!d zoBf{ZC2&lwB4oVq=sMz~=XO5PopM?!mDt6T(bc*H(%sOgG`W%W?A`cth}`y#28L%~ zk+GkCe7!2Cbv3bqdgq5a#~SCf=W=kGV?OL!YL*ST`~@e3m=I6F2iP5w1r_#dWgk=N zYpbU!ElX+>aehu0la2$sxRYKd)!J>(!&*(=s3X&V%~}V<7}+`Y)bcxgU7Q}be1+^d zX2x-PT~#V-hkbUtHCXOZWAEBGf?LF%zzt;DXoJ|D(ayWTB%3gogWdZtMh)rZL^obR6zkyY3ycctqlNeX_o1^}`!zcKX=+18XGM`-YvB{k;vZ+a*Pt z_gC~kqYvUi_PefeZPyCS#^`#d^%h89OY`%k@^AAM)`LRpb3VVwn)MKR!NX*4JMWN- zZ89`}t48{|PDFTx{x+syD&n;>XQb+P3J%D~4ZZs`UzXkpYkKO>6Z4D(ORB%O+rE~$ zFGLfy<65|5^eN9EUsJqiUw(#H>Dqx;m=o~M<+ktOPRw#Y3#4)1t5vZ@m%4hTFj_~vjIRoJp z%K87`&Ywgr%J~=n(7vwb`Ck|NC#(LLx-80Q{sp{Uet2Wc8OZ(_~%zfWH+Ntbp^Ukh|O!^A3=A7M- zDPeS;7VYvdq9J_(i9A`9M`Ie|X=qm#e}=&ixDI2NX9F>k=3S&_>^h^fecG^%#l%m< z_I=1B&$}qpyjv;fq;R^~+5S(fr77w>OSyaIa0B);tY<5I zk+rsscsA!boOm{(;Tw8d((=tNa0d4gDgv z)wSkTsv*(rS)H>PJ12k3oVTgx#PO3tTXlS-QckR-b=;Zmn|R z1o9V<|1v}F0KW~f{)c_4*=OkEuT-x>yT6UkLjO(aF*_fKgdsis88?OrY$8hj8Y_E` z|C`q}le=F2Q@>AEE$mb~O`rJYa)8JnPB>Cp%)a~no{z<0MbZqFPgNPQ3mx+|3IWR6UY$=M;S8VBTaQJS;rPVjDer~7z~(FE~N?0N5F%I{CuTVl+L zNLUVpPITwF-@rS0uc?#DTF+juQssRZcNI)YKKC20e+)VP1mCGQDcAK1&vDIjU=Ga3 zVua_D!wugDchr?hm6sxVvZ_=mzWUEOruVS8(~vionC?Hc*98x=&xU=fDkXEu*!$NS zyI3#9dXX%c_{_hTkiA5*o0rc~eRPxU5lP_Miw3f?%!d*f4@xZ+Rv8+%mcrNbfa%B=V?X<@t;qe|F0c{kW) zB8J_csZ!SG6He;hj7)k+ryN1$WJvb|6&y7OOy}4%LLXmTIUJL8x zE*+{PUF_D42ZvE$wK?!HcQqk#*G!(=h9L)(U}j3OI+3P)q;W?7{{*vyf)|*?KjHI1%%e4F}O{ed~2_RI2(o(+Aw)*ioOoY7L(f z&SbAc%Cb{Pu(0SO zXF@PJiy*XJFLvVWij3}r8Yo7yoTu^3@uqJN?onQT^H*Buwr!`4X}fcd06QR6ZQ0GM`nc+vX|IZo4|%crzAkRunEc_& zYVO0Cx+>Zq^ge|1eH&WeQC&~CZvN)q-*o@%dmVNeq=c7z=H=YqIwe`-x_>Qcesoq? zh{XGu-?Mu_x?}Z~s9)9m4qL0w1HK1f35zWg?F)& z(&iBeuFEH3wW`>&|G@JSmC|T1CEa*7zhA>J#R*WqRWS78(Ubb0L2emk#8Q!+d*QfF z;tvNE%$Yj#(jh>+#w`AN_r0n-Ls`Z1%{%8j=V{Lyp7J<%^W?CDfS&jvSLQWRB`@z2 zRz&fRA*SV@_wH5qxtgprCKLME`~Bp_Ss+))x8qB3NZ2(9L`JF~<*=D|H(G!j`aJ3b!L>T^D&_6d05cYOC{ zyrB);-O&CszbIXgeGg8Pn6qz_`B&(!V~^*vc;;oP+jG)GR9`>Th8jcSd>v+CivI1=k6{%MQJZ4V`E_tNGd8&xdoCvj z7yE5i`z_yYM2@fxMy*)N{|&C0>0Tn+6HKijLz+Q{Mm z_v6o^2Zrf^K2)AXzW2G$ta7{0z2+^CLo_8kVTyO<)Hb1)xCy_JkrRnD6EC}f4{?cC zHRF>uF*C&X@K0j9hYnTayJGuN^;3BD57(bfLE%>46G?eWA~Q1KwykpBMM>M)77iQ6 zVZ-P>3pWkBV>joRh7t6iD6LL#E>cLUS%&dQ7iDMRhOTSdz7NANhH=}xtzvt&sy$h+ zqkINiRV*n+J`WJr|A6n>dya@WGa6_ukhWn4(dyco)u)aQv4b#Vmqt`tyU$~wPsp= z;jl9^lWgY8UC{vDV{zZBwDt!^Z&eJ@LW;#^y+-fd+S9Bqs zOBd2={775%DeCbFt8pl~ekIp$aoWO8Vi@b_%fYlP1y4D2UOIdEms*WJwHhM%+a<*; zXm-CHZIacp$9|1TSvPx^)H{0(rS~xVt>4||9YZiFQ?<%$-Rv~P9ugnWW8uP81d`W? z&zxOZD72s@7o0jX)agU*ANF39YyO7&95&WYu;Th0ca^FP{e-__IS1^ZJA3^_ymH6Z z5dXW6-8NHPkS@(2O3#xB={>#;HHJ>)evQ-rEjAy9Xz_`w`8oxv-gnZD>Cw9Hbd$4< zSV4Mth1#0WLWz)uIfW-f^;!NJK$R7*5V1+~{A~?Q5~N?xhPadnV?1$cgdSinY1^M7 z;W`#gm9S6Ftw^U(Qj@2i9bfAmNgp_^#%#``$Y(8T(A6`vx{fYet@{4D70Gp1dp13o zu)EqNp659`S81#5e>Xl$7hjFFPv1E@A2_+m zERvLnM2`I&dfz_#u2b!2+fTd7BKL`UZJztn_kKh6oMP@xL9fXTNw98apR;O|D=^eVHT44tf$dte;Zq80+zv=ep8QpWKpb#i??g^jc=y zT=}jleQXz2ZS||N-fnFg+Pb+cA3ec-d~b-JYmfLhTjs`Du~Ru@C7}%ARfk_CT;1^N zXq1bqnN5cEku=uRB{uoP?~WWh_x@`}2me)ls#QE=!%)=mFsC|(GfqE2d+sXI+Kijf z6Dg=zd{X)@teA7uOwN{zHQT3Cy1xxU!i-nUm#6L9K3$}XkLJbcA#&wswbf6tQAg() zJOxT)ujbOWf(wM#Zq|*j1KXGVN!c-ur~~ciFw@}~{$}e9b)e~iRPE8+pV`)Av#We- z(Z84B1I9^psiXYFCt#)W4xVl5JPD#IV^@6?Twdxf#+N|EJ()>uT*_0GhRGZM^piyC zytRx{Dc;Z5KSA~ATwY1F+EeE!F{hDkqKY<*En8;m7aEgHQi;TVxSaZ+nc3Ce&ULPk zS_W1Ulnw?%YC?!$aV8wIpE@xV)Zh^L;2M8OflmOO=d#uYD%ATUANuqoUM!(glUxe(L&f zY`-+-S@MjPT+}`_qAB)O+D?y)fmt?e+G<22FJ3bE_7zXBP4V;f!AJasc(`Pe{A$Jw)SaQ4v3&b;at+@Y(9pV5ocw^8xjaak&+0I6qb*t6PO? z0Om^BjHtSd6YQ*-#Y*e>jpLzn*w*Bq@hsBu@Ibk8@qE)g-%u8Yu{6b1cy`GVW6I}O zF2gq;-_A>Jn_XJ9bQoQwEpg_s=4za>F5jNxeQzLHiDP^_gc zv-{{pvn|680=8%Lg2O$oYG7ijf(=>KBVX~OA7#>`P@4K!akO+A&r-jYewP?h(g)W{ zzmwYW^ODNO`D*VB=iCOAURLsxRVyQew=~8MhwKXAQ-{#LrcGdgTT4EK#4G zK`P2QW<5sOO(sV~LNeKyAEI+#)1R$5G9lSzbzU^;cKy_~t{2z|qKoffp|XNZbO?Vx zbT-%dmA77xd8A{5ew?I!9j(oq?J47NndT}2-=`->?qAg}N)-;DpPWMXK9 z4-XOlcUe~fd;j$Kf;csz_E zRjpcjfEP358kwXt8Oo zI~q8P>{`726t2GAPYAWr_&sRCwNp3ZFmX=x1?WZ=x6O`^%{4%slhUI`twm|%TWB9^t6d;E39Q^kIb~5LU z3g$#yg^2ugGT!q`bkXi)GLds~xF-5h{v6YJ)5g#3Qhs=iEa8I=s*G=~ZewruTn-CS zuNdwkd#asUC{tqCPeC;bszH91zTB&zdGd0;$QL~k%7L8+EwC}rwiTC>+sPI5WP zc9KPFOGQhl322+edsdE}AC*hG2&d2M>rg+B>&>W&Rb--#1y&2N@kl zj?7C-*)vx$4xOOabqf5?)pG~r(>x#N;1aPyi?nQ)T&t43%e|eav<$l@&RT8hcc<;1 z!Se&2bQ-DCV|JEw1ff09eil^n=abyboq5%1y|l0DIV3r=C|Ls|oy_%|{y&p%ot+`s zQlCNIhdA1uioB;~X(dkANBQJ|fQ?Nas(PhypGi@++lzP^%TJY1sK)AaYx zXY5zmKc~El$NOZIo88jqv<>IO#h)QcR;w6Kqh&cm&Hmx4SXtq|^S4+$W)?5BYFFj* zEC1xIlkndjbQ{>Z_XIg)Du}7NotMhuHVKudOLDfkww$6SYbiB8Y)@n=da_wQ+ zMZKz>WPUmAC0()P$2WQPq2&`=d|Q({*paFYC06Kc`yAeSS38F+#|c#)j`LeN zp)xWLDK*G@10 ziQB1APKnQFCw%@PJd-Q8 zxOt63IV+z(ODl5dDi+J%5(SFI&t)p6NV1Tp$KTPWsHf)kpSZ4FmyltLTqRF~MVDfe z85Tn~%6UeGS^r5KdLJ2Qk0<}U?#aP8E2E3Cy`7~#MD9|Ti@Ef>FR?8S<3}j#xlK)L z@-_4!=s`O1(^AJJa_$KG`x?DEHm)=9*fU@sw3~u@jLQ?t-$zc>?p2g_b3z>C4t_7O zzQx4)suH5kjk5Ez#4SRd*u-J^vv|miYu2>k+Uk7BzsGW3h^CD*lY0Cv?I}-bbSWKehg9kn@Px^HpaWFiPvN!dlz}O%OP)3IFf(-N;qsNz(Kar3g;uvFsT8hX#r!G8 zKDZxpvd9e^4yoyTk0u?__c~>*;pr%Jr7oWjzlQQFl$t4eN_>K*gS`P944dsaiUoF9H3 zS5eYeC5uUgf;r5NIViHAc@m^<49UtV$@^%KBFl5g@^p&lljiC7oK>FBDi7<{-Z-7| zQnBxJjYLoG9U_|SUb2s9z=q4!JV5Ry^L9SM3S(dQ4dg&ceI9!uUf}0F{A-RSG;1D{ zy~Hb1Wyg40r{vBoCU<5YTB4h~m^YKEA7Y$h^{}Cg)lM6XVu!V+50JHW59e&lRZ+6e zl<#4$x?`+_Q=SiyXUmK)+9u2iJ9h7|>7jebt~^-!|JV4x zg@60_?G8SF7yr9^zw|wqvCcGANDv zFqq$Q44PfKoM=wZigh^i*V%nv%GR)>b;uRN?hx{~^quOuQWpxbiT(MANP%P=o`DXG z9!XzkW+a|~LVGTf!#)j%;L%Ndm)>x%@%G-?*@Nn#F~@7n6b-T%Ln*>8>{7MV&v&|BXkL|@f@kryf8#IZr23j6q!;b;L;RCIcZE{T zlp;?_1rK8{TC*eifYUVSnNSbY8Y*<`PseSX-xKH4y>|6voT64Vg=vcL+%|Ee)O0g9 ziv9#`eA+C9j(zFKuJFu7dI!so9@>BK8_~Z)$-a~<6lXV^v%~dg`UIc`Pk9%b)hTxs|!7->zZaheF79oxL3Sn%1PpGm*C!_dqPyE4_%Ar zmy=6hf*+~T#WN(wauNZ*7MCq*~Rj-@XWQu{vU21+T%z~HsmN7| z+%vC3a@-3Q`&02TPAl`#j*5nW)!!`d;ylP4mnv`Wz$zia&z!!(k^O0^H7sFP&o<8g zAt+lY+MlAt2R=A{WUY9iV1EkkK<`NQ)=SWmKb5oHZr9Z1B029R=tc|iH&Bsz3Pe?m zqCep^iO)+!s&Hz5PGu%5z2b7thw-PL?xMw~kaDqn!4R2_SwM2qg_6uqDz;@iFOpK9 zAp3+;1vW^MZeH0lTV$(rF|adzSme3h8j zK7Qg1*h0x+lq~3BKgrj|l|>3RNx|*OGn`POhm2P;HX_rknOno@k@S_PIMMU2lUc3o z>2@kAh7;O~g(rvcoCM36T4($oFj`wY?p}Junmc(lGm86$~XWqF{$A$T-U{Q zKZ9x~j$)UnWfl~SbX0CEzH%EfDDv;Qv;OF%6n^XCw_o7Hoq*4Xa}kf&Gb z-&y}zWM8}NEA(li595@llm7!AB4Sm=5Xw1ilW9gf_5qu}*6aU>uTx07Tc}>;cbA^(r zl%!4a0hn&l5GhhlxQ~?*zN{-}xSyXNr(&^By6lrep=JtwbZCh5bfkyCT3VeYTs($$ z*;b`p;j<<_dj^Z`Uss&v_aiJ6YoZu^_d^%8v(CVqnA$6oGBl&DSRpBuhjCgyf#jo) zzX=rcCpY=~)Y-*;#ZKYRe*9@PWgO=SIhRw^D;`|`T>Oh>|Duaa3orKN#nNF)CjbqX zl8pJYqXjD#`MsRrz9+auFZx9<;&M=Xt(MBq9Mmkjq)nGFFF^Zp(FZ0+Dr#EfU%UK^ z(awL3xhk4-UHT-q;S1zCpK|0xzZ+8F#5PX+=pvBs9J*yS+{}_<>?4d=lCrD^xBzc* zYEhwVPRjZ-?;aJ}A|JF__k>(qY@NQgPDsBWovxv+Q);5xW9!xaKHE-uQc`ltY+YK0 z@8`Q&?On0}=_{xC+99WQ(OafB?t{jkpru0U7j2a;QM>3|{GwFdB$Q;_P&&9vr|m+& zRQmmdvs`@2?yiwlS@#s4IrH}|VqJJHmFGl{v)?Xr=l+B5ma+f6r@y7!Zi)W4d*`7B zZ=>x$oFC7gVTD(7@M=NSXY-5Nc=f*_T}vfdu9IKRd+K>Flxm_BPit6_!n|2le4aqB z6rIxS{lzH9eh6o$&70miupu1}+&m_?HIlcdFBZ?o8~9@?*WkP5DHTH8;n(TW|Hc8h;$&!)L&qi#cWzio^`lFd@Jh?@OoOfUoM!yny>5i}( zwU}{wg!n4a#q-j!7S!k~?cRcJk}J-<2znHcfF&~HnYD#w)@Hk>`x3OO((jp@Cvxt1 zl2#)-br<`&*?x}E@Fh6#0zHPPJs#s{)^V}6nAv27mcwb8VhwIhzL{)IzMg!G>BW?) z>@`1~Y)o!>9Vj_5M?4*^k*6ePCOS_8t$AW(x9}-Sl?{iTF{hPR={pPT0_W%bWOs5G zJAViNxA652e*f9warCxWb8v_?YSuibGB9`Hk*kT36-`~A2u?73clB5Dls=uT^$@>v za@qz?Eq~HyvsM3s&+{2CaQ_$X{%`U10KZ+})jfRQ0f&-z-=crON4)YLT+96Yhj{I5 zB0bya-EdXIKDBvuk0ZA;=T~*${q7vU#wOOwRm!t-_datcPpMSrfu>z&za~n)Q|#RZ zuEYHxFH9$&gS-CsW~#O^M3YQyKIB%n<2 zpa1rR6rOdT!~5LEb8K9l>kX{ucgJrZ-F{HJbAQR@q`f_37bk3D?X#WQ**+bfcG6Jy z$)_iusf#Q8XVz=jYvEpRO}@I`GktG-AD5XvpW_)+ihbdv*gfb=N(AbnV`l+U;!`G_ zxLF35?ypr1X|j;=u=yWP8t2qe0#M3{gfY2y`+7C;YxLE*jL}ecQP0Kn>{Iw!FP(g% z9;0@nbe6hPdLXK+si*ml*}W3qBJ7&3VR|Lpk#2bv+Yh~S(M`1Kif%G}8PiRw{o^vN z=%%*4`pT)j$<-O1sle`$( zJTbkg9KyX;5Au8bB-1Q(9>W9pCDKWHioeiH;=A`JUpOmq9=xFIOy^_$MEHFjc*@Ys zP3!@CE$a^C=PGh1k(%FO*%EnnRAU!nQ$Oq9dAD#Q|yXnyF%-p*|%CIb{DQ{;wq&TeNOFRGQZA*cMuz*701q0f|^{y+dX${ zVT_3J-BV{t(6jDySPH6tOG$HAxwqsE^jI;2?^%b2gQ3p#MN!mf+$e+HV6 z5<~&m!<2DUjjtX!THNy9FR?^sqx3~vnzBg39Fcgmi*fi9hgF=y4&{2elM?a0hj`|n zQ7iWOT>SUGPrcYUo}M~ZFEM^e+r8SwrO2!tO26716wAq3*U`7Rd|=~DUwdW*nc2K5@-)sT9nIC?mkzMXupM36NocI*n z@|&aF=@lx-x~@B@ySeWwyzU^&ia*^$pQEj0G_HP)I)5xD?%1a`t34x?wH|-1@Qv)io25Cg8(42T5KT8h*YY;^%Qb7!`|*Yi8mxiQ%$TY=Wt@Cu7u8k9QggR93| z{Vr(BPib00#U32b9()FgEB0cc@_1CvXInqVZoP3)Bvx39?Mqvl89Upsu+Jcyn3c|| zOwpabE~M7(wtr3A=8raxXZ6aJIT_rj&_=CIP|3HcNt-WV2hLA#s=d_L7S_+{_jT33 z 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 z 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 From 3d295094362e6f3433de8e36502c859b945e36b5 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:38:50 +0100 Subject: [PATCH 005/680] Modernise build - Use .NET 8 SDK as .NET 7 goes out of support in May 2024. - Support C# 12 in all projects. - Bump Newtonsoft.Json and xunit to latest versions. - Use NuGet central package version management. - Enable package source mappings. - Configure recommended properties for deterministic builds. - Configure recommended properties for NuGet package publishing. - Use artifacts output. - Add code coverage. - Prepare for GitHub Actions CI. - Temporarily suppress warnings about out-of-support TFMs and NuGet package dependency vulnerabilities. - Simplify target frameworks when only one is present. - Update some terminology/typos. --- .editorconfig | 5 +- Directory.Build.props | 76 +++++++++++++++++++ Directory.Build.targets | 27 +++++++ Directory.Packages.props | 32 ++++++++ NuGet.config | 12 +++ global.json | 9 ++- src/Directory.Build.props | 25 ------ .../Swashbuckle.AspNetCore.Annotations.csproj | 2 +- ...hbuckle.AspNetCore.ApiTesting.Xunit.csproj | 2 +- .../Swashbuckle.AspNetCore.ApiTesting.csproj | 24 +++--- .../Swashbuckle.AspNetCore.Cli.csproj | 4 +- .../Swashbuckle.AspNetCore.Newtonsoft.csproj | 12 +-- .../Swashbuckle.AspNetCore.ReDoc.csproj | 10 +-- .../Swashbuckle.AspNetCore.Swagger.csproj | 6 +- .../Swashbuckle.AspNetCore.SwaggerGen.csproj | 8 +- .../Swashbuckle.AspNetCore.SwaggerUI.csproj | 10 +-- ...hbuckle.AspNetCore.Annotations.Test.csproj | 13 +--- ...shbuckle.AspNetCore.ApiTesting.Test.csproj | 12 +-- .../Swashbuckle.AspNetCore.Cli.Test.csproj | 12 +-- ...hbuckle.AspNetCore.IntegrationTests.csproj | 16 ++-- .../TestSite.cs | 15 +--- ...shbuckle.AspNetCore.Newtonsoft.Test.csproj | 14 ++-- ...shbuckle.AspNetCore.SwaggerGen.Test.csproj | 15 ++-- .../Swashbuckle.AspNetCore.TestSupport.csproj | 6 +- test/WebSites/CliExample/CliExample.csproj | 4 +- .../CliExampleWithFactory.csproj | 6 +- .../MultipleVersions/MultipleVersions.csproj | 4 +- test/WebSites/NetCore21/NetCore21.csproj | 2 +- .../NswagClientExample.csproj | 7 +- .../OAuth2Integration.csproj | 4 +- .../ApiTestsSetup.cs | 30 +++++++- .../TestFirst.IntegrationTests.csproj | 13 +--- 32 files changed, 273 insertions(+), 164 deletions(-) create mode 100644 Directory.Build.props create mode 100644 Directory.Build.targets create mode 100644 Directory.Packages.props create mode 100644 NuGet.config delete mode 100644 src/Directory.Build.props diff --git a/.editorconfig b/.editorconfig index 49abe2ae52..f274c474de 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,8 +14,9 @@ indent_style = space indent_size = 4 trim_trailing_whitespace = true -[*.{proj,props,sln,targets}] -indent_style = tab +[*.{proj,props,sln,targets,yml}] +indent_size = 2 +indent_style = space trim_trailing_whitespace = true [*.{kproj,csproj,json,ps1,psd1,psm1,resx,rst}] diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000000..428b863177 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,76 @@ + + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + domaindrivendev + SHA256 + https://github.com/domaindrivendev/Swashbuckle.AspNetCore + true + Copyright (c) 2016-$([System.DateTime]::Now.ToString(yyyy)) Richard Morris + true + true + false + + latest + true + en-US + + false + $(NoWarn);NETSDK1138 + + $(NoWarn);NU1902;NU1903 + + $(NoWarn);NU5128 + + + MIT + https://github.com/domaindrivendev/Swashbuckle.AspNetCore + + See $(PackageProjectUrl)/releases for details. + false + true + git + $(PackageProjectUrl).git + snupkg + true + true + 6.5.0 + false + + + preview + pr.$(GITHUB_RUN_NUMBER) + + $(VersionPrefix).$(GITHUB_RUN_NUMBER) + + + + + + + + + true + + + cobertura,json + $(CoverletOutputFormat),opencover + [System.*]*,[*.Test*]*,[xunit.*]* + GeneratedCodeAttribute + + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 0000000000..719ddb4f24 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,27 @@ + + + true + + + true + + + + + + + + $([System.IO.Path]::Combine($(ArtifactsPath), 'coverage', 'coverage')) + true + HTML + $(ReportGeneratorReportTypes);MarkdownSummaryGitHub + $([System.IO.Path]::Combine($(ArtifactsPath), 'coverage')) + $([System.IO.Path]::Combine($(ReportGeneratorTargetDirectory), 'coverage.json')) + <_MarkdownSummaryPrefix><details><summary>:chart_with_upwards_trend: <b>$(AssemblyName) Code Coverage report</b></summary> + <_MarkdownSummarySuffix></details> + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000000..6c9d112f2f --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 0000000000..38ac8e75ec --- /dev/null +++ b/NuGet.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/global.json b/global.json index 2cca158af1..7c4a0199b5 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,7 @@ { - "sdk": { - "version": "7.0.100", - "rollForward": "latestMajor" - } + "sdk": { + "version": "8.0.203", + "allowPrerelease": false, + "rollForward": "latestMajor" } +} diff --git a/src/Directory.Build.props b/src/Directory.Build.props deleted file mode 100644 index acf8dce7cd..0000000000 --- a/src/Directory.Build.props +++ /dev/null @@ -1,25 +0,0 @@ - - - - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - true - true - https://licenses.nuget.org/MIT - MIT - https://github.com/domaindrivendev/Swashbuckle.AspNetCore - true - git - https://github.com/domaindrivendev/Swashbuckle.AspNetCore.git - 6.5.0 - 9 - - - - preview-$(APPVEYOR_BUILD_NUMBER) - - - - - - - diff --git a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj index 3a382718c3..98b1f795e1 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj +++ b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj @@ -6,7 +6,7 @@ true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;annotations - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/Swashbuckle.AspNetCore.ApiTesting.Xunit.csproj b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/Swashbuckle.AspNetCore.ApiTesting.Xunit.csproj index a06d6a4938..9d73bfbd0e 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/Swashbuckle.AspNetCore.ApiTesting.Xunit.csproj +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/Swashbuckle.AspNetCore.ApiTesting.Xunit.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj b/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj index ff201852ff..f35d43e46c 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj +++ b/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj @@ -8,30 +8,30 @@ netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 + + + + + + - + - + - + - + - - - - - - - - + + diff --git a/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj b/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj index c1c90fb57a..65231b9115 100644 --- a/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj +++ b/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj @@ -15,7 +15,7 @@ - + - \ No newline at end of file + diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj index cb44a7565f..56bfc04442 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj @@ -4,7 +4,7 @@ Swashbuckle.AspNetCore.Newtonsoft.snk Swagger Generator opt-in component to support Newtonsoft.Json serializer behaviors swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;newtonsoft - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 @@ -13,23 +13,23 @@ - + - + - + - + - + diff --git a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj index 2e08097a1d..7f9de9d46b 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj +++ b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj @@ -6,7 +6,7 @@ true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;redoc - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj index 950e475274..f7bd478f52 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj +++ b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj @@ -6,12 +6,12 @@ true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 - + @@ -19,7 +19,7 @@ - + diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj b/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj index 7781dd492e..6ca8869444 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj @@ -6,7 +6,7 @@ true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 @@ -15,9 +15,9 @@ - - - + + + diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj index ebc70ddb71..f4e88f45ef 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj @@ -6,7 +6,7 @@ true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore - true + true netstandard2.0;netcoreapp3.0;net5.0;net6.0;net7.0 @@ -16,10 +16,10 @@ - - - - + + + + diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Swashbuckle.AspNetCore.Annotations.Test.csproj b/test/Swashbuckle.AspNetCore.Annotations.Test/Swashbuckle.AspNetCore.Annotations.Test.csproj index d0912b52c7..fed1d26b47 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Swashbuckle.AspNetCore.Annotations.Test.csproj +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Swashbuckle.AspNetCore.Annotations.Test.csproj @@ -1,8 +1,7 @@  - 10 - net6.0 + net6.0 @@ -13,13 +12,9 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/Swashbuckle.AspNetCore.ApiTesting.Test.csproj b/test/Swashbuckle.AspNetCore.ApiTesting.Test/Swashbuckle.AspNetCore.ApiTesting.Test.csproj index 2bf0784f51..8bd159a0bb 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/Swashbuckle.AspNetCore.ApiTesting.Test.csproj +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/Swashbuckle.AspNetCore.ApiTesting.Test.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0 @@ -9,13 +9,9 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj b/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj index 32a8170152..da3d75f117 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj +++ b/test/Swashbuckle.AspNetCore.Cli.Test/Swashbuckle.AspNetCore.Cli.Test.csproj @@ -1,7 +1,7 @@  - net6.0 + net6.0 @@ -11,13 +11,9 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj index 766a12c32f..e19e5cf393 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj @@ -4,7 +4,7 @@ Swashbuckle.AspNetCore.IntegrationTests.snk $(NoWarn);8002 true - net6.0 + net6.0 @@ -22,15 +22,11 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - + + + + + diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs index 5ff8606862..475d400c18 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs @@ -18,11 +18,12 @@ public TestSite(Type startupType) public TestServer BuildServer() { - var siteContentRoot = GetApplicationPath(Path.Combine("..", "..", "..", "..", "WebSites")); + var startupAssembly = _startupType.GetTypeInfo().Assembly; + var applicationName = startupAssembly.GetName().Name; var builder = new WebHostBuilder() .UseEnvironment("Development") - .UseContentRoot(siteContentRoot) + .UseSolutionRelativeContentRoot(Path.Combine("test", "WebSites", applicationName)) .UseStartup(_startupType); return new TestServer(builder); @@ -35,13 +36,5 @@ public HttpClient BuildClient() return client; } - - private string GetApplicationPath(string relativePath) - { - var startupAssembly = _startupType.GetTypeInfo().Assembly; - var applicationName = startupAssembly.GetName().Name; - var applicationBasePath = System.AppContext.BaseDirectory; - return Path.GetFullPath(Path.Combine(applicationBasePath, relativePath, applicationName)); - } } -} \ No newline at end of file +} diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj index 5a159eda70..958de8d8fc 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj @@ -1,17 +1,13 @@ - + - net6.0 + net6.0 - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj index 7270e0f54a..d86382c907 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj @@ -2,8 +2,9 @@ Swashbuckle.AspNetCore.SwaggerGen.Test.xml + true 1701;1702;1591 - net6.0 + net6.0 @@ -13,13 +14,9 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + - \ No newline at end of file + diff --git a/test/Swashbuckle.AspNetCore.TestSupport/Swashbuckle.AspNetCore.TestSupport.csproj b/test/Swashbuckle.AspNetCore.TestSupport/Swashbuckle.AspNetCore.TestSupport.csproj index 41fd347761..befe7167b9 100644 --- a/test/Swashbuckle.AspNetCore.TestSupport/Swashbuckle.AspNetCore.TestSupport.csproj +++ b/test/Swashbuckle.AspNetCore.TestSupport/Swashbuckle.AspNetCore.TestSupport.csproj @@ -1,9 +1,11 @@  - net6.0 + net6.0 false false + 1.0.0 + @@ -15,7 +17,7 @@ - + diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index de789733e5..0c9b782c1f 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -13,7 +13,7 @@ @@ -31,4 +31,4 @@ - \ No newline at end of file + diff --git a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj index c47814ab14..a31db2520f 100644 --- a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj +++ b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj @@ -6,7 +6,7 @@ - + @@ -17,7 +17,7 @@ @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/test/WebSites/MultipleVersions/MultipleVersions.csproj b/test/WebSites/MultipleVersions/MultipleVersions.csproj index 28a77e2c5f..33ec719e11 100644 --- a/test/WebSites/MultipleVersions/MultipleVersions.csproj +++ b/test/WebSites/MultipleVersions/MultipleVersions.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/test/WebSites/NetCore21/NetCore21.csproj b/test/WebSites/NetCore21/NetCore21.csproj index 952bac97b9..92ab0dbcab 100644 --- a/test/WebSites/NetCore21/NetCore21.csproj +++ b/test/WebSites/NetCore21/NetCore21.csproj @@ -6,7 +6,7 @@ - + diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index 5ad96bbdfc..60282ca8f8 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -6,11 +6,8 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj index bbdc1e6830..31bcb43085 100644 --- a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj +++ b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj @@ -5,8 +5,8 @@ - - + + diff --git a/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs b/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs index b22d795461..ac9442e9bc 100644 --- a/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs +++ b/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs @@ -1,4 +1,6 @@ +using System; using System.IO; +using System.Linq; using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.ApiTesting; using Xunit; @@ -11,9 +13,9 @@ public ApiTestRunner() { Configure(c => { - var apiDocsRoot = Path.Combine(System.AppContext.BaseDirectory, "..", "..", "..", "..", "TestFirst", "wwwroot", "swagger"); + var apiDocsRoot = Path.Combine(GetSolutionRelativeContentRoot(Path.Combine("test", "WebSites", "TestFirst")), "wwwroot", "swagger"); - // This app demonstrates the two differnt workflows that can be used with this library ... + // This app demonstrates the two different workflows that can be used with this library ... // 1) Import OpenAPI file(s) from elsewhere (e.g. created via http://editor.swagger.io) c.AddOpenApiFile("v1-imported", $"{apiDocsRoot}/v1-imported/openapi.json"); @@ -32,9 +34,31 @@ public ApiTestRunner() c.FileOutputRoot = apiDocsRoot; }); } + + private static string GetSolutionRelativeContentRoot( + string solutionRelativePath, + string solutionName = "*.sln") + { + ArgumentNullException.ThrowIfNull(solutionRelativePath); + + var directoryInfo = new DirectoryInfo(AppContext.BaseDirectory); + do + { + var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, solutionName).FirstOrDefault(); + if (solutionPath != null) + { + return Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath)); + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo is not null); + + throw new InvalidOperationException($"Solution root could not be located using application root {AppContext.BaseDirectory}."); + } } [CollectionDefinition("ApiTests")] public class ApiTestsCollection : ICollectionFixture {} -} \ No newline at end of file +} diff --git a/test/WebSites/TestFirst.IntegrationTests/TestFirst.IntegrationTests.csproj b/test/WebSites/TestFirst.IntegrationTests/TestFirst.IntegrationTests.csproj index 2b47923076..6769f0427c 100644 --- a/test/WebSites/TestFirst.IntegrationTests/TestFirst.IntegrationTests.csproj +++ b/test/WebSites/TestFirst.IntegrationTests/TestFirst.IntegrationTests.csproj @@ -5,16 +5,9 @@ - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + From dc519107e27ad7cd4e5aa9b45d9a1b367496c055 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:39:27 +0100 Subject: [PATCH 006/680] Remove licenseUrl Remove `licenseUrl` as it is mutually exclusive with `licenseExpression`. --- src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj | 1 - src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj index 81c8d03a9c..7719f889e6 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj @@ -23,7 +23,6 @@ description=$(PackageDescription); id=$(PackageId); licenseExpression=$(PackageLicenseExpression); - licenseUrl=$(LicenseUrl); microsoftExtensionsApiDescriptionServerPackageVersion=$(MicrosoftExtensionsApiDescriptionServerPackageVersion); projectUrl=$(PackageProjectUrl); repositoryType=$(RepositoryType); diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec index f355c947ba..0231c6d279 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec @@ -7,7 +7,6 @@ $authors$ false $licenseExpression$ - $licenseUrl$ $projectUrl$ $description$ $tags$ From ff8f4beaca083823b60beffda74d388b61cec024 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:42:29 +0100 Subject: [PATCH 007/680] Add GitHub Actions workflows - Add workflow to build, pack, test and publish the project. - Add workflow to run CodeQL. - Add workflow to lint GitHub Actions workflows. - Add workflow to generate OSSF scorecard. - Add workflow to update the .NET SDK. - Add workflow to prune stale GitHub issues and PRs. - Add simple build script to share between CI and local development. - Remove AppVeyor CI. --- .github/actionlint-matcher.json | 17 +++ .github/workflows/actions-lint.yml | 36 +++++ .github/workflows/build.yml | 166 ++++++++++++++++++++++++ .github/workflows/codeql-analysis.yml | 54 ++++++++ .github/workflows/ossf-scorecard.yml | 44 +++++++ .github/workflows/stale.yml | 29 +++++ .github/workflows/update-dotnet-sdk.yml | 32 +++++ README.md | 4 +- Swashbuckle.AspNetCore.sln | 33 +++-- appveyor.yml | 39 ------ build.ps1 | 30 +++++ 11 files changed, 435 insertions(+), 49 deletions(-) create mode 100644 .github/actionlint-matcher.json create mode 100644 .github/workflows/actions-lint.yml create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/ossf-scorecard.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/update-dotnet-sdk.yml delete mode 100644 appveyor.yml create mode 100644 build.ps1 diff --git a/.github/actionlint-matcher.json b/.github/actionlint-matcher.json new file mode 100644 index 0000000000..4613e1617b --- /dev/null +++ b/.github/actionlint-matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml new file mode 100644 index 0000000000..342326cc13 --- /dev/null +++ b/.github/workflows/actions-lint.yml @@ -0,0 +1,36 @@ +name: actions-lint + +on: + push: + branches: [ master ] + paths-ignore: + - '**/*.gitattributes' + - '**/*.gitignore' + - '**/*.md' + pull_request: + branches: [ master ] + workflow_dispatch: + +permissions: + contents: read + +env: + FORCE_COLOR: 3 + TERM: xterm + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + + - name: Checkout code + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Add actionlint problem matcher + run: echo "::add-matcher::.github/actionlint-matcher.json" + + - name: Lint workflows + uses: docker://rhysd/actionlint@sha256:daa1edae4a6366f320b68abb60b74fb59a458c17b61938d3c62709d92b231558 # v1.6.27 + with: + args: -color diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..2b87d1cde5 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,166 @@ +name: build + +on: + push: + branches: [ master ] + tags: [ 'v*' ] + pull_request: + branches: [ master ] + workflow_dispatch: + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_GENERATE_ASPNET_CERTIFICATE: false + DOTNET_NOLOGO: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: 1 + NUGET_XMLDOC_MODE: skip + TERM: xterm + +permissions: + contents: read + +jobs: + build: + name: ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + outputs: + dotnet-sdk-version: ${{ steps.setup-dotnet.outputs.dotnet-version }} + + strategy: + fail-fast: false + matrix: + os: + - macos-latest + - ubuntu-latest + - windows-latest + + steps: + + - name: Checkout code + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + id: setup-dotnet + + - name: Setup NuGet cache + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }} + restore-keys: ${{ runner.os }}-nuget- + + - name: Build, Package and Test + shell: pwsh + run: | + ./build.ps1 + + - name: Upload Coverage Reports + if: always() + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: coverage-${{ runner.os }} + path: ./artifacts/coverage-reports + if-no-files-found: ignore + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@c16abc29c95fcf9174b58eb7e1abf4c866893bc8 # v4.1.1 + with: + files: ./artifacts/coverage/coverage.cobertura.xml + flags: ${{ runner.os }} + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Publish NuGet packages + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: packages-${{ runner.os }} + path: ./artifacts/package/release + if-no-files-found: error + + validate-packages: + needs: build + runs-on: ubuntu-latest + steps: + + - name: Download packages + uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + with: + name: packages-Windows + + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} + + - name: Validate NuGet packages + shell: pwsh + run: | + dotnet tool install --global dotnet-validate --version 0.0.1-preview.304 + $packages = Get-ChildItem -Filter "*.nupkg" | ForEach-Object { $_.FullName } + $invalidPackages = 0 + foreach ($package in $packages) { + dotnet validate package local $package + if ($LASTEXITCODE -ne 0) { + $invalidPackages++ + } + } + if ($invalidPackages -gt 0) { + Write-Output "::error::$invalidPackages NuGet package(s) failed validation." + } + + publish-myget: + needs: [ build, validate-packages ] + runs-on: ubuntu-latest + if: | + github.event.repository.fork == false && + (github.ref_name == github.event.repository.default_branch || + startsWith(github.ref, 'refs/tags/v')) + + environment: + name: MyGet.org + url: https://www.myget.org/gallery/domaindrivendev + + steps: + + - name: Download packages + uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + with: + name: packages-Windows + + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} + + - name: Push signed NuGet packages to NuGet.org + run: | + dotnet nuget push "*.nupkg" --api-key ${{ secrets.MYGET_TOKEN }} --skip-duplicate --source https://www.myget.org/F/domaindrivendev/api/v2 + + publish-nuget: + needs: [ build, validate-packages ] + runs-on: ubuntu-latest + if: | + github.event.repository.fork == false && + startsWith(github.ref, 'refs/tags/v') + + environment: + name: NuGet.org + url: https://www.nuget.org/profiles/domaindrivendev + + steps: + + - name: Download packages + uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + with: + name: packages-Windows + + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + with: + dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} + + - name: Push signed NuGet packages to NuGet.org + run: | + dotnet nuget push "*.nupkg" --api-key ${{ secrets.NUGET_TOKEN }} --skip-duplicate --source https://api.nuget.org/v3/index.json diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..f4aa8397b4 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,54 @@ +name: code-scan + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: '0 8 * * MON' + workflow_dispatch: + +permissions: + actions: read + contents: read + +jobs: + code-ql: + + runs-on: ubuntu-latest + + permissions: + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'csharp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + + - name: Setup NuGet cache + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }} + restore-keys: ${{ runner.os }}-nuget- + + - name: Initialize CodeQL + uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + category: '/language:${{ matrix.language }}' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml new file mode 100644 index 0000000000..5e48dd0854 --- /dev/null +++ b/.github/workflows/ossf-scorecard.yml @@ -0,0 +1,44 @@ +name: ossf-scorecard + +on: + push: + branches: [ master ] + schedule: + - cron: '0 8 * * MON' + workflow_dispatch: + +permissions: read-all + +jobs: + analysis: + name: analysis + runs-on: ubuntu-latest + + permissions: + id-token: write + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + with: + persist-credentials: false + + - name: Run analysis + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + publish_results: true + results_file: results.sarif + results_format: sarif + + - name: Upload artifact + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: SARIF + path: results.sarif + retention-days: 5 + + - name: Upload to code-scanning + uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + with: + sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..332a19f0de --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,29 @@ +name: stale + +on: + schedule: + - cron: '30 1 * * *' + workflow_dispatch: + +permissions: + issues: read + pull-requests: read + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 + with: + days-before-close: 14 + days-before-stale: 60 + exempt-issue-labels: 'announcement,help-wanted' + stale-issue-label: 'stale' + stale-issue-message: 'This issue is stale because it has been open for 60 days with no activity. It will be automatically closed in 14 days if no further updates are made.' + close-issue-message: 'This issue was closed because it has been inactive for 14 days since being marked as stale.' + stale-pr-label: 'stale' + stale-pr-message: 'This pull request is stale because it has been open for 60 days with no activity. It will be automatically closed in 14 days if no further changes are made.' + close-pr-message: 'This pull request was closed because it has been inactive for 14 days since being marked as stale.' diff --git a/.github/workflows/update-dotnet-sdk.yml b/.github/workflows/update-dotnet-sdk.yml new file mode 100644 index 0000000000..3197498804 --- /dev/null +++ b/.github/workflows/update-dotnet-sdk.yml @@ -0,0 +1,32 @@ +name: update-dotnet-sdk + +on: + schedule: + - cron: '0 10 * * WED' + workflow_dispatch: + +permissions: + contents: read + +jobs: + update-dotnet-sdk: + uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@9d42ad9bcbd97a74394c7273c9c616b4bf136b53 # v3.1.3 + with: + labels: 'dependencies,.NET' + update-nuget-packages: false + secrets: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + add-security-label: + needs: update-dotnet-sdk + permissions: + pull-requests: write + runs-on: ubuntu-latest + if : | + needs.update-dotnet-sdk.outputs.sdk-updated =='true' && + needs.update-dotnet-sdk.outputs.security == 'true' + steps: + - name: Add security label + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh pr edit "${{ needs.update-dotnet-sdk.outputs.pull-request-html-url }}" --add-label security diff --git a/README.md b/README.md index dcfda78d8b..0b0779b6d4 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Swashbuckle.AspNetCore ========= -[![Build status](https://ci.appveyor.com/api/projects/status/xpsk2cj1xn12c0r7/branch/master?svg=true)](https://ci.appveyor.com/project/domaindrivendev/ahoy/branch/master) +[![Build status](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/workflows/build/badge.svg?branch=master&event=push)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Code coverage](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore/branch/master/graph/badge.svg)](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/domaindrivendev/Swashbuckle.AspNetCore/badge)](https://securityscorecards.dev/viewer/?uri=github.com/domaindrivendev/Swashbuckle.AspNetCore) -[![Nuget](https://img.shields.io/nuget/v/swashbuckle.aspnetcore)](https://www.nuget.org/packages/swashbuckle.aspnetcore/) +[![NuGet](https://buildstats.info/nuget/Swashbuckle.AspNetCore)](https://www.nuget.org/packages/Swashbuckle.AspNetCore/ "Download Swashbuckle.AspNetCore from NuGet.org") [Swagger](http://swagger.io) tooling for APIs built with ASP.NET Core. Generate beautiful API documentation, including a UI to explore and test operations, directly from your routes, controllers and models. diff --git a/Swashbuckle.AspNetCore.sln b/Swashbuckle.AspNetCore.sln index 083033fadb..d310bb0f8b 100644 --- a/Swashbuckle.AspNetCore.sln +++ b/Swashbuckle.AspNetCore.sln @@ -7,11 +7,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{15A55F4A-FC3 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FA1B4021-0A97-4F68-B966-148191F6AAA8}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes .gitignore = .gitignore - appveyor.yml = appveyor.yml CONTRIBUTING.md = CONTRIBUTING.md - src\Directory.Build.props = src\Directory.Build.props + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + Directory.Packages.props = Directory.Packages.props + global.json = global.json ISSUE_TEMPLATE.md = ISSUE_TEMPLATE.md + LICENSE = LICENSE README.md = README.md EndProjectSection ProjectSection(FolderGlobals) = preProject @@ -24,12 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Swag EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore", "src\Swashbuckle.AspNetCore\Swashbuckle.AspNetCore.csproj", "{7D077710-1B4A-4DEC-96E9-CE6192D4493D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ci", "ci", "{65B4042A-F6DC-423A-A494-7CFD610E671B}" - ProjectSection(SolutionItems) = preProject - ci\build.ps1 = ci\build.ps1 - ci\test.ps1 = ci\test.ps1 - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.ReDoc", "src\Swashbuckle.AspNetCore.ReDoc\Swashbuckle.AspNetCore.ReDoc.csproj", "{B5E94C7D-B76E-4181-87E5-ACD8971A987E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Cli", "src\Swashbuckle.AspNetCore.Cli\Swashbuckle.AspNetCore.Cli.csproj", "{3164424E-D74A-45CD-BE17-BE33ED79C090}" @@ -92,6 +91,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Swashbuckle.AspNetCore.Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinimalApp", "test\WebSites\MinimalApp\MinimalApp.csproj", "{3D0126CB-5439-483C-B2D5-4B4BE111D15C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{3BA087DA-788C-43D6-9D8B-1EF017014A4A}" + ProjectSection(SolutionItems) = preProject + .github\actionlint-matcher.json = .github\actionlint-matcher.json + .github\dependabot.yml = .github\dependabot.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{A0EC16BE-C520-4FCF-BB54-2D79CD255F00}" + ProjectSection(SolutionItems) = preProject + .github\workflows\actions-lint.yml = .github\workflows\actions-lint.yml + .github\workflows\build.yml = .github\workflows\build.yml + .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml + .github\workflows\ossf-scorecard.yml = .github\workflows\ossf-scorecard.yml + .github\workflows\stale.yml = .github\workflows\stale.yml + .github\workflows\update-dotnet-sdk.yml = .github\workflows\update-dotnet-sdk.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -524,6 +539,8 @@ Global {76692D68-C38C-4A7D-B3DA-DA78A393E266} = {0ADCB223-F375-45AB-8BC4-834EC9C69554} {66590FBA-5FDD-4AC9-AF91-26ADAB33CCB8} = {0ADCB223-F375-45AB-8BC4-834EC9C69554} {3D0126CB-5439-483C-B2D5-4B4BE111D15C} = {DB3F57FC-1472-4F03-B551-43394DA3C5EB} + {3BA087DA-788C-43D6-9D8B-1EF017014A4A} = {FA1B4021-0A97-4F68-B966-148191F6AAA8} + {A0EC16BE-C520-4FCF-BB54-2D79CD255F00} = {3BA087DA-788C-43D6-9D8B-1EF017014A4A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {36FC6A67-247D-4149-8EDD-79FFD1A75F51} diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index cf38d36b98..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,39 +0,0 @@ -image: Visual Studio 2019 - -install: - - ps: Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile "./dotnet-install.ps1" - - ps: ./dotnet-install.ps1 -JsonFile global.json - -init: -- git config --global core.autocrlf true - -environment: - DOTNET_VERSION: "7.0.100" - -build_script: -- ps: dotnet pack -c Release -o artifacts - -test_script: -- ps: dotnet test - -artifacts: -- path: artifacts\*.nupkg - -deploy: -- provider: NuGet - server: https://www.myget.org/F/domaindrivendev/api/v2 - on: - branch: master - appveyor_repo_tag: false - api_key: - secure: 9QrW8KWCDr8G2ufmOX4O7U4yjneFOYY+5h+auICuWYb1YPaKpQBtUVqrRExg8VXR - skip_symbols: true - -- provider: NuGet - server: https://www.nuget.org/api/v2/package - on: - branch: master - appveyor_repo_tag: true - api_key: - secure: 4Ck/dgw64rouEDpv5fpgMsla2ZjM7H3idArzuwP7D8DUIM44BQhSCRhujesSHPAr - skip_symbols: true diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000000..78a8c2b1df --- /dev/null +++ b/build.ps1 @@ -0,0 +1,30 @@ +#! /usr/bin/env pwsh + +#Requires -PSEdition Core +#Requires -Version 7 + +param( + [Parameter(Mandatory = $false)][string] $Configuration = "Release" +) + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" + +dotnet pack --configuration $Configuration + +if ($LASTEXITCODE -ne 0) { + throw "dotnet pack failed with exit code $LASTEXITCODE" +} + +$additionalArgs = @() + +if (![string]::IsNullOrEmpty($env:GITHUB_SHA)) { + $additionalArgs += "--logger" + $additionalArgs += "GitHubActions;report-warnings=false" +} + +dotnet test --configuration $Configuration $additionalArgs + +if ($LASTEXITCODE -ne 0) { + throw "dotnet test failed with exit code $LASTEXITCODE" +} From 16f82010c539293e70aaf0be2f20b44ed6a4034a Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:43:02 +0100 Subject: [PATCH 008/680] Configure dependabot Enable dependabot for GitHub Actions and NuGet packages. --- .github/dependabot.yml | 58 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..28853aaef9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,58 @@ +version: 2 +updates: +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: weekly + day: wednesday + time: "10:00" + timezone: Europe/London +# TODO Enable npm updates +#- package-ecosystem: npm +# directory: "/src/Swashbuckle.AspNetCore.ReDoc" +# schedule: +# interval: weekly +# day: wednesday +# time: "10:00" +# timezone: Europe/London +#- package-ecosystem: npm +# directory: "/src/Swashbuckle.AspNetCore.SwaggerUI" +# schedule: +# interval: weekly +# day: wednesday +# time: "10:00" +# timezone: Europe/London +- package-ecosystem: nuget + directory: "/" + groups: + coverlet: + patterns: + - coverlet* + Microsoft.OpenApi: + patterns: + - Microsoft.OpenApi* + xunit: + patterns: + - xunit* + schedule: + interval: weekly + day: wednesday + time: "10:00" + timezone: Europe/London + ignore: + # Ignore the libraries which are "pinned" + - dependency-name: "IdentityServer4" + - dependency-name: "IdentityServer4.AccessTokenValidation" + - dependency-name: "Microsoft.AspNetCore" + - dependency-name: "Microsoft.AspNetCore.App" + - dependency-name: "Microsoft.AspNetCore.Mvc.ApiExplorer" + - dependency-name: "Microsoft.AspNetCore.Mvc.DataAnnotations" + - dependency-name: "Microsoft.AspNetCore.Mvc.Formatters.Json" + - dependency-name: "Microsoft.AspNetCore.Mvc.NewtonsoftJson" + - dependency-name: "Microsoft.AspNetCore.Mvc.Testing" + - dependency-name: "Microsoft.AspNetCore.Routing" + - dependency-name: "Microsoft.AspNetCore.StaticFiles" + - dependency-name: "Microsoft.Extensions.FileProviders.Embedded" + - dependency-name: "Microsoft.OpenApi" + - dependency-name: "Microsoft.OpenApi.Readers" + - dependency-name: "System.Text.Json" From b6343410e9e49d13a2ea832cb9b35e1e8a9f5ceb Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:52:47 +0100 Subject: [PATCH 009/680] Fix report path Fix coverage reports being collected from the wrong location. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b87d1cde5..2f02fdeead 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -62,7 +62,7 @@ jobs: uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: name: coverage-${{ runner.os }} - path: ./artifacts/coverage-reports + path: ./artifacts/coverage if-no-files-found: ignore - name: Upload coverage to Codecov From 39d88eaca8ee469fc1ca271d58aa57da4210d10d Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 13:53:14 +0100 Subject: [PATCH 010/680] Set executable bit Set the executable bit for Linux and macOS. --- build.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build.ps1 diff --git a/build.ps1 b/build.ps1 old mode 100644 new mode 100755 From 030c3af37beb7db163c572a16bee6580fad29c18 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 14:21:59 +0100 Subject: [PATCH 011/680] Fix DotNetSwaggerPath Fix `DotNetSwaggerPath` being incorrect with artifacts output in use. --- test/WebSites/CliExample/CliExample.csproj | 2 +- .../WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj | 2 +- test/WebSites/MinimalApp/MinimalApp.csproj | 2 +- test/WebSites/NswagClientExample/NswagClientExample.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index 0c9b782c1f..67f1b11d28 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger.dll")) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) diff --git a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj index a31db2520f..8b36c39458 100644 --- a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj +++ b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger.dll")) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) diff --git a/test/WebSites/MinimalApp/MinimalApp.csproj b/test/WebSites/MinimalApp/MinimalApp.csproj index 6e89e08bf8..f3b09fb04f 100644 --- a/test/WebSites/MinimalApp/MinimalApp.csproj +++ b/test/WebSites/MinimalApp/MinimalApp.csproj @@ -4,7 +4,7 @@ net6.0 enable enable - $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger")) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index 60282ca8f8..b0810e5432 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -2,7 +2,7 @@ net6.0 - $([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger.dll")) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) From baf8254d3d6f46384b6be49a2583e07a0b4d9d32 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 14:27:13 +0100 Subject: [PATCH 012/680] Fix permissions Fix incorrect workflow permissions when using `GITHUB_TOKEN`. --- .github/workflows/update-dotnet-sdk.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/update-dotnet-sdk.yml b/.github/workflows/update-dotnet-sdk.yml index 3197498804..74690b295a 100644 --- a/.github/workflows/update-dotnet-sdk.yml +++ b/.github/workflows/update-dotnet-sdk.yml @@ -7,10 +7,14 @@ on: permissions: contents: read + pull-requests: read jobs: update-dotnet-sdk: uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@9d42ad9bcbd97a74394c7273c9c616b4bf136b53 # v3.1.3 + permissions: + contents: write + pull-requests: write with: labels: 'dependencies,.NET' update-nuget-packages: false From 16c4cc92f2be2598673234949bf4059df06ce024 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 14:38:26 +0100 Subject: [PATCH 013/680] Change PR build version suffix Change to `pr..`. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 428b863177..df8e3b3656 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -53,7 +53,7 @@ preview - pr.$(GITHUB_RUN_NUMBER) + pr.$([System.String]::Copy('$(GITHUB_REF_NAME)').Replace('/merge','')).$(GITHUB_RUN_NUMBER) $(VersionPrefix).$(GITHUB_RUN_NUMBER) From 7f0b0e684b9388698bbfa95331f6d9351b7925d4 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:03:00 +0100 Subject: [PATCH 014/680] Fix paths used with exec - Ensure values are quoted. - Use absolute paths. --- test/WebSites/CliExample/CliExample.csproj | 2 +- .../WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj | 2 +- test/WebSites/NswagClientExample/NswagClientExample.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index 67f1b11d28..4620641a10 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -28,7 +28,7 @@ --> - + diff --git a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj index 8b36c39458..bec0c5d8db 100644 --- a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj +++ b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj @@ -32,7 +32,7 @@ --> - + diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index b0810e5432..355e65378f 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -22,7 +22,7 @@ - + From 11e6ce7f6c3f200aeef80a50c8b2ce0dc71b5ee3 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:09:15 +0100 Subject: [PATCH 015/680] Remove XML file Remove checked-in XML file and use the file from the build output. --- ...shbuckle.AspNetCore.SwaggerGen.Test.csproj | 1 - ...Swashbuckle.AspNetCore.SwaggerGen.Test.xml | 203 ------------------ 2 files changed, 204 deletions(-) delete mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.xml diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj index d86382c907..646ab02e11 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj @@ -1,7 +1,6 @@  - Swashbuckle.AspNetCore.SwaggerGen.Test.xml true 1701;1702;1591 net6.0 diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.xml b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.xml deleted file mode 100644 index 7b4ea815f7..0000000000 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.xml +++ /dev/null @@ -1,203 +0,0 @@ - - - - Swashbuckle.AspNetCore.SwaggerGen.Test - - - - - Summary for GenericControllerWithXmlComments - - - - - Summary for ActionWithSummaryAndRemarksTags - - - Remarks for ActionWithSummaryAndRemarksTags - - - - Description for param1 - Description for param2 - - - - Summary for FakeControllerWithXmlComments - - Description for default response - - - - Summary for ActionWithSummaryAndRemarksTags - - - Remarks for ActionWithSummaryAndRemarksTags - - - - Description for param1 - Description for param2 - - - Description for 200 response - Description for 400 response - - - - Summary for XmlAnnotatedGenericType - - - - - - - Summary for GenericProperty - - - - - Summary of AcceptsTypeParameters - - - - - - - - Summary of AcceptsConstructedOfTypeParametersType - - - - - - Summary for XmlAnnotatedSubType - - - - - Summary for XmlAnnotatedType - - - - - Summary for BoolField - - true - - - - Summary for BoolProperty - - true - - - - Summary for IntProperty - - 10 - - - - Summary for LongProperty - - 4294967295 - - - - Summary for FloatProperty - - 1.2 - - - - Summary for DoubleProperty - - 1.25 - - - - Summary for EnumProperty - - 2 - - - - Summary for GuidProperty - - d3966535-2637-48fa-b911-e3c27405ee09 - - - - Summary for Nullable StringPropertyWithNullExample - - null - - - - Summary for StringProperty - - Example for StringProperty - - - - Summary for StringPropertyWithUri - - - - - - Summary for ObjectProperty - - {"prop1": 1, "prop2": "foobar"} - - - - Summary for AcceptsNothing - - - - - Summary for AcceptsNestedType - - - - - - Summary for AcceptsConstructedGenericType - - - - - - Summary for AcceptsConstructedOfConstructedGenericType - - - - - - Summary for AcceptsArrayOfConstructedGenericType - - - - - - Summary for NestedType - - - - - Summary of DoubleNestedType.InnerType.Property - - - - - For ad-hoc serializer testing - - - - NOTE: Whitespace in these tests is significant and uses a combination of {tabs} and {spaces} - You should toggle "View White Space" to "on". - - - From 33181c2827f6efb567d33597abfedcad386c140c Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:18:20 +0100 Subject: [PATCH 016/680] Another attempt to fix paths Try to fix the path to dotnet-swagger in CI. --- test/WebSites/CliExample/CliExample.csproj | 5 +++-- .../CliExampleWithFactory/CliExampleWithFactory.csproj | 5 +++-- test/WebSites/NswagClientExample/NswagClientExample.csproj | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index 4620641a10..8a9e36ccf6 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) @@ -28,7 +28,8 @@ --> - + diff --git a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj index bec0c5d8db..11fa0005fe 100644 --- a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj +++ b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) @@ -32,7 +32,8 @@ --> - + diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index 355e65378f..d632317e41 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -2,7 +2,7 @@ net6.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)', 'dotnet-swagger.dll')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) @@ -22,7 +22,8 @@ - + From 405d4f16a0bd07807041f9765cccd56c71461c06 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:29:04 +0100 Subject: [PATCH 017/680] Add Swashbuckle.AspNetCore.Cli reference Add an explicit project reference on Swashbuckle.AspNetCore.Cli to ensure that this project depends on it. --- test/WebSites/NswagClientExample/NswagClientExample.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index d632317e41..907ec62ea6 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -11,6 +11,7 @@ + From e2016060e5dcf22dd5703b816bdce3004e6d6ad3 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:31:45 +0100 Subject: [PATCH 018/680] Lowercase Configuration Need to match the file system casing for Linux and macOS. --- test/WebSites/CliExample/CliExample.csproj | 2 +- .../WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj | 2 +- test/WebSites/NswagClientExample/NswagClientExample.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index 8a9e36ccf6..dea08d22df 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration.ToLower())_$(TargetFramework)')) diff --git a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj index 11fa0005fe..d47ca632c2 100644 --- a/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj +++ b/test/WebSites/CliExampleWithFactory/CliExampleWithFactory.csproj @@ -2,7 +2,7 @@ net6.0;net7.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration.ToLower())_$(TargetFramework)')) diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index 907ec62ea6..dd608ed499 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -2,7 +2,7 @@ net6.0 - $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration)_$(TargetFramework)')) + $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration.ToLower())_$(TargetFramework)')) From 4e72a0b6e04be8909eee70a85f53be218039a7c8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:40:23 +0100 Subject: [PATCH 019/680] Fix condition Include projects ending in `Tests` too. --- Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 719ddb4f24..d69a81c44d 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,5 +1,5 @@ - + true From 03c79b1fea361510a46838f6d1ccc8b882603f34 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 15:54:57 +0100 Subject: [PATCH 020/680] Tighten coverage scope Just include Swashbuckle assemblies in coverage, so exclude all the test websites etc. --- Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/Directory.Build.props b/Directory.Build.props index df8e3b3656..00ccd169e8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -71,6 +71,7 @@ $(CoverletOutputFormat),opencover [System.*]*,[*.Test*]*,[xunit.*]* GeneratedCodeAttribute + [Swashbuckle.*]* From d324b8e57c03c346b0747175771b377bafd465e1 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:01:35 +0100 Subject: [PATCH 021/680] Remove SDK setup steps - Attempt to fix the workflow by removing steps to setup the .NET SDK and use the NuGet cache. - Fix name to match file. --- .github/workflows/codeql-analysis.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f4aa8397b4..00119b86fa 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,4 +1,4 @@ -name: code-scan +name: codeql-analysis on: push: @@ -30,16 +30,6 @@ jobs: - name: Checkout repository uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - - name: Setup .NET SDK - uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 - - - name: Setup NuGet cache - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }} - restore-keys: ${{ runner.os }}-nuget- - - name: Initialize CodeQL uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: From 54daca9dfbcb9dba3b17de6c7235f0d62a598f80 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:09:10 +0100 Subject: [PATCH 022/680] Re-add setup-dotnet Add the step to install the .NET SDK back. --- .github/workflows/codeql-analysis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 00119b86fa..d698eefad4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,6 +30,9 @@ jobs: - name: Checkout repository uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 + - name: Setup .NET SDK + uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 + - name: Initialize CodeQL uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: From c3277873883831bafcd4fb7333cc220cbc1f2de8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:25:47 +0100 Subject: [PATCH 023/680] Change syntax Match the syntax with another repo where the workflow is definitely doing the right thing. --- .github/workflows/codeql-analysis.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d698eefad4..2c1e4320b9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -21,11 +21,6 @@ jobs: permissions: security-events: write - strategy: - fail-fast: false - matrix: - language: [ 'csharp' ] - steps: - name: Checkout repository uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 @@ -36,7 +31,7 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: - languages: ${{ matrix.language }} + languages: csharp - name: Autobuild uses: github/codeql-action/autobuild@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 @@ -44,4 +39,4 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: - category: '/language:${{ matrix.language }}' + category: "/language:csharp" From 31f9fec117b573c37670344952000dc9110cd168 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:27:00 +0100 Subject: [PATCH 024/680] Try Windows Try running on Windows. --- .github/workflows/codeql-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2c1e4320b9..ba081f65b3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,7 @@ permissions: jobs: code-ql: - runs-on: ubuntu-latest + runs-on: windows-latest permissions: security-events: write From 09fedd05ba88a4fbf0d197cc99325582492c1635 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:32:11 +0100 Subject: [PATCH 025/680] Try manual build Switch back to Ubuntu and manually build the solution. --- .github/workflows/codeql-analysis.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ba081f65b3..69f9c33609 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -16,7 +16,7 @@ permissions: jobs: code-ql: - runs-on: windows-latest + runs-on: ubuntu-latest permissions: security-events: write @@ -32,11 +32,14 @@ jobs: uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: languages: csharp + build-mode: manual - - name: Autobuild - uses: github/codeql-action/autobuild@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + - name: Build, Package and Test + shell: pwsh + run: | + dotnet build --configuration Release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 with: - category: "/language:csharp" + category: '/language:csharp' From 703d2b83063b6f7e33605238e5e88a54ba12bd93 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:42:21 +0100 Subject: [PATCH 026/680] Rename step Rename step to reflect what it's doing. --- .github/workflows/codeql-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 69f9c33609..1d047b5f99 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,10 +34,10 @@ jobs: languages: csharp build-mode: manual - - name: Build, Package and Test + - name: Build shell: pwsh run: | - dotnet build --configuration Release + dotnet build --configuration Release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 From 53453774293928fee8b138c8f3228a8ac06d5920 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Wed, 10 Apr 2024 11:02:47 +0100 Subject: [PATCH 027/680] Simplify condition `String.Copy()` isn't needed. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 00ccd169e8..c213a5e7c4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -53,7 +53,7 @@ preview - pr.$([System.String]::Copy('$(GITHUB_REF_NAME)').Replace('/merge','')).$(GITHUB_RUN_NUMBER) + pr.$(GITHUB_REF_NAME.Replace('/merge', '')).$(GITHUB_RUN_NUMBER) $(VersionPrefix).$(GITHUB_RUN_NUMBER) From 5e2ea49240e77f7b22d8969e40987333916e84ae Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 3 Apr 2024 16:47:07 +0100 Subject: [PATCH 028/680] Bump version Bump patch version so the version is greater than the current stable release. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index c213a5e7c4..3ea75150b0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -48,7 +48,7 @@ snupkg true true - 6.5.0 + 6.5.1 false From c8a5a0eb3280a8ff66697aa2f4273840791c4083 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:48:59 +0000 Subject: [PATCH 029/680] Bump martincostello/update-dotnet-sdk from 3.1.3 to 3.1.4 Bumps [martincostello/update-dotnet-sdk](https://github.com/martincostello/update-dotnet-sdk) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/martincostello/update-dotnet-sdk/releases) - [Commits](https://github.com/martincostello/update-dotnet-sdk/compare/9d42ad9bcbd97a74394c7273c9c616b4bf136b53...b5684309b4d6dd59ee955b150f5632c5f3a59f79) --- updated-dependencies: - dependency-name: martincostello/update-dotnet-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/update-dotnet-sdk.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-dotnet-sdk.yml b/.github/workflows/update-dotnet-sdk.yml index 74690b295a..9d76ce5a81 100644 --- a/.github/workflows/update-dotnet-sdk.yml +++ b/.github/workflows/update-dotnet-sdk.yml @@ -11,7 +11,7 @@ permissions: jobs: update-dotnet-sdk: - uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@9d42ad9bcbd97a74394c7273c9c616b4bf136b53 # v3.1.3 + uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@b5684309b4d6dd59ee955b150f5632c5f3a59f79 # v3.1.4 permissions: contents: write pull-requests: write From e287244d70acaba8eff2b5dc367a3237aa14e63f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:49:04 +0000 Subject: [PATCH 030/680] Bump codecov/codecov-action from 4.1.1 to 4.3.0 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4.1.1 to 4.3.0. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/c16abc29c95fcf9174b58eb7e1abf4c866893bc8...84508663e988701840491b86de86b666e8a86bed) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f02fdeead..2a137e6a20 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,7 +66,7 @@ jobs: if-no-files-found: ignore - name: Upload coverage to Codecov - uses: codecov/codecov-action@c16abc29c95fcf9174b58eb7e1abf4c866893bc8 # v4.1.1 + uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0 with: files: ./artifacts/coverage/coverage.cobertura.xml flags: ${{ runner.os }} From c9767c7b52e582ce87bd4ea859d81dd00cad9e33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 07:49:12 +0000 Subject: [PATCH 031/680] Bump github/codeql-action from 3.24.9 to 3.24.10 Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.9 to 3.24.10. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/1b1aada464948af03b950897e5eb522f92603cc2...4355270be187e1b672a7a1c7c7bae5afdc1ab94a) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1d047b5f99..3be76fb4d9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -29,7 +29,7 @@ jobs: uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0 - name: Initialize CodeQL - uses: github/codeql-action/init@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + uses: github/codeql-action/init@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 with: languages: csharp build-mode: manual @@ -40,6 +40,6 @@ jobs: dotnet build --configuration Release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + uses: github/codeql-action/analyze@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 5e48dd0854..5e0f246c6d 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -39,6 +39,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@1b1aada464948af03b950897e5eb522f92603cc2 # v3.24.9 + uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 with: sarif_file: results.sarif From 6e68be40e74816aa353a01a016d3bf17793d4f35 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Sat, 13 Apr 2024 09:06:40 +0100 Subject: [PATCH 032/680] Fix build badge Fix the build badge not rendering a status correctly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b0779b6d4..a7f8b1963e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Swashbuckle.AspNetCore ========= -[![Build status](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/workflows/build/badge.svg?branch=master&event=push)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Code coverage](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore/branch/master/graph/badge.svg)](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/domaindrivendev/Swashbuckle.AspNetCore/badge)](https://securityscorecards.dev/viewer/?uri=github.com/domaindrivendev/Swashbuckle.AspNetCore) +[![Build status](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions/workflows/build.yml/badge.svg?branch=master&event=push)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Code coverage](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore/branch/master/graph/badge.svg)](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/domaindrivendev/Swashbuckle.AspNetCore/badge)](https://securityscorecards.dev/viewer/?uri=github.com/domaindrivendev/Swashbuckle.AspNetCore) [![NuGet](https://buildstats.info/nuget/Swashbuckle.AspNetCore)](https://www.nuget.org/packages/Swashbuckle.AspNetCore/ "Download Swashbuckle.AspNetCore from NuGet.org") From b7cd5eb44dcb1e51dc59663341e72b6db11a6a92 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Sat, 13 Apr 2024 09:08:23 +0100 Subject: [PATCH 033/680] Fix preview package versions Add missing suffix to the prerelease label for previews. --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 3ea75150b0..cb03fcf26a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,7 +52,7 @@ false - preview + preview.$(GITHUB_RUN_NUMBER) pr.$(GITHUB_REF_NAME.Replace('/merge', '')).$(GITHUB_RUN_NUMBER) $(VersionPrefix).$(GITHUB_RUN_NUMBER) From d69e86f27d743f5e6623cf35f9bfc5f385017091 Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 22 Oct 2022 08:35:16 +0100 Subject: [PATCH 034/680] Remove unused parameter Fix warning by removing unused parameter from private method. --- .../SchemaGenerator/SchemaGenerator.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index de2ac497fc..dd855c42db 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -50,7 +50,7 @@ private OpenApiSchema GenerateSchemaForMember( var dataContract = GetDataContractFor(modelType); var schema = _generatorOptions.UseOneOfForPolymorphism && IsBaseTypeWithKnownTypesDefined(dataContract, out var knownTypesDataContracts) - ? GeneratePolymorphicSchema(dataContract, schemaRepository, knownTypesDataContracts) + ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema.Reference != null) @@ -112,7 +112,7 @@ private OpenApiSchema GenerateSchemaForParameter( var dataContract = GetDataContractFor(modelType); var schema = _generatorOptions.UseOneOfForPolymorphism && IsBaseTypeWithKnownTypesDefined(dataContract, out var knownTypesDataContracts) - ? GeneratePolymorphicSchema(dataContract, schemaRepository, knownTypesDataContracts) + ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema.Reference != null) @@ -152,7 +152,7 @@ private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository sch var dataContract = GetDataContractFor(modelType); var schema = _generatorOptions.UseOneOfForPolymorphism && IsBaseTypeWithKnownTypesDefined(dataContract, out var knownTypesDataContracts) - ? GeneratePolymorphicSchema(dataContract, schemaRepository, knownTypesDataContracts) + ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); if (schema.Reference == null) @@ -188,7 +188,6 @@ private bool IsBaseTypeWithKnownTypesDefined(DataContract dataContract, out IEnu } private OpenApiSchema GeneratePolymorphicSchema( - DataContract dataContract, SchemaRepository schemaRepository, IEnumerable knownTypesDataContracts) { From ac1afcf5db74e33eb55f8238cc316e48da51f15b Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 22 Oct 2022 08:36:38 +0100 Subject: [PATCH 035/680] Allocate array once Only allocate the array once, rather than on every call. --- .../SchemaGenerator/SchemaGenerator.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index dd855c42db..0c937612c6 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -199,6 +199,12 @@ private OpenApiSchema GeneratePolymorphicSchema( }; } + private static readonly Type[] BinaryStringTypes = new[] + { + typeof(IFormFile), + typeof(FileResult), + }; + private OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) { if (TryGetCustomTypeMapping(dataContract.UnderlyingType, out Func customSchemaFactory)) @@ -206,7 +212,7 @@ private OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRe return customSchemaFactory(); } - if (dataContract.UnderlyingType.IsAssignableToOneOf(typeof(IFormFile), typeof(FileResult))) + if (dataContract.UnderlyingType.IsAssignableToOneOf(BinaryStringTypes)) { return new OpenApiSchema { Type = "string", Format = "binary" }; } From 044a995ad1303e33b92d022ff6324d82ee033778 Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 22 Oct 2022 08:42:55 +0100 Subject: [PATCH 036/680] Handle parameters of type Stream and PipeReader Do not generate schema models for `Stream` and `PipeReader`. See https://github.com/dotnet/aspnetcore/issues/44677#issuecomment-1287482505. --- .../SchemaGenerator/SchemaGenerator.cs | 4 ++++ .../SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs | 3 ++- .../SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index 0c937612c6..d9349be7ab 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -203,6 +203,10 @@ private OpenApiSchema GeneratePolymorphicSchema( { typeof(IFormFile), typeof(FileResult), + typeof(System.IO.Stream), +#if NETCOREAPP3_0_OR_GREATER + typeof(System.IO.Pipelines.PipeReader), +#endif }; private OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs index 58b91c440b..040380ed5f 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs @@ -23,7 +23,8 @@ public class NewtonsoftSchemaGeneratorTests [Theory] [InlineData(typeof(IFormFile))] [InlineData(typeof(FileResult))] - public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type type) + [InlineData(typeof(System.IO.Stream))] + public void GenerateSchema_GeneratesFileSchema_BinaryStringResultType(Type type) { var schema = Subject().GenerateSchema(type, new SchemaRepository()); diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index 30da476cec..efa3f7ad33 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -23,7 +23,9 @@ public class JsonSerializerSchemaGeneratorTests [Theory] [InlineData(typeof(IFormFile))] [InlineData(typeof(FileResult))] - public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type type) + [InlineData(typeof(System.IO.Stream))] + [InlineData(typeof(System.IO.Pipelines.PipeReader))] + public void GenerateSchema_GeneratesFileSchema_BinaryStringResultType(Type type) { var schema = Subject().GenerateSchema(type, new SchemaRepository()); From d4161a65b257e97b640a53668eb8d93156e7411b Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Sat, 13 Apr 2024 09:59:22 +0100 Subject: [PATCH 037/680] Add security policy Add security policy. --- SECURITY.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..43b675a0b8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +## Reporting a Vulnerability + +To privately report a security vulnerability, please create a security advisory in the [repository's Security tab](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/security/advisories). + +Further details can be found in the [GitHub documentation](https://docs.github.com/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability). From c207be7c6ba61c20201674697b9be3e08aee26ae Mon Sep 17 00:00:00 2001 From: David Warner Date: Sat, 13 Apr 2024 19:27:18 +1000 Subject: [PATCH 038/680] Update Redoc spelling (#2568) --- README.md | 22 +++++++++---------- .../ReDocBuilderExtensions.cs | 4 ++-- .../Swashbuckle.AspNetCore.ReDoc.csproj | 2 +- src/Swashbuckle.AspNetCore.ReDoc/index.html | 2 +- .../ReDocIntegrationTests.cs | 4 ++-- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0b0779b6d4..8edbc6158d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Once you have an API that can describe itself in Swagger, you've opened the trea # Compatibility # -|Swashbuckle Version|ASP.NET Core|Swagger / OpenAPI Spec.|swagger-ui|ReDoc UI| +|Swashbuckle Version|ASP.NET Core|Swagger / OpenAPI Spec.|swagger-ui|Redoc UI| |----------|----------|----------|----------|----------| |[master](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/master/README.md)|>= 2.0.0|2.0, 3.0|4.15.5|2.0.0| |[6.5.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.5.0)|>= 2.0.0|2.0, 3.0|4.15.5|2.0.0| @@ -159,7 +159,7 @@ Refer to the [routing documentation](https://docs.microsoft.com/en-us/aspnet/cor Swashbuckle consists of multiple components that can be used together or individually depending on your needs. At its core, there's a Swagger generator, middleware to expose it as JSON endpoints, and a packaged version of the [swagger-ui](https://github.com/swagger-api/swagger-ui). These 3 packages can be installed with the `Swashbuckle.AspNetCore` "metapackage" and will work together seamlessly (see [Getting Started](#getting-started)) to provide beautiful API docs that are automatically generated from your code. -Additionally, there's add-on packages (CLI tools, [an alternate UI](https://github.com/Rebilly/ReDoc) etc.) that you can optionally install and configure as needed. +Additionally, there's add-on packages (CLI tools, [an alternate UI](https://github.com/Rebilly/redoc) etc.) that you can optionally install and configure as needed. ## "Core" Packages (i.e. installed via Swashbuckle.AspNetCore) @@ -175,7 +175,7 @@ Additionally, there's add-on packages (CLI tools, [an alternate UI](https://gith |---------|-----------| |Swashbuckle.AspNetCore.Annotations|Includes a set of custom attributes that can be applied to controllers, actions and models to enrich the generated Swagger| |Swashbuckle.AspNetCore.Cli|Provides a command line interface for retrieving Swagger directly from a startup assembly, and writing to file| -|Swashbuckle.AspNetCore.ReDoc|Exposes an embedded version of the ReDoc UI (an alternative to swagger-ui)| +|Swashbuckle.AspNetCore.ReDoc|Exposes an embedded version of the Redoc UI (an alternative to swagger-ui)| ## Community Packages ## @@ -247,7 +247,7 @@ The steps described above will get you up and running with minimal setup. Howeve * [Swashbuckle.AspNetCore.ReDoc](#swashbuckleaspnetcoreredoc) * [Change Relative Path to the UI](#redoc-change-relative-path-to-the-ui) * [Change Document Title](#redoc-change-document-title) - * [Apply ReDoc Parameters](#apply-redoc-parameters) + * [Apply Redoc Parameters](#apply-redoc-parameters) * [Inject Custom CSS](#redoc-inject-custom-css) * [Customize index.html](#redoc-customize-indexhtml) @@ -304,7 +304,7 @@ app.UseSwagger(c => ### Working with Virtual Directories and Reverse Proxies ### -Virtual directories and reverse proxies can cause issues for applications that generate links and redirects, particularly if the app returns *absolute* URLs based on the `Host` header and other information from the current request. To avoid these issues, Swashbuckle uses *relative* URLs where possible, and encourages their use when configuring the SwaggerUI and ReDoc middleware. +Virtual directories and reverse proxies can cause issues for applications that generate links and redirects, particularly if the app returns *absolute* URLs based on the `Host` header and other information from the current request. To avoid these issues, Swashbuckle uses *relative* URLs where possible, and encourages their use when configuring the SwaggerUI and Redoc middleware. For example, to wire up the SwaggerUI middleware, you provide the URL to one or more OpenAPI/Swagger documents. This is the URL that the swagger-ui, a client-side application, will call to retrieve your API metadata. To ensure this works behind virtual directories and reverse proxies, you should express this relative to the `RoutePrefix` of the swagger-ui itself: @@ -1625,7 +1625,7 @@ public class SwaggerHostFactory

Change Relative Path to the UI

-By default, the ReDoc UI will be exposed at "/api-docs". If necessary, you can alter this when enabling the ReDoc middleware: +By default, the Redoc UI will be exposed at "/api-docs". If necessary, you can alter this when enabling the Redoc middleware: ```csharp app.UseReDoc(c => @@ -1637,7 +1637,7 @@ app.UseReDoc(c =>

Change Document Title

-By default, the ReDoc UI will have a generic document title. You can alter this when enabling the ReDoc middleware: +By default, the Redoc UI will have a generic document title. You can alter this when enabling the Redoc middleware: ```csharp app.UseReDoc(c => @@ -1647,9 +1647,9 @@ app.UseReDoc(c => } ``` -### Apply ReDoc Parameters ### +### Apply Redoc Parameters ### -ReDoc ships with its own set of configuration parameters, all described here https://github.com/Rebilly/ReDoc/blob/master/README.md#redoc-options-object. In Swashbuckle, most of these are surfaced through the ReDoc middleware options: +Redoc ships with its own set of configuration parameters, all described here https://github.com/Rebilly/redoc/blob/main/README.md#redoc-options-object. In Swashbuckle, most of these are surfaced through the Redoc middleware options: ```csharp app.UseReDoc(c => @@ -1685,7 +1685,7 @@ app.UseReDoc(c => } ``` -It is also possible to modify the theme by using the `AdditionalItems` property, see https://github.com/Rebilly/ReDoc/blob/master/README.md#redoc-options-object for more information. +It is also possible to modify the theme by using the `AdditionalItems` property, see https://github.com/Rebilly/redoc/blob/main/README.md#redoc-options-object for more information. ```csharp app.UseReDoc(c => @@ -1697,7 +1697,7 @@ app.UseReDoc(c =>

Customize index.html

-To customize the UI beyond the basic options listed above, you can provide your own version of the ReDoc index.html page: +To customize the UI beyond the basic options listed above, you can provide your own version of the Redoc index.html page: ```csharp app.UseReDoc(c => diff --git a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs index 9bd4ab17b2..ff9f1e3ac4 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs +++ b/src/Swashbuckle.AspNetCore.ReDoc/ReDocBuilderExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Builder public static class ReDocBuilderExtensions { /// - /// Register the ReDoc middleware with provided options + /// Register the Redoc middleware with provided options /// public static IApplicationBuilder UseReDoc(this IApplicationBuilder app, ReDocOptions options) { @@ -16,7 +16,7 @@ public static IApplicationBuilder UseReDoc(this IApplicationBuilder app, ReDocOp } /// - /// Register the ReDoc middleware with optional setup action for DI-injected options + /// Register the Redoc middleware with optional setup action for DI-injected options /// public static IApplicationBuilder UseReDoc( this IApplicationBuilder app, diff --git a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj index 7f9de9d46b..b62cd452e1 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj +++ b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj @@ -2,7 +2,7 @@ Swashbuckle.AspNetCore.ReDoc.snk - Middleware to expose an embedded version of ReDoc from an ASP.NET Core application + Middleware to expose an embedded version of Redoc from an ASP.NET Core application true $(NoWarn);1591 swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;redoc diff --git a/src/Swashbuckle.AspNetCore.ReDoc/index.html b/src/Swashbuckle.AspNetCore.ReDoc/index.html index 0b58024a3d..28bcc5cdb0 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/index.html +++ b/src/Swashbuckle.AspNetCore.ReDoc/index.html @@ -8,7 +8,7 @@ + %(HeadContent)
- + - \ No newline at end of file + diff --git a/src/Swashbuckle.AspNetCore.ReDoc/index.js b/src/Swashbuckle.AspNetCore.ReDoc/index.js new file mode 100644 index 0000000000..48ead65adb --- /dev/null +++ b/src/Swashbuckle.AspNetCore.ReDoc/index.js @@ -0,0 +1 @@ +Redoc.init('%(SpecUrl)', JSON.parse('%(ConfigObject)'), document.getElementById('redoc-container')); \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/ResourceHelper.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/ResourceHelper.cs new file mode 100644 index 0000000000..7401ef2dd1 --- /dev/null +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/ResourceHelper.cs @@ -0,0 +1,13 @@ +using System.IO; +using System.Reflection; + +namespace Swashbuckle.AspNetCore.SwaggerUI; + +internal static class ResourceHelper +{ + public static Stream GetEmbeddedResource(string fileName) + { + return typeof(ResourceHelper).GetTypeInfo().Assembly + .GetManifestResourceStream($"Swashbuckle.AspNetCore.SwaggerUI.{fileName}"); + } +} diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs index 190e8c2ee4..d89129d8c1 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIMiddleware.cs @@ -64,26 +64,30 @@ public SwaggerUIMiddleware( public async Task Invoke(HttpContext httpContext) { var httpMethod = httpContext.Request.Method; - var path = httpContext.Request.Path.Value; - var isGet = HttpMethods.IsGet(httpMethod); - - // If the RoutePrefix is requested (with or without trailing slash), redirect to index URL - if (isGet && Regex.IsMatch(path, $"^/?{Regex.Escape(_options.RoutePrefix)}/?$", RegexOptions.IgnoreCase)) + if (HttpMethods.IsGet(httpMethod)) { - // Use relative redirect to support proxy environments - var relativeIndexUrl = string.IsNullOrEmpty(path) || path.EndsWith("/") - ? "index.html" - : $"{path.Split('/').Last()}/index.html"; + var path = httpContext.Request.Path.Value; - RespondWithRedirect(httpContext.Response, relativeIndexUrl); - return; - } + // If the RoutePrefix is requested (with or without trailing slash), redirect to index URL + if (Regex.IsMatch(path, $"^/?{Regex.Escape(_options.RoutePrefix)}/?$", RegexOptions.IgnoreCase)) + { + // Use relative redirect to support proxy environments + var relativeIndexUrl = string.IsNullOrEmpty(path) || path.EndsWith("/") + ? "index.html" + : $"{path.Split('/').Last()}/index.html"; - if (isGet && Regex.IsMatch(path, $"^/{Regex.Escape(_options.RoutePrefix)}/?index.html$", RegexOptions.IgnoreCase)) - { - await RespondWithIndexHtml(httpContext.Response); - return; + RespondWithRedirect(httpContext.Response, relativeIndexUrl); + return; + } + + var match = Regex.Match(path, $"^/{Regex.Escape(_options.RoutePrefix)}/?(index.(html|js))$", RegexOptions.IgnoreCase); + + if (match.Success) + { + await RespondWithFile(httpContext.Response, match.Groups[1].Value); + return; + } } await _staticFileMiddleware.Invoke(httpContext); @@ -110,23 +114,35 @@ private static void RespondWithRedirect(HttpResponse response, string location) response.Headers["Location"] = location; } - private async Task RespondWithIndexHtml(HttpResponse response) + private async Task RespondWithFile(HttpResponse response, string fileName) { response.StatusCode = 200; - response.ContentType = "text/html;charset=utf-8"; - using (var stream = _options.IndexStream()) + Stream stream; + + if (fileName == "index.js") + { + response.ContentType = "application/javascript;charset=utf-8"; + stream = ResourceHelper.GetEmbeddedResource(fileName); + } + else + { + response.ContentType = "text/html;charset=utf-8"; + stream = _options.IndexStream(); + } + + using (stream) { using var reader = new StreamReader(stream); // Inject arguments before writing to response - var htmlBuilder = new StringBuilder(await reader.ReadToEndAsync()); + var content = new StringBuilder(await reader.ReadToEndAsync()); foreach (var entry in GetIndexArguments()) { - htmlBuilder.Replace(entry.Key, entry.Value); + content.Replace(entry.Key, entry.Value); } - await response.WriteAsync(htmlBuilder.ToString(), Encoding.UTF8); + await response.WriteAsync(content.ToString(), Encoding.UTF8); } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIOptions.cs index 3d9a9e9437..8ca532b253 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/SwaggerUIOptions.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; @@ -18,8 +17,7 @@ public class SwaggerUIOptions /// /// Gets or sets a Stream function for retrieving the swagger-ui page /// - public Func IndexStream { get; set; } = () => typeof(SwaggerUIOptions).GetTypeInfo().Assembly - .GetManifestResourceStream("Swashbuckle.AspNetCore.SwaggerUI.index.html"); + public Func IndexStream { get; set; } = () => ResourceHelper.GetEmbeddedResource("index.html"); ///
/// Gets or sets a title for the swagger-ui page @@ -34,7 +32,7 @@ public class SwaggerUIOptions /// /// Gets the JavaScript config object, represented as JSON, that will be passed to the SwaggerUI /// - public ConfigObject ConfigObject { get; set; } = new ConfigObject(); + public ConfigObject ConfigObject { get; set; } = new ConfigObject(); /// /// Gets the JavaScript config object, represented as JSON, that will be passed to the initOAuth method diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj index 10d562b646..f1691cf517 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj @@ -16,7 +16,8 @@ - + + diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/index.html b/src/Swashbuckle.AspNetCore.SwaggerUI/index.html index cfe58ed83e..cea46ec4e0 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/index.html +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/index.html @@ -5,113 +5,17 @@ %(DocumentTitle) + - - %(HeadContent) + %(HeadContent)
- - - - - - + + + diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/index.js b/src/Swashbuckle.AspNetCore.SwaggerUI/index.js new file mode 100644 index 0000000000..7c4e7c59ee --- /dev/null +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/index.js @@ -0,0 +1,69 @@ +/* Source: https://gist.github.com/lamberta/3768814 + * Parse a string function definition and return a function object. Does not use eval. + * @param {string} str + * @return {function} + * + * Example: + * var f = function (x, y) { return x * y; }; + * var g = parseFunction(f.toString()); + * g(33, 3); //=> 99 + */ +function parseFunction(str) { + if (!str) return void (0); + + var fn_body_idx = str.indexOf('{'), + fn_body = str.substring(fn_body_idx + 1, str.lastIndexOf('}')), + fn_declare = str.substring(0, fn_body_idx), + fn_params = fn_declare.substring(fn_declare.indexOf('(') + 1, fn_declare.lastIndexOf(')')), + args = fn_params.split(','); + + args.push(fn_body); + + function Fn() { + return Function.apply(this, args); + } + Fn.prototype = Function.prototype; + + return new Fn(); +} + +window.onload = function () { + var configObject = JSON.parse('%(ConfigObject)'); + var oauthConfigObject = JSON.parse('%(OAuthConfigObject)'); + + // Workaround for https://github.com/swagger-api/swagger-ui/issues/5945 + configObject.urls.forEach(function (item) { + if (item.url.startsWith("http") || item.url.startsWith("/")) return; + item.url = window.location.href.replace("index.html", item.url).split('#')[0]; + }); + + // If validatorUrl is not explicitly provided, disable the feature by setting to null + if (!configObject.hasOwnProperty("validatorUrl")) + configObject.validatorUrl = null + + // If oauth2RedirectUrl isn't specified, use the built-in default + if (!configObject.hasOwnProperty("oauth2RedirectUrl")) + configObject.oauth2RedirectUrl = (new URL("oauth2-redirect.html", window.location.href)).href; + + // Apply mandatory parameters + configObject.dom_id = "#swagger-ui"; + configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset]; + configObject.layout = "StandaloneLayout"; + + // Parse and add interceptor functions + var interceptors = JSON.parse('%(Interceptors)'); + if (interceptors.RequestInterceptorFunction) + configObject.requestInterceptor = parseFunction(interceptors.RequestInterceptorFunction); + if (interceptors.ResponseInterceptorFunction) + configObject.responseInterceptor = parseFunction(interceptors.ResponseInterceptorFunction); + + // Begin Swagger UI call region + + const ui = SwaggerUIBundle(configObject); + + ui.initOAuth(oauthConfigObject); + + // End Swagger UI call region + + window.ui = ui +} diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs index 048d23221c..69c635a7d5 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs @@ -24,38 +24,60 @@ public async Task IndexUrl_ReturnsEmbeddedVersionOfTheRedocUI() { var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - var indexResponse = await client.GetAsync("/api-docs/index.html"); + var htmlResponse = await client.GetAsync("/api-docs/index.html"); + var cssResponse = await client.GetAsync("/api-docs/index.css"); var jsResponse = await client.GetAsync("/api-docs/redoc.standalone.js"); - var indexContent = await indexResponse.Content.ReadAsStringAsync(); - Assert.Contains("Redoc.init", indexContent); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); } + [Fact] + public async Task RedocMiddleware_ReturnsInitializerScript() + { + var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); + + var response = await client.GetAsync("/api-docs/index.js"); + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains("Redoc.init", content); + Assert.DoesNotContain("%(DocumentTitle)", content); + Assert.DoesNotContain("%(HeadContent)", content); + Assert.DoesNotContain("%(SpecUrl)", content); + Assert.DoesNotContain("%(ConfigObject)", content); + } + [Fact] public async Task IndexUrl_IgnoresUrlCase() { var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - var indexResponse = await client.GetAsync("/Api-Docs/index.html"); - var jsResponse = await client.GetAsync("/Api-Docs/redoc.standalone.js"); + var htmlResponse = await client.GetAsync("/Api-Docs/index.html"); + var cssResponse = await client.GetAsync("/Api-Docs/index.css"); + var jsInitResponse = await client.GetAsync("/Api-Docs/index.js"); + var jsRedocResponse = await client.GetAsync("/Api-Docs/redoc.standalone.js"); - var indexContent = await indexResponse.Content.ReadAsStringAsync(); - Assert.Contains("Redoc.init", indexContent); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsInitResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsRedocResponse.StatusCode); } [Theory] - [InlineData("/redoc/1.0/index.html", "/swagger/1.0/swagger.json")] - [InlineData("/redoc/2.0/index.html", "/swagger/2.0/swagger.json")] - public async Task RedocMiddleware_CanBeConfiguredMultipleTimes(string redocUrl, string swaggerPath) + [InlineData("/redoc/1.0/index.html", "/redoc/1.0/index.js", "/swagger/1.0/swagger.json")] + [InlineData("/redoc/2.0/index.html", "/redoc/2.0/index.js", "/swagger/2.0/swagger.json")] + public async Task RedocMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string swaggerPath) { var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); - var response = await client.GetAsync(redocUrl); - var content = await response.Content.ReadAsStringAsync(); + var htmlResponse = await client.GetAsync(htmlUrl); + var jsResponse = await client.GetAsync(jsUrl); + var content = await jsResponse.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); Assert.Contains(swaggerPath, content); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs index 9ac7bf5dee..fa52f555da 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs @@ -26,26 +26,52 @@ public async Task RoutePrefix_RedirectsToPathRelativeIndexUrl( } [Theory] - [InlineData(typeof(Basic.Startup), "/index.html", "/swagger-ui.js", "/swagger-ui.css")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/swagger/swagger-ui.js", "/swagger/swagger-ui.css")] + [InlineData(typeof(Basic.Startup), "/index.html", "/swagger-ui.js", "/index.css", "/swagger-ui.css")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/swagger/swagger-ui.js", "swagger/index.css", "/swagger/swagger-ui.css")] public async Task IndexUrl_ReturnsEmbeddedVersionOfTheSwaggerUI( Type startupType, - string indexPath, - string jsPath, - string cssPath) + string htmlPath, + string swaggerUijsPath, + string indexCssPath, + string swaggerUiCssPath) { var client = new TestSite(startupType).BuildClient(); - var indexResponse = await client.GetAsync(indexPath); - Assert.Equal(HttpStatusCode.OK, indexResponse.StatusCode); - var indexContent = await indexResponse.Content.ReadAsStringAsync(); - Assert.Contains("SwaggerUIBundle", indexContent); + var htmlResponse = await client.GetAsync(htmlPath); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - var jsResponse = await client.GetAsync(jsPath); + var jsResponse = await client.GetAsync(swaggerUijsPath); Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - var cssResponse = await client.GetAsync(cssPath); + var cssResponse = await client.GetAsync(indexCssPath); Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + + cssResponse = await client.GetAsync(swaggerUiCssPath); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + } + + [Theory] + [InlineData(typeof(Basic.Startup), "/index.js")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.js")] + public async Task SwaggerUIMiddleware_ReturnsInitializerScript( + Type startupType, + string indexJsPath) + { + var client = new TestSite(startupType).BuildClient(); + + var jsResponse = await client.GetAsync(indexJsPath); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + + var jsContent = await jsResponse.Content.ReadAsStringAsync(); + Assert.Contains("SwaggerUIBundle", jsContent); + Assert.DoesNotContain("%(DocumentTitle)", jsContent); + Assert.DoesNotContain("%(HeadContent)", jsContent); + Assert.DoesNotContain("%(StylesPath)", jsContent); + Assert.DoesNotContain("%(ScriptBundlePath)", jsContent); + Assert.DoesNotContain("%(ScriptPresetsPath)", jsContent); + Assert.DoesNotContain("%(ConfigObject)", jsContent); + Assert.DoesNotContain("%(OAuthConfigObject)", jsContent); + Assert.DoesNotContain("%(Interceptors)", jsContent); } [Fact] @@ -76,7 +102,7 @@ public async Task IndexUrl_ReturnsInterceptors_IfConfigured() { var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); - var response = await client.GetAsync("/swagger/index.html"); + var response = await client.GetAsync("/swagger/index.js"); var content = await response.Content.ReadAsStringAsync(); Assert.Contains("\"RequestInterceptorFunction\":", content); @@ -84,17 +110,19 @@ public async Task IndexUrl_ReturnsInterceptors_IfConfigured() } [Theory] - [InlineData("/swagger/index.html", new [] { "Version 1.0", "Version 2.0" })] - [InlineData("/swagger/1.0/index.html", new [] { "Version 1.0" })] - [InlineData("/swagger/2.0/index.html", new [] { "Version 2.0" })] - public async Task SwaggerUIMiddleware_CanBeConfiguredMultipleTimes(string swaggerUiUrl, string[] versions) + [InlineData("/swagger/index.html", "/swagger/index.js", new[] { "Version 1.0", "Version 2.0" })] + [InlineData("/swagger/1.0/index.html", "/swagger/1.0/index.js", new[] { "Version 1.0" })] + [InlineData("/swagger/2.0/index.html", "/swagger/2.0/index.js", new[] { "Version 2.0" })] + public async Task SwaggerUIMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string[] versions) { var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); - var response = await client.GetAsync(swaggerUiUrl); - var content = await response.Content.ReadAsStringAsync(); + var htmlResponse = await client.GetAsync(htmlUrl); + var jsResponse = await client.GetAsync(jsUrl); + var content = await jsResponse.Content.ReadAsStringAsync(); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); foreach (var version in versions) { Assert.Contains(version, content); @@ -106,20 +134,20 @@ public async Task SwaggerUIMiddleware_CanBeConfiguredMultipleTimes(string swagge [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/ext/custom-stylesheet.css", "/ext/custom-javascript.js", "/ext/custom-javascript.js")] public async Task IndexUrl_Returns_ExpectedAssetPaths( Type startupType, - string indexPath, + string htmlPath, string cssPath, string scriptBundlePath, string scriptPresetsPath) { var client = new TestSite(startupType).BuildClient(); - var indexResponse = await client.GetAsync(indexPath); - Assert.Equal(HttpStatusCode.OK, indexResponse.StatusCode); - var content = await indexResponse.Content.ReadAsStringAsync(); + var htmlResponse = await client.GetAsync(htmlPath); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + var content = await htmlResponse.Content.ReadAsStringAsync(); Assert.Contains($"", content); - Assert.Contains($""); - options.HeadContent = builder.ToString(); - } - - /// - /// Adds Swagger JSON endpoints. Can be fully-qualified or relative to the UI page - /// - /// - /// Can be fully qualified or relative to the current host - /// The description that appears in the document selector drop-down - public static void SwaggerEndpoint(this SwaggerUIOptions options, string url, string name) - { - var urls = new List(options.ConfigObject.Urls ?? []) - { - new() { Url = url, Name = name } - }; - options.ConfigObject.Urls = urls; - } - - /// - /// Enables deep linking for tags and operations - /// - /// - public static void EnableDeepLinking(this SwaggerUIOptions options) - { - options.ConfigObject.DeepLinking = true; - } - /// - /// Enables persist authorization data - /// - /// - public static void EnablePersistAuthorization(this SwaggerUIOptions options) - { - options.ConfigObject.PersistAuthorization = true; - } - - /// - /// Controls the display of operationId in operations list - /// - /// - public static void DisplayOperationId(this SwaggerUIOptions options) - { - options.ConfigObject.DisplayOperationId = true; - } - - /// - /// The default expansion depth for models (set to -1 completely hide the models) - /// - /// - /// - public static void DefaultModelsExpandDepth(this SwaggerUIOptions options, int depth) - { - options.ConfigObject.DefaultModelsExpandDepth = depth; - } - - /// - /// The default expansion depth for the model on the model-example section - /// - /// - /// - public static void DefaultModelExpandDepth(this SwaggerUIOptions options, int depth) - { - options.ConfigObject.DefaultModelExpandDepth = depth; - } - - /// - /// Controls how the model is shown when the API is first rendered. - /// (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.) - /// - /// - /// - public static void DefaultModelRendering(this SwaggerUIOptions options, ModelRendering modelRendering) - { - options.ConfigObject.DefaultModelRendering = modelRendering; - } - - /// - /// Controls the display of the request duration (in milliseconds) for Try-It-Out requests - /// - /// - public static void DisplayRequestDuration(this SwaggerUIOptions options) - { - options.ConfigObject.DisplayRequestDuration = true; - } - - /// - /// Controls the default expansion setting for the operations and tags. - /// It can be 'List' (expands only the tags), 'Full' (expands the tags and operations) or 'None' (expands nothing) - /// - /// - /// - public static void DocExpansion(this SwaggerUIOptions options, DocExpansion docExpansion) - { - options.ConfigObject.DocExpansion = docExpansion; - } - - /// - /// Enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. - /// If an expression is provided it will be used and applied initially. - /// Filtering is case sensitive matching the filter expression anywhere inside the tag - /// - /// - /// - public static void EnableFilter(this SwaggerUIOptions options, string expression = null) - { - options.ConfigObject.Filter = expression ?? ""; - } - - /// - /// Enables the "Try it out" section by default. - /// - /// - public static void EnableTryItOutByDefault(this SwaggerUIOptions options) - { - options.ConfigObject.TryItOutEnabled = true; - } - - /// - /// Limits the number of tagged operations displayed to at most this many. The default is to show all operations - /// - /// - /// - public static void MaxDisplayedTags(this SwaggerUIOptions options, int count) - { - options.ConfigObject.MaxDisplayedTags = count; - } - - /// - /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema - /// - /// - public static void ShowExtensions(this SwaggerUIOptions options) - { - options.ConfigObject.ShowExtensions = true; - } - - /// - /// Controls the display of extensions (pattern, maxLength, minLength, maximum, minimum) fields and values for Parameters - /// - /// - public static void ShowCommonExtensions(this SwaggerUIOptions options) - { - options.ConfigObject.ShowCommonExtensions = true; - } - - /// - /// List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. - /// This does not filter the operations from the display - /// - /// - /// - public static void SupportedSubmitMethods(this SwaggerUIOptions options, params SubmitMethod[] submitMethods) - { - options.ConfigObject.SupportedSubmitMethods = submitMethods; - } - - /// - /// OAuth redirect URL - /// - /// - /// - public static void OAuth2RedirectUrl(this SwaggerUIOptions options, string url) - { - options.ConfigObject.OAuth2RedirectUrl = url; - } + /// + /// Injects additional CSS stylesheets into the index.html page + /// + /// + /// A path to the stylesheet - i.e. the link "href" attribute + /// The target media - i.e. the link "media" attribute + public static void InjectStylesheet(this SwaggerUIOptions options, string path, string media = "screen") + { + var builder = new StringBuilder(options.HeadContent); + builder.AppendLine($""); + options.HeadContent = builder.ToString(); + } - [Obsolete("The validator is disabled by default. Use EnableValidator to enable it")] - public static void ValidatorUrl(this SwaggerUIOptions options, string url) - { - options.ConfigObject.ValidatorUrl = url; - } - - /// - /// You can use this parameter to enable the swagger-ui's built-in validator (badge) functionality - /// Setting it to null will disable validation - /// - /// - /// - public static void EnableValidator(this SwaggerUIOptions options, string url = "https://online.swagger.io/validator") - { - options.ConfigObject.ValidatorUrl = url; - } - - /// - /// Default clientId - /// - /// - /// - public static void OAuthClientId(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.ClientId = value; - } - - /// - /// Default userName - /// - /// - /// - public static void OAuthUsername(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.Username = value; - } - - /// - /// Default clientSecret - /// - /// - /// - /// Setting this exposes the client secrets in inline javascript in the swagger-ui generated html. - public static void OAuthClientSecret(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.ClientSecret = value; - } - - /// - /// realm query parameter (for oauth1) added to authorizationUrl and tokenUrl - /// - /// - /// - public static void OAuthRealm(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.Realm = value; - } - - /// - /// Application name, displayed in authorization popup - /// - /// - /// - public static void OAuthAppName(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.AppName = value; - } - - /// - /// Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20) - /// - /// - /// - public static void OAuthScopeSeparator(this SwaggerUIOptions options, string value) - { - options.OAuthConfigObject.ScopeSeparator = value; - } + /// + /// Injects additional Javascript files into the index.html page + /// + /// + /// A path to the javascript - i.e. the script "src" attribute + /// The script type - i.e. the script "type" attribute + public static void InjectJavascript(this SwaggerUIOptions options, string path, string type = "text/javascript") + { + var builder = new StringBuilder(options.HeadContent); + builder.AppendLine($""); + options.HeadContent = builder.ToString(); + } - /// - /// String array of initially selected oauth scopes, default is empty array - /// - public static void OAuthScopes(this SwaggerUIOptions options, params string[] scopes) - { - options.OAuthConfigObject.Scopes = scopes; - } - - /// - /// Additional query parameters added to authorizationUrl and tokenUrl - /// - /// - /// - public static void OAuthAdditionalQueryStringParams( - this SwaggerUIOptions options, - Dictionary value) - { - options.OAuthConfigObject.AdditionalQueryStringParams = value; - } - - /// - /// Only activated for the accessCode flow. During the authorization_code request to the tokenUrl, - /// pass the Client Password using the HTTP Basic Authentication scheme (Authorization header with - /// Basic base64encoded[client_id:client_secret]). The default is false - /// - /// - public static void OAuthUseBasicAuthenticationWithAccessCodeGrant(this SwaggerUIOptions options) - { - options.OAuthConfigObject.UseBasicAuthenticationWithAccessCodeGrant = true; - } - - /// - /// Only applies to authorizatonCode flows. Proof Key for Code Exchange brings enhanced security for OAuth public clients. - /// The default is false - /// - /// - public static void OAuthUsePkce(this SwaggerUIOptions options) - { - options.OAuthConfigObject.UsePkceWithAuthorizationCodeGrant = true; - } - - /// - /// Function to intercept remote definition, "Try it out", and OAuth 2.0 requests. - /// - /// - /// MUST be a valid Javascript function: (request: SwaggerRequest) => SwaggerRequest - public static void UseRequestInterceptor(this SwaggerUIOptions options, string value) - { - options.Interceptors.RequestInterceptorFunction = value; - } - - /// - /// Function to intercept remote definition, "Try it out", and OAuth 2.0 responses. - /// - /// - /// MUST be a valid Javascript function: (response: SwaggerResponse ) => SwaggerResponse - public static void UseResponseInterceptor(this SwaggerUIOptions options, string value) - { - options.Interceptors.ResponseInterceptorFunction = value; - } - - /// - /// Function to enable the option to expose the available - /// Swagger document urls to external parties. - /// - /// - public static void EnableSwaggerDocumentUrlsEndpoint(this SwaggerUIOptions options) + /// + /// Adds Swagger JSON endpoints. Can be fully-qualified or relative to the UI page + /// + /// + /// Can be fully qualified or relative to the current host + /// The description that appears in the document selector drop-down + public static void SwaggerEndpoint(this SwaggerUIOptions options, string url, string name) + { + var urls = new List(options.ConfigObject.Urls ?? []) { - options.ExposeSwaggerDocumentUrlsRoute = true; - } + new() { Url = url, Name = name } + }; + options.ConfigObject.Urls = urls; + } + + /// + /// Enables deep linking for tags and operations + /// + /// + public static void EnableDeepLinking(this SwaggerUIOptions options) + { + options.ConfigObject.DeepLinking = true; + } + /// + /// Enables persist authorization data + /// + /// + public static void EnablePersistAuthorization(this SwaggerUIOptions options) + { + options.ConfigObject.PersistAuthorization = true; + } + + /// + /// Controls the display of operationId in operations list + /// + /// + public static void DisplayOperationId(this SwaggerUIOptions options) + { + options.ConfigObject.DisplayOperationId = true; + } + + /// + /// The default expansion depth for models (set to -1 completely hide the models) + /// + /// + /// + public static void DefaultModelsExpandDepth(this SwaggerUIOptions options, int depth) + { + options.ConfigObject.DefaultModelsExpandDepth = depth; + } + + /// + /// The default expansion depth for the model on the model-example section + /// + /// + /// + public static void DefaultModelExpandDepth(this SwaggerUIOptions options, int depth) + { + options.ConfigObject.DefaultModelExpandDepth = depth; + } + + /// + /// Controls how the model is shown when the API is first rendered. + /// (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.) + /// + /// + /// + public static void DefaultModelRendering(this SwaggerUIOptions options, ModelRendering modelRendering) + { + options.ConfigObject.DefaultModelRendering = modelRendering; + } + + /// + /// Controls the display of the request duration (in milliseconds) for Try-It-Out requests + /// + /// + public static void DisplayRequestDuration(this SwaggerUIOptions options) + { + options.ConfigObject.DisplayRequestDuration = true; + } + + /// + /// Controls the default expansion setting for the operations and tags. + /// It can be 'List' (expands only the tags), 'Full' (expands the tags and operations) or 'None' (expands nothing) + /// + /// + /// + public static void DocExpansion(this SwaggerUIOptions options, DocExpansion docExpansion) + { + options.ConfigObject.DocExpansion = docExpansion; + } + + /// + /// Enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. + /// If an expression is provided it will be used and applied initially. + /// Filtering is case sensitive matching the filter expression anywhere inside the tag + /// + /// + /// + public static void EnableFilter(this SwaggerUIOptions options, string expression = null) + { + options.ConfigObject.Filter = expression ?? ""; + } + + /// + /// Enables the "Try it out" section by default. + /// + /// + public static void EnableTryItOutByDefault(this SwaggerUIOptions options) + { + options.ConfigObject.TryItOutEnabled = true; + } + + /// + /// Limits the number of tagged operations displayed to at most this many. The default is to show all operations + /// + /// + /// + public static void MaxDisplayedTags(this SwaggerUIOptions options, int count) + { + options.ConfigObject.MaxDisplayedTags = count; + } + + /// + /// Controls the display of vendor extension (x-) fields and values for Operations, Parameters, and Schema + /// + /// + public static void ShowExtensions(this SwaggerUIOptions options) + { + options.ConfigObject.ShowExtensions = true; + } + + /// + /// Controls the display of extensions (pattern, maxLength, minLength, maximum, minimum) fields and values for Parameters + /// + /// + public static void ShowCommonExtensions(this SwaggerUIOptions options) + { + options.ConfigObject.ShowCommonExtensions = true; + } + + /// + /// List of HTTP methods that have the Try it out feature enabled. An empty array disables Try it out for all operations. + /// This does not filter the operations from the display + /// + /// + /// + public static void SupportedSubmitMethods(this SwaggerUIOptions options, params SubmitMethod[] submitMethods) + { + options.ConfigObject.SupportedSubmitMethods = submitMethods; + } + + /// + /// OAuth redirect URL + /// + /// + /// + public static void OAuth2RedirectUrl(this SwaggerUIOptions options, string url) + { + options.ConfigObject.OAuth2RedirectUrl = url; + } + + [Obsolete("The validator is disabled by default. Use EnableValidator to enable it")] + public static void ValidatorUrl(this SwaggerUIOptions options, string url) + { + options.ConfigObject.ValidatorUrl = url; + } + + /// + /// You can use this parameter to enable the swagger-ui's built-in validator (badge) functionality + /// Setting it to null will disable validation + /// + /// + /// + public static void EnableValidator(this SwaggerUIOptions options, string url = "https://online.swagger.io/validator") + { + options.ConfigObject.ValidatorUrl = url; + } + + /// + /// Default clientId + /// + /// + /// + public static void OAuthClientId(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.ClientId = value; + } + + /// + /// Default userName + /// + /// + /// + public static void OAuthUsername(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.Username = value; + } + + /// + /// Default clientSecret + /// + /// + /// + /// Setting this exposes the client secrets in inline javascript in the swagger-ui generated html. + public static void OAuthClientSecret(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.ClientSecret = value; + } + + /// + /// realm query parameter (for oauth1) added to authorizationUrl and tokenUrl + /// + /// + /// + public static void OAuthRealm(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.Realm = value; + } + + /// + /// Application name, displayed in authorization popup + /// + /// + /// + public static void OAuthAppName(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.AppName = value; + } + + /// + /// Scope separator for passing scopes, encoded before calling, default value is a space (encoded value %20) + /// + /// + /// + public static void OAuthScopeSeparator(this SwaggerUIOptions options, string value) + { + options.OAuthConfigObject.ScopeSeparator = value; + } + + /// + /// String array of initially selected oauth scopes, default is empty array + /// + public static void OAuthScopes(this SwaggerUIOptions options, params string[] scopes) + { + options.OAuthConfigObject.Scopes = scopes; + } + + /// + /// Additional query parameters added to authorizationUrl and tokenUrl + /// + /// + /// + public static void OAuthAdditionalQueryStringParams( + this SwaggerUIOptions options, + Dictionary value) + { + options.OAuthConfigObject.AdditionalQueryStringParams = value; + } + + /// + /// Only activated for the accessCode flow. During the authorization_code request to the tokenUrl, + /// pass the Client Password using the HTTP Basic Authentication scheme (Authorization header with + /// Basic base64encoded[client_id:client_secret]). The default is false + /// + /// + public static void OAuthUseBasicAuthenticationWithAccessCodeGrant(this SwaggerUIOptions options) + { + options.OAuthConfigObject.UseBasicAuthenticationWithAccessCodeGrant = true; + } + + /// + /// Only applies to authorizatonCode flows. Proof Key for Code Exchange brings enhanced security for OAuth public clients. + /// The default is false + /// + /// + public static void OAuthUsePkce(this SwaggerUIOptions options) + { + options.OAuthConfigObject.UsePkceWithAuthorizationCodeGrant = true; + } + + /// + /// Function to intercept remote definition, "Try it out", and OAuth 2.0 requests. + /// + /// + /// MUST be a valid Javascript function: (request: SwaggerRequest) => SwaggerRequest + public static void UseRequestInterceptor(this SwaggerUIOptions options, string value) + { + options.Interceptors.RequestInterceptorFunction = value; + } + + /// + /// Function to intercept remote definition, "Try it out", and OAuth 2.0 responses. + /// + /// + /// MUST be a valid Javascript function: (response: SwaggerResponse ) => SwaggerResponse + public static void UseResponseInterceptor(this SwaggerUIOptions options, string value) + { + options.Interceptors.ResponseInterceptorFunction = value; + } + + /// + /// Function to enable the option to expose the available + /// Swagger document urls to external parties. + /// + /// + public static void EnableSwaggerDocumentUrlsEndpoint(this SwaggerUIOptions options) + { + options.ExposeSwaggerDocumentUrlsRoute = true; } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs index 5c561d5cfa..9349fe26eb 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs @@ -3,30 +3,29 @@ using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.TestSupport; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class AnnotationsDocumentFilterTests { - public class AnnotationsDocumentFilterTests + [Fact] + public void Apply_CreatesMetadataForControllerNameTag_FromSwaggerTagAttribute() { - [Fact] - public void Apply_CreatesMetadataForControllerNameTag_FromSwaggerTagAttribute() - { - var document = new OpenApiDocument(); - var apiDescription = ApiDescriptionFactory.Create(c => nameof(c.ActionWithNoAttributes)); - var filterContext = new DocumentFilterContext( - apiDescriptions: new[] { apiDescription }, - schemaGenerator: null, - schemaRepository: null); + var document = new OpenApiDocument(); + var apiDescription = ApiDescriptionFactory.Create(c => nameof(c.ActionWithNoAttributes)); + var filterContext = new DocumentFilterContext( + apiDescriptions: new[] { apiDescription }, + schemaGenerator: null, + schemaRepository: null); - Subject().Apply(document, filterContext); + Subject().Apply(document, filterContext); - var tag = document.Tags.Single(t => t.Name == "FakeControllerWithSwaggerAnnotations"); - Assert.Equal("Description for FakeControllerWithSwaggerAnnotations", tag.Description); - Assert.Equal("http://tempuri.org/", tag.ExternalDocs.Url.ToString()); - } + var tag = document.Tags.Single(t => t.Name == "FakeControllerWithSwaggerAnnotations"); + Assert.Equal("Description for FakeControllerWithSwaggerAnnotations", tag.Description); + Assert.Equal("http://tempuri.org/", tag.ExternalDocs.Url.ToString()); + } - private AnnotationsDocumentFilter Subject() - { - return new AnnotationsDocumentFilter(); - } + private AnnotationsDocumentFilter Subject() + { + return new AnnotationsDocumentFilter(); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs index 2a2b03a5b4..3478d7b284 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs @@ -6,165 +6,164 @@ using Swashbuckle.AspNetCore.SwaggerGen; using Xunit; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class AnnotationsOperationFilterTests { - public class AnnotationsOperationFilterTests + [Fact] + public void Apply_EnrichesOperationMetadata_IfActionDecoratedWithSwaggerOperationAttribute() { - [Fact] - public void Apply_EnrichesOperationMetadata_IfActionDecoratedWithSwaggerOperationAttribute() - { - var operation = new OpenApiOperation(); - var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerOperationAttribute)); - var filterContext = new OperationFilterContext( - apiDescription: null, - schemaRegistry: null, - schemaRepository: null, - methodInfo: methodInfo); - - Subject().Apply(operation, filterContext); - - Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); - Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); - Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); - Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); - } - - [Fact] - public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseAttribute() + var operation = new OpenApiOperation(); + var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerOperationAttribute)); + var filterContext = new OperationFilterContext( + apiDescription: null, + schemaRegistry: null, + schemaRepository: null, + methodInfo: methodInfo); + + Subject().Apply(operation, filterContext); + + Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); + Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); + Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); + Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); + } + + [Fact] + public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseAttribute() + { + var operation = new OpenApiOperation { - var operation = new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses - { - { "204", new OpenApiResponse { } }, - { "400", new OpenApiResponse { } }, - } - }; - var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerResponseAttributes)); - var filterContext = new OperationFilterContext( - apiDescription: null, - schemaRegistry: null, - schemaRepository: null, - methodInfo: methodInfo); - - Subject().Apply(operation, filterContext); - - Assert.Equal(["204", "400", "500"], [.. operation.Responses.Keys]); - var response1 = operation.Responses["204"]; - Assert.Equal("Description for 204 response", response1.Description); - var response2 = operation.Responses["400"]; - Assert.Equal("Description for 400 response", response2.Description); - var response3 = operation.Responses["500"]; - Assert.Equal("Description for 500 response", response3.Description); - } - - [Fact] - public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseContentTypesAttribute() + { "204", new OpenApiResponse { } }, + { "400", new OpenApiResponse { } }, + } + }; + var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerResponseAttributes)); + var filterContext = new OperationFilterContext( + apiDescription: null, + schemaRegistry: null, + schemaRepository: null, + methodInfo: methodInfo); + + Subject().Apply(operation, filterContext); + + Assert.Equal(["204", "400", "500"], [.. operation.Responses.Keys]); + var response1 = operation.Responses["204"]; + Assert.Equal("Description for 204 response", response1.Description); + var response2 = operation.Responses["400"]; + Assert.Equal("Description for 400 response", response2.Description); + var response3 = operation.Responses["500"]; + Assert.Equal("Description for 500 response", response3.Description); + } + + [Fact] + public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseContentTypesAttribute() + { + var operation = new OpenApiOperation { - var operation = new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses - { - { "200", new OpenApiResponse { } }, - } - }; - var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerResponseContentTypesAttributes)); - var filterContext = new OperationFilterContext( - apiDescription: null, - schemaRegistry: new SchemaGenerator(new SchemaGeneratorOptions(), new JsonSerializerDataContractResolver(new JsonSerializerOptions())), - schemaRepository: new SchemaRepository(), - methodInfo: methodInfo); - - Subject().Apply(operation, filterContext); - - Assert.Equal(["200", "500"], [.. operation.Responses.Keys]); - var response1 = operation.Responses["200"]; - Assert.Equal("Description for 200 response", response1.Description); - Assert.NotNull(response1.Content); - Assert.Equal(2, response1.Content.Count); - var jsonContent = response1.Content.First(); - var xmlContent = response1.Content.Last(); - Assert.Equal("application/json", jsonContent.Key); - Assert.Equal(JsonSchemaTypes.String, jsonContent.Value.Schema.Type); - Assert.Equal("application/xml", xmlContent.Key); - Assert.Equal(JsonSchemaTypes.String, xmlContent.Value.Schema.Type); - } - - [Fact] - public void Apply_DelegatesToSpecifiedFilter_IfControllerDecoratedWithSwaggerOperationFilterAttribute() - { - var operation = new OpenApiOperation(); - var methodInfo = typeof(FakeControllerWithSwaggerAnnotations).GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithNoAttributes)); - var filterContext = new OperationFilterContext( - apiDescription: null, - schemaRegistry: null, - schemaRepository: null, - methodInfo: methodInfo); + { "200", new OpenApiResponse { } }, + } + }; + var methodInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerResponseContentTypesAttributes)); + var filterContext = new OperationFilterContext( + apiDescription: null, + schemaRegistry: new SchemaGenerator(new SchemaGeneratorOptions(), new JsonSerializerDataContractResolver(new JsonSerializerOptions())), + schemaRepository: new SchemaRepository(), + methodInfo: methodInfo); + + Subject().Apply(operation, filterContext); + + Assert.Equal(["200", "500"], [.. operation.Responses.Keys]); + var response1 = operation.Responses["200"]; + Assert.Equal("Description for 200 response", response1.Description); + Assert.NotNull(response1.Content); + Assert.Equal(2, response1.Content.Count); + var jsonContent = response1.Content.First(); + var xmlContent = response1.Content.Last(); + Assert.Equal("application/json", jsonContent.Key); + Assert.Equal(JsonSchemaTypes.String, jsonContent.Value.Schema.Type); + Assert.Equal("application/xml", xmlContent.Key); + Assert.Equal(JsonSchemaTypes.String, xmlContent.Value.Schema.Type); + } - Subject().Apply(operation, filterContext); + [Fact] + public void Apply_DelegatesToSpecifiedFilter_IfControllerDecoratedWithSwaggerOperationFilterAttribute() + { + var operation = new OpenApiOperation(); + var methodInfo = typeof(FakeControllerWithSwaggerAnnotations).GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithNoAttributes)); + var filterContext = new OperationFilterContext( + apiDescription: null, + schemaRegistry: null, + schemaRepository: null, + methodInfo: methodInfo); - Assert.NotEmpty(operation.Extensions); - } + Subject().Apply(operation, filterContext); - [Fact] - public void Apply_DelegatesToSpecifiedFilter_IfActionDecoratedWithSwaggerOperationFilterAttribute() - { - var operation = new OpenApiOperation(); - var methodInfo = typeof(FakeControllerWithSwaggerAnnotations).GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerOperationFilterAttribute)); - var filterContext = new OperationFilterContext( - apiDescription: null, - schemaRegistry: null, - schemaRepository: null, - methodInfo: methodInfo); + Assert.NotEmpty(operation.Extensions); + } + + [Fact] + public void Apply_DelegatesToSpecifiedFilter_IfActionDecoratedWithSwaggerOperationFilterAttribute() + { + var operation = new OpenApiOperation(); + var methodInfo = typeof(FakeControllerWithSwaggerAnnotations).GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerOperationFilterAttribute)); + var filterContext = new OperationFilterContext( + apiDescription: null, + schemaRegistry: null, + schemaRepository: null, + methodInfo: methodInfo); - Subject().Apply(operation, filterContext); + Subject().Apply(operation, filterContext); - Assert.NotEmpty(operation.Extensions); - } + Assert.NotEmpty(operation.Extensions); + } - [Fact] - public void Apply_EnrichesOperationMetadata_IfMinimalActionDecoratedWithSwaggerOperationAttribute() + [Fact] + public void Apply_EnrichesOperationMetadata_IfMinimalActionDecoratedWithSwaggerOperationAttribute() + { + var operationAttribute = new SwaggerOperationAttribute("Summary for ActionWithSwaggerOperationAttribute") { - var operationAttribute = new SwaggerOperationAttribute("Summary for ActionWithSwaggerOperationAttribute") - { - Description = "Description for ActionWithSwaggerOperationAttribute", - OperationId = "actionWithSwaggerOperationAttribute", - Tags = ["foobar"] - }; + Description = "Description for ActionWithSwaggerOperationAttribute", + OperationId = "actionWithSwaggerOperationAttribute", + Tags = ["foobar"] + }; - var action = RequestDelegateFactory.Create((string parameter) => "{}"); + var action = RequestDelegateFactory.Create((string parameter) => "{}"); - var operation = new OpenApiOperation(); - var methodInfo = action.RequestDelegate.Method; + var operation = new OpenApiOperation(); + var methodInfo = action.RequestDelegate.Method; - var apiDescription = new ApiDescription() - { - ActionDescriptor = new ActionDescriptor() - { - EndpointMetadata = [operationAttribute] - } - }; - - var filterContext = new OperationFilterContext( - apiDescription: apiDescription, - schemaRegistry: null, - schemaRepository: null, - methodInfo: methodInfo); - - Subject().Apply(operation, filterContext); - - Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); - Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); - Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); - Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); - } - - private static AnnotationsOperationFilter Subject() + var apiDescription = new ApiDescription() { - return new(); - } + ActionDescriptor = new ActionDescriptor() + { + EndpointMetadata = [operationAttribute] + } + }; + + var filterContext = new OperationFilterContext( + apiDescription: apiDescription, + schemaRegistry: null, + schemaRepository: null, + methodInfo: methodInfo); + + Subject().Apply(operation, filterContext); + + Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); + Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); + Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); + Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); + } + + private static AnnotationsOperationFilter Subject() + { + return new(); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs index ab3a71c883..48af7a7665 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs @@ -3,67 +3,66 @@ using Xunit; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class AnnotationsParameterFilterTests { - public class AnnotationsParameterFilterTests + [Fact] + public void Apply_EnrichesParameterMetadata_IfParameterDecoratedWithSwaggerParameterAttribute() { - [Fact] - public void Apply_EnrichesParameterMetadata_IfParameterDecoratedWithSwaggerParameterAttribute() - { - var parameter = new OpenApiParameter { }; - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerParameterAttribute)) - .GetParameters()[0]; - var filterContext = new ParameterFilterContext( - apiParameterDescription: null, - schemaGenerator: null, - schemaRepository: null, - parameterInfo: parameterInfo); + var parameter = new OpenApiParameter { }; + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerParameterAttribute)) + .GetParameters()[0]; + var filterContext = new ParameterFilterContext( + apiParameterDescription: null, + schemaGenerator: null, + schemaRepository: null, + parameterInfo: parameterInfo); - Subject().Apply(parameter, filterContext); + Subject().Apply(parameter, filterContext); - Assert.Equal("Description for param", parameter.Description); - Assert.True(parameter.Required); - } + Assert.Equal("Description for param", parameter.Description); + Assert.True(parameter.Required); + } - [Fact] - public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerParameterAttribute() - { - var parameter = new OpenApiParameter(); - var propertyInfo = typeof(SwaggerAnnotatedType).GetProperty(nameof(SwaggerAnnotatedType.StringWithSwaggerParameterAttribute)); - var filterContext = new ParameterFilterContext( - apiParameterDescription: new ApiParameterDescription(), - schemaGenerator: null, - schemaRepository: null, - propertyInfo: propertyInfo); + [Fact] + public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerParameterAttribute() + { + var parameter = new OpenApiParameter(); + var propertyInfo = typeof(SwaggerAnnotatedType).GetProperty(nameof(SwaggerAnnotatedType.StringWithSwaggerParameterAttribute)); + var filterContext = new ParameterFilterContext( + apiParameterDescription: new ApiParameterDescription(), + schemaGenerator: null, + schemaRepository: null, + propertyInfo: propertyInfo); - Subject().Apply(parameter, filterContext); + Subject().Apply(parameter, filterContext); - Assert.Equal("Description for StringWithSwaggerParameterAttribute", parameter.Description); - Assert.True(parameter.Required); - } + Assert.Equal("Description for StringWithSwaggerParameterAttribute", parameter.Description); + Assert.True(parameter.Required); + } - [Fact] - public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParameterAttribute() - { - var parameter = new OpenApiParameter { Required = true }; - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerParameterAttributeDescriptionOnly)) - .GetParameters()[0]; - var filterContext = new ParameterFilterContext( - apiParameterDescription: null, - schemaGenerator: null, - schemaRepository: null, - parameterInfo: parameterInfo); + [Fact] + public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParameterAttribute() + { + var parameter = new OpenApiParameter { Required = true }; + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerParameterAttributeDescriptionOnly)) + .GetParameters()[0]; + var filterContext = new ParameterFilterContext( + apiParameterDescription: null, + schemaGenerator: null, + schemaRepository: null, + parameterInfo: parameterInfo); - Subject().Apply(parameter, filterContext); + Subject().Apply(parameter, filterContext); - Assert.True(parameter.Required); - } + Assert.True(parameter.Required); + } - private AnnotationsParameterFilter Subject() - { - return new AnnotationsParameterFilter(); - } + private AnnotationsParameterFilter Subject() + { + return new AnnotationsParameterFilter(); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs index 0ab2d4bed4..fc2a93f672 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs @@ -8,91 +8,90 @@ using Swashbuckle.AspNetCore.TestSupport; using Xunit; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class AnnotationsRequestBodyFilterTests { - public class AnnotationsRequestBodyFilterTests + [Fact] + public void Apply_EnrichesRequestBodyMetadata_IfControllerParameterDecoratedWithSwaggerRequestBodyAttribute() { - [Fact] - public void Apply_EnrichesRequestBodyMetadata_IfControllerParameterDecoratedWithSwaggerRequestBodyAttribute() + var requestBody = new OpenApiRequestBody(); + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestBodyAttribute)) + .GetParameters()[0]; + var bodyParameterDescription = new ApiParameterDescription { - var requestBody = new OpenApiRequestBody(); - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestBodyAttribute)) - .GetParameters()[0]; - var bodyParameterDescription = new ApiParameterDescription - { - ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } - }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } + }; + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); - Subject().Apply(requestBody, context); + Subject().Apply(requestBody, context); - Assert.Equal("Description for param", requestBody.Description); - Assert.True(requestBody.Required); - } + Assert.Equal("Description for param", requestBody.Description); + Assert.True(requestBody.Required); + } - [Fact] - public void Apply_EnrichesRequestBodyMetadata_IfEndpointParameterDecoratedWithSwaggerRequestBodyAttribute() + [Fact] + public void Apply_EnrichesRequestBodyMetadata_IfEndpointParameterDecoratedWithSwaggerRequestBodyAttribute() + { + var requestBody = new OpenApiRequestBody(); + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestBodyAttribute)) + .GetParameters()[0]; + var bodyParameterDescription = new ApiParameterDescription { - var requestBody = new OpenApiRequestBody(); - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestBodyAttribute)) - .GetParameters()[0]; - var bodyParameterDescription = new ApiParameterDescription - { - ParameterDescriptor = new CustomParameterDescriptor { ParameterInfo = parameterInfo } - }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + ParameterDescriptor = new CustomParameterDescriptor { ParameterInfo = parameterInfo } + }; + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); - Subject().Apply(requestBody, context); + Subject().Apply(requestBody, context); - Assert.Equal("Description for param", requestBody.Description); - Assert.True(requestBody.Required); - } + Assert.Equal("Description for param", requestBody.Description); + Assert.True(requestBody.Required); + } - [Fact] - public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerRequestBodyAttribute() + [Fact] + public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerRequestBodyAttribute() + { + var requestBody = new OpenApiRequestBody(); + var bodyParameterDescription = new ApiParameterDescription { - var requestBody = new OpenApiRequestBody(); - var bodyParameterDescription = new ApiParameterDescription - { - ModelMetadata = ModelMetadataFactory.CreateForProperty(typeof(SwaggerAnnotatedType), nameof(SwaggerAnnotatedType.StringWithSwaggerRequestBodyAttribute)) - }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + ModelMetadata = ModelMetadataFactory.CreateForProperty(typeof(SwaggerAnnotatedType), nameof(SwaggerAnnotatedType.StringWithSwaggerRequestBodyAttribute)) + }; + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); - Subject().Apply(requestBody, context); - - Assert.Equal("Description for StringWithSwaggerRequestBodyAttribute", requestBody.Description); - Assert.True(requestBody.Required); - } + Subject().Apply(requestBody, context); + + Assert.Equal("Description for StringWithSwaggerRequestBodyAttribute", requestBody.Description); + Assert.True(requestBody.Required); + } - [Fact] - public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParameterAttribute() - { + [Fact] + public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParameterAttribute() + { - var requestBody = new OpenApiRequestBody { Required = true }; - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestbodyAttributeDescriptionOnly)) - .GetParameters()[0]; - var bodyParameterDescription = new ApiParameterDescription - { - ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } - }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var requestBody = new OpenApiRequestBody { Required = true }; + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerRequestbodyAttributeDescriptionOnly)) + .GetParameters()[0]; + var bodyParameterDescription = new ApiParameterDescription + { + ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } + }; + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); - Subject().Apply(requestBody, context); + Subject().Apply(requestBody, context); - Assert.True(requestBody.Required); - } + Assert.True(requestBody.Required); + } - private AnnotationsRequestBodyFilter Subject() - { - return new AnnotationsRequestBodyFilter(); - } + private AnnotationsRequestBodyFilter Subject() + { + return new AnnotationsRequestBodyFilter(); + } - private sealed class CustomParameterDescriptor : ParameterDescriptor, IParameterInfoParameterDescriptor - { - public ParameterInfo ParameterInfo { get; set; } - } + private sealed class CustomParameterDescriptor : ParameterDescriptor, IParameterInfoParameterDescriptor + { + public ParameterInfo ParameterInfo { get; set; } } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs index e99bf8255b..69136a9b55 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs @@ -3,110 +3,109 @@ using Swashbuckle.AspNetCore.SwaggerGen; using Xunit; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class AnnotationsSchemaFilterTests { - public class AnnotationsSchemaFilterTests + [Theory] + [InlineData(typeof(SwaggerAnnotatedType))] + [InlineData(typeof(SwaggerAnnotatedStruct))] + public void Apply_EnrichesSchemaMetadata_IfTypeDecoratedWithSwaggerSchemaAttribute(Type type) + { + var schema = new OpenApiSchema(); + var context = new SchemaFilterContext(type: type, schemaGenerator: null, schemaRepository: null); + + Subject().Apply(schema, context); + + Assert.Equal($"Description for {type.Name}", schema.Description); + Assert.Equal(new[] { "StringWithSwaggerSchemaAttribute" }, schema.Required); + Assert.Equal($"Title for {type.Name}", schema.Title); + } + + [Fact] + public void Apply_EnrichesSchemaMetadata_IfParameterDecoratedWithSwaggerSchemaAttribute() + { + var schema = new OpenApiSchema(); + var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) + .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerSchemaAttribute)) + .GetParameters()[0]; + var context = new SchemaFilterContext( + type: parameterInfo.ParameterType, + schemaGenerator: null, + schemaRepository: null, + parameterInfo: parameterInfo); + + Subject().Apply(schema, context); + + Assert.Equal($"Description for param", schema.Description); + Assert.Equal("date", schema.Format); + } + + [Theory] + [InlineData(typeof(SwaggerAnnotatedType), nameof(SwaggerAnnotatedType.StringWithSwaggerSchemaAttribute), true, true, false)] + [InlineData(typeof(SwaggerAnnotatedStruct), nameof(SwaggerAnnotatedStruct.StringWithSwaggerSchemaAttribute), true, true, false)] + public void Apply_EnrichesSchemaMetadata_IfPropertyDecoratedWithSwaggerSchemaAttribute( + Type declaringType, + string propertyName, + bool expectedReadOnly, + bool expectedWriteOnly, + bool expectedNullable) + { + var schema = new OpenApiSchema { Nullable = true }; + var propertyInfo = declaringType + .GetProperty(propertyName); + var context = new SchemaFilterContext( + type: propertyInfo.PropertyType, + schemaGenerator: null, + schemaRepository: null, + memberInfo: propertyInfo); + + Subject().Apply(schema, context); + + Assert.Equal($"Description for {propertyName}", schema.Description); + Assert.Equal("date", schema.Format); + Assert.Equal(expectedReadOnly, schema.ReadOnly); + Assert.Equal(expectedWriteOnly, schema.WriteOnly); + Assert.Equal(expectedNullable, schema.Nullable); + } + + [Fact] + public void Apply_DoesNotModifyFlags_IfNotSpecifiedWithSwaggerSchemaAttribute() + { + var schema = new OpenApiSchema { ReadOnly = true, WriteOnly = true, Nullable = true }; + var propertyInfo = typeof(SwaggerAnnotatedType) + .GetProperty(nameof(SwaggerAnnotatedType.StringWithSwaggerSchemaAttributeDescriptionOnly)); + var context = new SchemaFilterContext( + type: propertyInfo.PropertyType, + schemaGenerator: null, + schemaRepository: null, + memberInfo: propertyInfo); + + Subject().Apply(schema, context); + + Assert.True(schema.ReadOnly); + Assert.True(schema.WriteOnly); + Assert.True(schema.Nullable); + } + + [Theory] + [InlineData(typeof(SwaggerAnnotatedType))] + [InlineData(typeof(SwaggerAnnotatedStruct))] + public void Apply_DelegatesToSpecifiedFilter_IfTypeDecoratedWithFilterAttribute(Type type) + { + var schema = new OpenApiSchema(); + var context = new SchemaFilterContext(type: type, schemaGenerator: null, schemaRepository: null); + + Subject().Apply(schema, context); + + Assert.NotEmpty(schema.Extensions); + } + + private static AnnotationsSchemaFilter Subject() { - [Theory] - [InlineData(typeof(SwaggerAnnotatedType))] - [InlineData(typeof(SwaggerAnnotatedStruct))] - public void Apply_EnrichesSchemaMetadata_IfTypeDecoratedWithSwaggerSchemaAttribute(Type type) - { - var schema = new OpenApiSchema(); - var context = new SchemaFilterContext(type: type, schemaGenerator: null, schemaRepository: null); - - Subject().Apply(schema, context); - - Assert.Equal($"Description for {type.Name}", schema.Description); - Assert.Equal(new[] { "StringWithSwaggerSchemaAttribute" }, schema.Required); - Assert.Equal($"Title for {type.Name}", schema.Title); - } - - [Fact] - public void Apply_EnrichesSchemaMetadata_IfParameterDecoratedWithSwaggerSchemaAttribute() - { - var schema = new OpenApiSchema(); - var parameterInfo = typeof(FakeControllerWithSwaggerAnnotations) - .GetMethod(nameof(FakeControllerWithSwaggerAnnotations.ActionWithSwaggerSchemaAttribute)) - .GetParameters()[0]; - var context = new SchemaFilterContext( - type: parameterInfo.ParameterType, - schemaGenerator: null, - schemaRepository: null, - parameterInfo: parameterInfo); - - Subject().Apply(schema, context); - - Assert.Equal($"Description for param", schema.Description); - Assert.Equal("date", schema.Format); - } - - [Theory] - [InlineData(typeof(SwaggerAnnotatedType), nameof(SwaggerAnnotatedType.StringWithSwaggerSchemaAttribute), true, true, false)] - [InlineData(typeof(SwaggerAnnotatedStruct), nameof(SwaggerAnnotatedStruct.StringWithSwaggerSchemaAttribute), true, true, false)] - public void Apply_EnrichesSchemaMetadata_IfPropertyDecoratedWithSwaggerSchemaAttribute( - Type declaringType, - string propertyName, - bool expectedReadOnly, - bool expectedWriteOnly, - bool expectedNullable) - { - var schema = new OpenApiSchema { Nullable = true }; - var propertyInfo = declaringType - .GetProperty(propertyName); - var context = new SchemaFilterContext( - type: propertyInfo.PropertyType, - schemaGenerator: null, - schemaRepository: null, - memberInfo: propertyInfo); - - Subject().Apply(schema, context); - - Assert.Equal($"Description for {propertyName}", schema.Description); - Assert.Equal("date", schema.Format); - Assert.Equal(expectedReadOnly, schema.ReadOnly); - Assert.Equal(expectedWriteOnly, schema.WriteOnly); - Assert.Equal(expectedNullable, schema.Nullable); - } - - [Fact] - public void Apply_DoesNotModifyFlags_IfNotSpecifiedWithSwaggerSchemaAttribute() - { - var schema = new OpenApiSchema { ReadOnly = true, WriteOnly = true, Nullable = true }; - var propertyInfo = typeof(SwaggerAnnotatedType) - .GetProperty(nameof(SwaggerAnnotatedType.StringWithSwaggerSchemaAttributeDescriptionOnly)); - var context = new SchemaFilterContext( - type: propertyInfo.PropertyType, - schemaGenerator: null, - schemaRepository: null, - memberInfo: propertyInfo); - - Subject().Apply(schema, context); - - Assert.True(schema.ReadOnly); - Assert.True(schema.WriteOnly); - Assert.True(schema.Nullable); - } - - [Theory] - [InlineData(typeof(SwaggerAnnotatedType))] - [InlineData(typeof(SwaggerAnnotatedStruct))] - public void Apply_DelegatesToSpecifiedFilter_IfTypeDecoratedWithFilterAttribute(Type type) - { - var schema = new OpenApiSchema(); - var context = new SchemaFilterContext(type: type, schemaGenerator: null, schemaRepository: null); - - Subject().Apply(schema, context); - - Assert.NotEmpty(schema.Extensions); - } - - private static AnnotationsSchemaFilter Subject() - { - // A service provider is required from .NET 8 onwards. - // See https://learn.microsoft.com/dotnet/core/compatibility/extensions/8.0/activatorutilities-createinstance-null-provider. - var serviceProvider = new ServiceCollection().BuildServiceProvider(); - return new AnnotationsSchemaFilter(serviceProvider); - } + // A service provider is required from .NET 8 onwards. + // See https://learn.microsoft.com/dotnet/core/compatibility/extensions/8.0/activatorutilities-createinstance-null-provider. + var serviceProvider = new ServiceCollection().BuildServiceProvider(); + return new AnnotationsSchemaFilter(serviceProvider); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs index 87af3c17b3..ecc0efbbfc 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs @@ -1,58 +1,57 @@ using Microsoft.AspNetCore.Mvc; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +[SwaggerOperationFilter(typeof(VendorExtensionsOperationFilter))] +[SwaggerResponse(500, "Description for 500 response", typeof(IDictionary))] +[SwaggerTag("Description for FakeControllerWithSwaggerAnnotations", "http://tempuri.org")] +internal class FakeControllerWithSwaggerAnnotations { - [SwaggerOperationFilter(typeof(VendorExtensionsOperationFilter))] - [SwaggerResponse(500, "Description for 500 response", typeof(IDictionary))] - [SwaggerTag("Description for FakeControllerWithSwaggerAnnotations", "http://tempuri.org")] - internal class FakeControllerWithSwaggerAnnotations + [SwaggerOperation("Summary for ActionWithSwaggerOperationAttribute", + Description = "Description for ActionWithSwaggerOperationAttribute", + OperationId = "actionWithSwaggerOperationAttribute", + Tags = new[] { "foobar" } + )] + public void ActionWithSwaggerOperationAttribute() + { } + + public void ActionWithSwaggerParameterAttribute( + [SwaggerParameter("Description for param", Required = true)] string param) + { } + + public void ActionWithSwaggerParameterAttributeDescriptionOnly( + [SwaggerParameter("Description for param")] string param) + { } + + public void ActionWithSwaggerSchemaAttribute( + [SwaggerSchema("Description for param", Format = "date")] string param) + { } + + public void ActionWithSwaggerRequestBodyAttribute( + [SwaggerRequestBody("Description for param", Required = true)] string param) + { } + + public void ActionWithSwaggerRequestbodyAttributeDescriptionOnly( + [SwaggerRequestBody("Description for param")] string param) + { } + + [SwaggerResponse(204, "Description for 204 response")] + [SwaggerResponse(400, "Description for 400 response", typeof(IDictionary))] + public IActionResult ActionWithSwaggerResponseAttributes() + { + throw new NotImplementedException(); + } + + [SwaggerResponse(200, "Description for 200 response", typeof(string), "application/json", "application/xml")] + public IActionResult ActionWithSwaggerResponseContentTypesAttributes() { - [SwaggerOperation("Summary for ActionWithSwaggerOperationAttribute", - Description = "Description for ActionWithSwaggerOperationAttribute", - OperationId = "actionWithSwaggerOperationAttribute", - Tags = new[] { "foobar" } - )] - public void ActionWithSwaggerOperationAttribute() - { } - - public void ActionWithSwaggerParameterAttribute( - [SwaggerParameter("Description for param", Required = true)] string param) - { } - - public void ActionWithSwaggerParameterAttributeDescriptionOnly( - [SwaggerParameter("Description for param")] string param) - { } - - public void ActionWithSwaggerSchemaAttribute( - [SwaggerSchema("Description for param", Format = "date")] string param) - { } - - public void ActionWithSwaggerRequestBodyAttribute( - [SwaggerRequestBody("Description for param", Required = true)] string param) - { } - - public void ActionWithSwaggerRequestbodyAttributeDescriptionOnly( - [SwaggerRequestBody("Description for param")] string param) - { } - - [SwaggerResponse(204, "Description for 204 response")] - [SwaggerResponse(400, "Description for 400 response", typeof(IDictionary))] - public IActionResult ActionWithSwaggerResponseAttributes() - { - throw new NotImplementedException(); - } - - [SwaggerResponse(200, "Description for 200 response", typeof(string), "application/json", "application/xml")] - public IActionResult ActionWithSwaggerResponseContentTypesAttributes() - { - throw new NotImplementedException(); - } - - public void ActionWithNoAttributes() - { } - - [SwaggerOperationFilter(typeof(VendorExtensionsOperationFilter))] - public void ActionWithSwaggerOperationFilterAttribute() - { } + throw new NotImplementedException(); } + + public void ActionWithNoAttributes() + { } + + [SwaggerOperationFilter(typeof(VendorExtensionsOperationFilter))] + public void ActionWithSwaggerOperationFilterAttribute() + { } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/SwaggerAnnotatedType.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/SwaggerAnnotatedType.cs index 54f363fc39..b88959c84d 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/SwaggerAnnotatedType.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/SwaggerAnnotatedType.cs @@ -1,27 +1,26 @@ -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +[SwaggerSchema("Description for SwaggerAnnotatedType", Required = new[] { "StringWithSwaggerSchemaAttribute" }, Title = "Title for SwaggerAnnotatedType")] +[SwaggerSchemaFilter(typeof(VendorExtensionsSchemaFilter))] +public class SwaggerAnnotatedType { - [SwaggerSchema("Description for SwaggerAnnotatedType", Required = new[] { "StringWithSwaggerSchemaAttribute" }, Title = "Title for SwaggerAnnotatedType")] - [SwaggerSchemaFilter(typeof(VendorExtensionsSchemaFilter))] - public class SwaggerAnnotatedType - { - [SwaggerSchema("Description for StringWithSwaggerSchemaAttribute", Format = "date", ReadOnly = true, WriteOnly = true, Nullable = false)] - public string StringWithSwaggerSchemaAttribute { get; set; } + [SwaggerSchema("Description for StringWithSwaggerSchemaAttribute", Format = "date", ReadOnly = true, WriteOnly = true, Nullable = false)] + public string StringWithSwaggerSchemaAttribute { get; set; } - [SwaggerSchema("Description for StringWithSwaggerSchemaAttributeDescriptionOnly")] - public string StringWithSwaggerSchemaAttributeDescriptionOnly { get; set; } + [SwaggerSchema("Description for StringWithSwaggerSchemaAttributeDescriptionOnly")] + public string StringWithSwaggerSchemaAttributeDescriptionOnly { get; set; } - [SwaggerParameter("Description for StringWithSwaggerParameterAttribute", Required = true)] - public string StringWithSwaggerParameterAttribute { get; set; } + [SwaggerParameter("Description for StringWithSwaggerParameterAttribute", Required = true)] + public string StringWithSwaggerParameterAttribute { get; set; } - [SwaggerRequestBody("Description for StringWithSwaggerRequestBodyAttribute", Required = true)] - public string StringWithSwaggerRequestBodyAttribute { get; set; } - } + [SwaggerRequestBody("Description for StringWithSwaggerRequestBodyAttribute", Required = true)] + public string StringWithSwaggerRequestBodyAttribute { get; set; } +} - [SwaggerSchema("Description for SwaggerAnnotatedStruct", Required = new[] { "StringWithSwaggerSchemaAttribute" }, Title = "Title for SwaggerAnnotatedStruct")] - [SwaggerSchemaFilter(typeof(VendorExtensionsSchemaFilter))] - public struct SwaggerAnnotatedStruct - { - [SwaggerSchema("Description for StringWithSwaggerSchemaAttribute", Format = "date", ReadOnly = true, WriteOnly = true, Nullable = false)] - public string StringWithSwaggerSchemaAttribute { get; set; } - } -} \ No newline at end of file +[SwaggerSchema("Description for SwaggerAnnotatedStruct", Required = new[] { "StringWithSwaggerSchemaAttribute" }, Title = "Title for SwaggerAnnotatedStruct")] +[SwaggerSchemaFilter(typeof(VendorExtensionsSchemaFilter))] +public struct SwaggerAnnotatedStruct +{ + [SwaggerSchema("Description for StringWithSwaggerSchemaAttribute", Format = "date", ReadOnly = true, WriteOnly = true, Nullable = false)] + public string StringWithSwaggerSchemaAttribute { get; set; } +} diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs index 5675d2f82b..245281c4f4 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs @@ -2,13 +2,12 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class VendorExtensionsOperationFilter : IOperationFilter { - public class VendorExtensionsOperationFilter : IOperationFilter + public void Apply(OpenApiOperation operation, OperationFilterContext context) { - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - operation.Extensions.Add("X-property1", new OpenApiString("value")); - } + operation.Extensions.Add("X-property1", new OpenApiString("value")); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs index 9ed2c62554..778e553035 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs @@ -2,13 +2,12 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Swashbuckle.AspNetCore.Annotations.Test +namespace Swashbuckle.AspNetCore.Annotations.Test; + +public class VendorExtensionsSchemaFilter : ISchemaFilter { - public class VendorExtensionsSchemaFilter : ISchemaFilter + public void Apply(OpenApiSchema schema, SchemaFilterContext context) { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - schema.Extensions.Add("X-property1", new OpenApiString("value")); - } + schema.Extensions.Add("X-property1", new OpenApiString("value")); } -} \ No newline at end of file +} diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs index db632a06fc..37a17b7f24 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs @@ -1,171 +1,170 @@ using Microsoft.OpenApi.Models; using Xunit; -namespace Swashbuckle.AspNetCore.ApiTesting.Test +namespace Swashbuckle.AspNetCore.ApiTesting.Test; + +public class ApiTestRunnerBaseTests { - public class ApiTestRunnerBaseTests + [Fact] + public async Task TestAsync_ThrowsException_IfDocumentNotFound() { - [Fact] - public async Task TestAsync_ThrowsException_IfDocumentNotFound() - { - var exception = await Assert.ThrowsAsync(() => Subject().TestAsync( - "v1", - "GetProducts", - "200", - new HttpRequestMessage(), - CreateHttpClient())); + var exception = await Assert.ThrowsAsync(() => Subject().TestAsync( + "v1", + "GetProducts", + "200", + new HttpRequestMessage(), + CreateHttpClient())); - Assert.Equal("Document with name 'v1' not found", exception.Message); - } + Assert.Equal("Document with name 'v1' not found", exception.Message); + } - [Fact] - public async Task TestAsync_ThrowsException_IfOperationNotFound() + [Fact] + public async Task TestAsync_ThrowsException_IfOperationNotFound() + { + var subject = new FakeApiTestRunner(); + subject.Configure(c => { - var subject = new FakeApiTestRunner(); - subject.Configure(c => - { - c.OpenApiDocs.Add("v1", new OpenApiDocument()); - }); - - var exception = await Assert.ThrowsAsync(() => subject.TestAsync( - "v1", - "GetProducts", - "200", - new HttpRequestMessage(), - CreateHttpClient())); + c.OpenApiDocs.Add("v1", new OpenApiDocument()); + }); + + var exception = await Assert.ThrowsAsync(() => subject.TestAsync( + "v1", + "GetProducts", + "200", + new HttpRequestMessage(), + CreateHttpClient())); - Assert.Equal("Operation with id 'GetProducts' not found in OpenAPI document 'v1'", exception.Message); - } + Assert.Equal("Operation with id 'GetProducts' not found in OpenAPI document 'v1'", exception.Message); + } - [Theory] - [InlineData("/api/products", "200", "Required parameter 'param' is not present")] - [InlineData("/api/products?param=foo", "200", null)] - public async Task TestAsync_ThrowsException_IfExpectedStatusCodeIs2xxAndRequestDoesNotMatchSpec( - string requestUri, - string statusCode, - string expectedExceptionMessage) + [Theory] + [InlineData("/api/products", "200", "Required parameter 'param' is not present")] + [InlineData("/api/products?param=foo", "200", null)] + public async Task TestAsync_ThrowsException_IfExpectedStatusCodeIs2xxAndRequestDoesNotMatchSpec( + string requestUri, + string statusCode, + string expectedExceptionMessage) + { + var subject = new FakeApiTestRunner(); + subject.Configure(c => { - var subject = new FakeApiTestRunner(); - subject.Configure(c => + c.OpenApiDocs.Add("v1", new OpenApiDocument { - c.OpenApiDocs.Add("v1", new OpenApiDocument + Paths = new OpenApiPaths { - Paths = new OpenApiPaths + ["/api/products"] = new OpenApiPathItem { - ["/api/products"] = new OpenApiPathItem + Operations = new Dictionary { - Operations = new Dictionary + [OperationType.Get] = new OpenApiOperation { - [OperationType.Get] = new OpenApiOperation + OperationId = "GetProducts", + Parameters = new List { - OperationId = "GetProducts", - Parameters = new List + new OpenApiParameter { - new OpenApiParameter - { - Name = "param", - Required = true, - In = ParameterLocation.Query - } - }, - Responses = new OpenApiResponses - { - [ "200" ] = new OpenApiResponse() + Name = "param", + Required = true, + In = ParameterLocation.Query } + }, + Responses = new OpenApiResponses + { + [ "200" ] = new OpenApiResponse() } } } } - }); + } }); + }); - var exception = await Record.ExceptionAsync(() => subject.TestAsync( - "v1", - "GetProducts", - statusCode, - new HttpRequestMessage - { - RequestUri = new Uri(requestUri, UriKind.Relative), - Method = HttpMethod.Get - }, - CreateHttpClient())); - - Assert.Equal(expectedExceptionMessage, exception?.Message); - } + var exception = await Record.ExceptionAsync(() => subject.TestAsync( + "v1", + "GetProducts", + statusCode, + new HttpRequestMessage + { + RequestUri = new Uri(requestUri, UriKind.Relative), + Method = HttpMethod.Get + }, + CreateHttpClient())); - [Theory] - [InlineData("/api/products", "400", "Status code '200' does not match expected value '400'")] - [InlineData("/api/products?param=foo", "200", null)] - public async Task TestAsync_ThrowsException_IfResponseDoesNotMatchSpec( - string requestUri, - string statusCode, - string expectedExceptionMessage) + Assert.Equal(expectedExceptionMessage, exception?.Message); + } + + [Theory] + [InlineData("/api/products", "400", "Status code '200' does not match expected value '400'")] + [InlineData("/api/products?param=foo", "200", null)] + public async Task TestAsync_ThrowsException_IfResponseDoesNotMatchSpec( + string requestUri, + string statusCode, + string expectedExceptionMessage) + { + var subject = new FakeApiTestRunner(); + subject.Configure(c => { - var subject = new FakeApiTestRunner(); - subject.Configure(c => + c.OpenApiDocs.Add("v1", new OpenApiDocument { - c.OpenApiDocs.Add("v1", new OpenApiDocument + Paths = new OpenApiPaths { - Paths = new OpenApiPaths + ["/api/products"] = new OpenApiPathItem { - ["/api/products"] = new OpenApiPathItem + Operations = new Dictionary { - Operations = new Dictionary + [OperationType.Get] = new OpenApiOperation { - [OperationType.Get] = new OpenApiOperation + OperationId = "GetProducts", + Responses = new OpenApiResponses { - OperationId = "GetProducts", - Responses = new OpenApiResponses - { - [ "400" ] = new OpenApiResponse(), - [ "200" ] = new OpenApiResponse() - } + [ "400" ] = new OpenApiResponse(), + [ "200" ] = new OpenApiResponse() } } } } - }); + } }); + }); - var exception = await Record.ExceptionAsync(() => subject.TestAsync( - "v1", - "GetProducts", - statusCode, - new HttpRequestMessage - { - RequestUri = new Uri(requestUri, UriKind.Relative), - Method = HttpMethod.Get - }, - CreateHttpClient())); - - Assert.Equal(expectedExceptionMessage, exception?.Message); - } + var exception = await Record.ExceptionAsync(() => subject.TestAsync( + "v1", + "GetProducts", + statusCode, + new HttpRequestMessage + { + RequestUri = new Uri(requestUri, UriKind.Relative), + Method = HttpMethod.Get + }, + CreateHttpClient())); - private FakeApiTestRunner Subject() - { - return new FakeApiTestRunner(); - } + Assert.Equal(expectedExceptionMessage, exception?.Message); + } - private HttpClient CreateHttpClient() - { - var client = new HttpClient(new FakeHttpMessageHandler()); - client.BaseAddress = new Uri("http://tempuri.org"); - return client; - } + private FakeApiTestRunner Subject() + { + return new FakeApiTestRunner(); } - internal class FakeApiTestRunner : ApiTestRunnerBase + private HttpClient CreateHttpClient() { - public FakeApiTestRunner() - { - } + var client = new HttpClient(new FakeHttpMessageHandler()); + client.BaseAddress = new Uri("http://tempuri.org"); + return client; } +} - internal class FakeHttpMessageHandler : HttpMessageHandler +internal class FakeApiTestRunner : ApiTestRunnerBase +{ + public FakeApiTestRunner() { - protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - return Task.FromResult(new HttpResponseMessage()); - } + } +} + +internal class FakeHttpMessageHandler : HttpMessageHandler +{ + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + return Task.FromResult(new HttpResponseMessage()); } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs index 2039f6b6b3..aa133d07da 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs @@ -4,654 +4,653 @@ using JsonSchemaType = string; -namespace Swashbuckle.AspNetCore.ApiTesting.Test +namespace Swashbuckle.AspNetCore.ApiTesting.Test; + +public class JsonValidatorTests { - public class JsonValidatorTests - { - public static TheoryData Validate_ReturnsError_IfInstanceNotOfExpectedTypeData => - new() - { - { JsonSchemaTypes.Null, "{}", false, "Path: . Instance is not of type 'null'" }, - { JsonSchemaTypes.Null, "null", true, null }, - { JsonSchemaTypes.Boolean, "'foobar'", false, "Path: . Instance is not of type 'boolean'" }, - { JsonSchemaTypes.Boolean, "true", true, null }, - { JsonSchemaTypes.Object, "'foobar'", false, "Path: . Instance is not of type 'object'" }, - { JsonSchemaTypes.Object, "{}", true, null }, - { JsonSchemaTypes.Array, "'foobar'", false, "Path: . Instance is not of type 'array'" }, - { JsonSchemaTypes.Array, "[]", true, null }, - { JsonSchemaTypes.Number, "'foobar'", false, "Path: . Instance is not of type 'number'" }, - { JsonSchemaTypes.Number, "1", true, null }, - { JsonSchemaTypes.String, "{}", false, "Path: . Instance is not of type 'string'" }, - { JsonSchemaTypes.String, "'foobar'", true, null }, - }; - - [Theory] - [MemberData(nameof(Validate_ReturnsError_IfInstanceNotOfExpectedTypeData))] - public void Validate_ReturnsError_IfInstanceNotOfExpectedType( - JsonSchemaType schemaType, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + public static TheoryData Validate_ReturnsError_IfInstanceNotOfExpectedTypeData => + new() { - var openApiSchema = new OpenApiSchema { Type = schemaType }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + { JsonSchemaTypes.Null, "{}", false, "Path: . Instance is not of type 'null'" }, + { JsonSchemaTypes.Null, "null", true, null }, + { JsonSchemaTypes.Boolean, "'foobar'", false, "Path: . Instance is not of type 'boolean'" }, + { JsonSchemaTypes.Boolean, "true", true, null }, + { JsonSchemaTypes.Object, "'foobar'", false, "Path: . Instance is not of type 'object'" }, + { JsonSchemaTypes.Object, "{}", true, null }, + { JsonSchemaTypes.Array, "'foobar'", false, "Path: . Instance is not of type 'array'" }, + { JsonSchemaTypes.Array, "[]", true, null }, + { JsonSchemaTypes.Number, "'foobar'", false, "Path: . Instance is not of type 'number'" }, + { JsonSchemaTypes.Number, "1", true, null }, + { JsonSchemaTypes.String, "{}", false, "Path: . Instance is not of type 'string'" }, + { JsonSchemaTypes.String, "'foobar'", true, null }, + }; + + [Theory] + [MemberData(nameof(Validate_ReturnsError_IfInstanceNotOfExpectedTypeData))] + public void Validate_ReturnsError_IfInstanceNotOfExpectedType( + JsonSchemaType schemaType, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { Type = schemaType }; + var instance = JToken.Parse(instanceText); - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(5.0, "9", false, "Path: . Number is not evenly divisible by multipleOf")] - [InlineData(5.0, "10", true, null)] - public void Validate_ReturnsError_IfNumberNotEvenlyDivisibleByMultipleOf( - decimal schemaMultipleOf, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) - { - var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, MultipleOf = schemaMultipleOf }; - var instance = JToken.Parse(instanceText); + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(10.0, "10.1", false, "Path: . Number is greater than maximum")] - [InlineData(10.0, "10.0", true, null)] - public void Validate_ReturnsError_IfNumberGreaterThanMaximum( - decimal schemaMaximum, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) - { - var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Maximum = schemaMaximum }; - var instance = JToken.Parse(instanceText); + [Theory] + [InlineData(5.0, "9", false, "Path: . Number is not evenly divisible by multipleOf")] + [InlineData(5.0, "10", true, null)] + public void Validate_ReturnsError_IfNumberNotEvenlyDivisibleByMultipleOf( + decimal schemaMultipleOf, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, MultipleOf = schemaMultipleOf }; + var instance = JToken.Parse(instanceText); - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(10.0, "10.0", false, "Path: . Number is greater than, or equal to, maximum")] - [InlineData(10.0, "9.9", true, null)] - public void Validate_ReturnsError_IfNumberGreaterThanOrEqualToMaximumAndExclusiveMaximumSet( - decimal schemaMaximum, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) - { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Number, - Maximum = schemaMaximum, - ExclusiveMaximum = true - }; - var instance = JToken.Parse(instanceText); + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + [Theory] + [InlineData(10.0, "10.1", false, "Path: . Number is greater than maximum")] + [InlineData(10.0, "10.0", true, null)] + public void Validate_ReturnsError_IfNumberGreaterThanMaximum( + decimal schemaMaximum, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Maximum = schemaMaximum }; + var instance = JToken.Parse(instanceText); - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(10.0, "9.9", false, "Path: . Number is less than minimum")] - [InlineData(10.0, "10.0", true, null)] - public void Validate_ReturnsError_IfNumberLessThanMinimum( - decimal schemaMinimum, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) - { - var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Minimum = schemaMinimum }; - var instance = JToken.Parse(instanceText); + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(10.0, "10.0", false, "Path: . Number is less than, or equal to, minimum")] - [InlineData(10.0, "10.1", true, null)] - public void Validate_ReturnsError_IfNumberLessThanOrEqualToMinimumAndExclusiveMinimumSet( - decimal schemaMinimum, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(10.0, "10.0", false, "Path: . Number is greater than, or equal to, maximum")] + [InlineData(10.0, "9.9", true, null)] + public void Validate_ReturnsError_IfNumberGreaterThanOrEqualToMaximumAndExclusiveMaximumSet( + decimal schemaMaximum, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Number, - Minimum = schemaMinimum, - ExclusiveMinimum = true - }; - var instance = JToken.Parse(instanceText); + Type = JsonSchemaTypes.Number, + Maximum = schemaMaximum, + ExclusiveMaximum = true + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + [Theory] + [InlineData(10.0, "9.9", false, "Path: . Number is less than minimum")] + [InlineData(10.0, "10.0", true, null)] + public void Validate_ReturnsError_IfNumberLessThanMinimum( + decimal schemaMinimum, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Minimum = schemaMinimum }; + var instance = JToken.Parse(instanceText); - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(5, "'123456'", false, "Path: . String length is greater than maxLength")] - [InlineData(5, "'12345'", true, null)] - public void Validate_ReturnsError_IfStringLengthGreaterThanMaxLength( - int schemaMaxLength, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) - { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.String, - MaxLength = schemaMaxLength - }; - var instance = JToken.Parse(instanceText); + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(5, "'1234'", false, "Path: . String length is less than minLength" )] - [InlineData(5, "'12345'", true, null)] - public void Validate_ReturnsError_IfStringLengthLessThanMinLength( - int schemaMinLength, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(10.0, "10.0", false, "Path: . Number is less than, or equal to, minimum")] + [InlineData(10.0, "10.1", true, null)] + public void Validate_ReturnsError_IfNumberLessThanOrEqualToMinimumAndExclusiveMinimumSet( + decimal schemaMinimum, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.String, - MinLength = schemaMinLength - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.Number, + Minimum = schemaMinimum, + ExclusiveMinimum = true + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("^[a-z]{3}$", "'aa1'", false, "Path: . String does not match pattern")] - [InlineData("^[a-z]{3}$", "'aaz'", true, null)] - public void Validate_ReturnsError_IfStringDoesNotMatchPattern( - string schemaPattern, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(5, "'123456'", false, "Path: . String length is greater than maxLength")] + [InlineData(5, "'12345'", true, null)] + public void Validate_ReturnsError_IfStringLengthGreaterThanMaxLength( + int schemaMaxLength, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.String, - Pattern = schemaPattern - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); - - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } + Type = JsonSchemaTypes.String, + MaxLength = schemaMaxLength + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - public static TheoryData Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchemaData => - new() - { - { JsonSchemaTypes.Boolean, "[ true, 'foo' ]", false, "Path: [1]. Instance is not of type 'boolean'" }, - { JsonSchemaTypes.Number, "[ 123, 'foo' ]", false, "Path: [1]. Instance is not of type 'number'" }, - { JsonSchemaTypes.Boolean, "[ true, false ]", true, null }, - }; - - [Theory] - [MemberData(nameof(Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchemaData))] - public void Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchema( - JsonSchemaType itemsSchemaType, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(5, "'1234'", false, "Path: . String length is less than minLength" )] + [InlineData(5, "'12345'", true, null)] + public void Validate_ReturnsError_IfStringLengthLessThanMinLength( + int schemaMinLength, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Array, - Items = new OpenApiSchema { Type = itemsSchemaType } - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.String, + MinLength = schemaMinLength + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(2, "[ 1, 2, 3 ]", false, "Path: . Array size is greater than maxItems")] - [InlineData(2, "[ 1, 2 ]", true, null)] - public void Validate_ReturnsError_IfArraySizeGreaterThanMaxItems( - int schemaMaxItems, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData("^[a-z]{3}$", "'aa1'", false, "Path: . String does not match pattern")] + [InlineData("^[a-z]{3}$", "'aaz'", true, null)] + public void Validate_ReturnsError_IfStringDoesNotMatchPattern( + string schemaPattern, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Array, - MaxItems = schemaMaxItems - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.String, + Pattern = schemaPattern + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(2, "[ 1 ]", false, "Path: . Array size is less than minItems")] - [InlineData(2, "[ 1, 2 ]", true, null)] - public void Validate_ReturnsError_IfArraySizeLessThanMinItems( - int schemaMinItems, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + public static TheoryData Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchemaData => + new() { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Array, - MinItems = schemaMinItems - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); - - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("[ 1, 1, 3 ]", false, "Path: . Array does not contain uniqueItems")] - [InlineData("[ 1, 2, 3 ]", true, null)] - public void Validate_ReturnsError_IfArrayDoesNotContainUniqueItemsAndUniqueItemsSet( - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + { JsonSchemaTypes.Boolean, "[ true, 'foo' ]", false, "Path: [1]. Instance is not of type 'boolean'" }, + { JsonSchemaTypes.Number, "[ 123, 'foo' ]", false, "Path: [1]. Instance is not of type 'number'" }, + { JsonSchemaTypes.Boolean, "[ true, false ]", true, null }, + }; + + [Theory] + [MemberData(nameof(Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchemaData))] + public void Validate_ReturnsError_IfArrayItemDoesNotMatchItemsSchema( + JsonSchemaType itemsSchemaType, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Array, - UniqueItems = true - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.Array, + Items = new OpenApiSchema { Type = itemsSchemaType } + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(1, "{ \"id\": 1, \"name\": \"foo\" }", false, "Path: . Number of properties is greater than maxProperties")] - [InlineData(1, "{ \"id\": 1 }", true, null)] - public void Validate_ReturnsError_IfNumberOfPropertiesGreaterThanMaxProperties( - int schemaMaxProperties, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(2, "[ 1, 2, 3 ]", false, "Path: . Array size is greater than maxItems")] + [InlineData(2, "[ 1, 2 ]", true, null)] + public void Validate_ReturnsError_IfArraySizeGreaterThanMaxItems( + int schemaMaxItems, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - MaxProperties = schemaMaxProperties - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.Array, + MaxItems = schemaMaxItems + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(2, "{ \"id\": 1 }", false, "Path: . Number of properties is less than minProperties")] - [InlineData(2, "{ \"id\": 1, \"name\": \"foo\" }", true, null)] - public void Validate_ReturnsError_IfNumberOfPropertiesLessThanMinProperties( - int schemaMinProperties, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(2, "[ 1 ]", false, "Path: . Array size is less than minItems")] + [InlineData(2, "[ 1, 2 ]", true, null)] + public void Validate_ReturnsError_IfArraySizeLessThanMinItems( + int schemaMinItems, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - MinProperties = schemaMinProperties - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + Type = JsonSchemaTypes.Array, + MinItems = schemaMinItems + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(new[] { "id", "name" }, "{ \"id\": 1 }", false, "Path: . Required property(s) not present")] - [InlineData(new[] { "id", "name" }, "{ \"id\": 1, \"name\": \"foo\" }", true, null)] - public void Validate_ReturnsError_IfRequiredPropertyNotPresent( - string[] schemaRequired, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData("[ 1, 1, 3 ]", false, "Path: . Array does not contain uniqueItems")] + [InlineData("[ 1, 2, 3 ]", true, null)] + public void Validate_ReturnsError_IfArrayDoesNotContainUniqueItemsAndUniqueItemsSet( + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - Required = new SortedSet(schemaRequired) - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); - - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } + Type = JsonSchemaTypes.Array, + UniqueItems = true + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - public static TheoryData Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchemaData => - new() - { - { JsonSchemaTypes.Number, "{ \"id\": \"foo\" }", false, "Path: id. Instance is not of type 'number'" }, - { JsonSchemaTypes.String, "{ \"id\": 123 }", false, "Path: id. Instance is not of type 'string'" }, - { JsonSchemaTypes.Number, "{ \"id\": 123 }", true, null }, - }; - - [Theory] - [MemberData(nameof(Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchemaData))] - public void Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchema( - JsonSchemaType propertySchemaType, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(1, "{ \"id\": 1, \"name\": \"foo\" }", false, "Path: . Number of properties is greater than maxProperties")] + [InlineData(1, "{ \"id\": 1 }", true, null)] + public void Validate_ReturnsError_IfNumberOfPropertiesGreaterThanMaxProperties( + int schemaMaxProperties, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - Properties = new Dictionary - { - [ "id" ] = new OpenApiSchema { Type = propertySchemaType } - } - }; - var instance = JToken.Parse(instanceText); - - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); - - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } + Type = JsonSchemaTypes.Object, + MaxProperties = schemaMaxProperties + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - public static TheoryData Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchemaData => - new() - { - { JsonSchemaTypes.Number, "{ \"id\": \"foo\" }", false, "Path: id. Instance is not of type 'number'" }, - { JsonSchemaTypes.String, "{ \"name\": 123 }", false, "Path: name. Instance is not of type 'string'" }, - { JsonSchemaTypes.Number, "{ \"description\": 123 }", true, null }, - }; - - [Theory] - [MemberData(nameof(Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchemaData))] - public void Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchema( - JsonSchemaType additionalPropertiesType, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(2, "{ \"id\": 1 }", false, "Path: . Number of properties is less than minProperties")] + [InlineData(2, "{ \"id\": 1, \"name\": \"foo\" }", true, null)] + public void Validate_ReturnsError_IfNumberOfPropertiesLessThanMinProperties( + int schemaMinProperties, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - AdditionalProperties = new OpenApiSchema { Type = additionalPropertiesType } - }; - var instance = JToken.Parse(instanceText); + Type = JsonSchemaTypes.Object, + MinProperties = schemaMinProperties + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + [Theory] + [InlineData(new[] { "id", "name" }, "{ \"id\": 1 }", false, "Path: . Required property(s) not present")] + [InlineData(new[] { "id", "name" }, "{ \"id\": 1, \"name\": \"foo\" }", true, null)] + public void Validate_ReturnsError_IfRequiredPropertyNotPresent( + string[] schemaRequired, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema + { + Type = JsonSchemaTypes.Object, + Required = new SortedSet(schemaRequired) + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData(false, "{ \"id\": \"foo\" }", false, "Path: . Additional properties not allowed")] - [InlineData(true, "{ \"id\": \"foo\" }", true, null)] - public void Validate_ReturnsError_IfAdditionalPropertiesPresentAndAdditionalPropertiesAllowedUnset( - bool additionalPropertiesAllowed, - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + public static TheoryData Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchemaData => + new() + { + { JsonSchemaTypes.Number, "{ \"id\": \"foo\" }", false, "Path: id. Instance is not of type 'number'" }, + { JsonSchemaTypes.String, "{ \"id\": 123 }", false, "Path: id. Instance is not of type 'string'" }, + { JsonSchemaTypes.Number, "{ \"id\": 123 }", true, null }, + }; + + [Theory] + [MemberData(nameof(Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchemaData))] + public void Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchema( + JsonSchemaType propertySchemaType, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema + Type = JsonSchemaTypes.Object, + Properties = new Dictionary { - Type = JsonSchemaTypes.Object, - AdditionalPropertiesAllowed = additionalPropertiesAllowed - }; - var instance = JToken.Parse(instanceText); + [ "id" ] = new OpenApiSchema { Type = propertySchemaType } + } + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + public static TheoryData Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchemaData => + new() + { + { JsonSchemaTypes.Number, "{ \"id\": \"foo\" }", false, "Path: id. Instance is not of type 'number'" }, + { JsonSchemaTypes.String, "{ \"name\": 123 }", false, "Path: name. Instance is not of type 'string'" }, + { JsonSchemaTypes.Number, "{ \"description\": 123 }", true, null }, + }; + + [Theory] + [MemberData(nameof(Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchemaData))] + public void Validate_ReturnsError_IfAdditionalPropertyDoesNotMatchAdditionalPropertiesSchema( + JsonSchemaType additionalPropertiesType, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema + { + Type = JsonSchemaTypes.Object, + AdditionalProperties = new OpenApiSchema { Type = additionalPropertiesType } + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("{ \"p1\": 1, \"p2\": 2 }", false, "Path: . Required property(s) not present (allOf[2])")] - [InlineData("{ \"p1\": 1, \"p2\": 2, \"p3\": 3 }", true, null)] - public void Validate_ReturnsError_IfInstanceDoesNotMatchAllSchemasSpecifiedByAllOf( - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData(false, "{ \"id\": \"foo\" }", false, "Path: . Additional properties not allowed")] + [InlineData(true, "{ \"id\": \"foo\" }", true, null)] + public void Validate_ReturnsError_IfAdditionalPropertiesPresentAndAdditionalPropertiesAllowedUnset( + bool additionalPropertiesAllowed, + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - AllOf = - [ - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } - ] - }; - var instance = JToken.Parse(instanceText); + Type = JsonSchemaTypes.Object, + AdditionalPropertiesAllowed = additionalPropertiesAllowed + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + [Theory] + [InlineData("{ \"p1\": 1, \"p2\": 2 }", false, "Path: . Required property(s) not present (allOf[2])")] + [InlineData("{ \"p1\": 1, \"p2\": 2, \"p3\": 3 }", true, null)] + public void Validate_ReturnsError_IfInstanceDoesNotMatchAllSchemasSpecifiedByAllOf( + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema + { + AllOf = + [ + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + ] + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("{}", false, "Path: . Required property(s) not present (anyOf[0])")] - [InlineData("{ \"p1\": 1 }", true, null)] - public void Validate_ReturnsError_IfInstanceDoesNotMatchAnySchemaSpecifiedByAnyOf( - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData("{}", false, "Path: . Required property(s) not present (anyOf[0])")] + [InlineData("{ \"p1\": 1 }", true, null)] + public void Validate_ReturnsError_IfInstanceDoesNotMatchAnySchemaSpecifiedByAnyOf( + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema - { - AnyOf = - [ - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } - ] - }; - var instance = JToken.Parse(instanceText); + AnyOf = + [ + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + ] + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - var returnValue = Subject().Validate( - openApiSchema, - new OpenApiDocument(), - instance, - out IEnumerable errorMessages); + [Theory] + [InlineData("{}", false, "Path: . Required property(s) not present (oneOf[0])")] + [InlineData("{ \"p1\": 1, \"p2\": 2 }", false, "Path: . Instance matches multiple schemas in oneOf array")] + [InlineData("{ \"p1\": 1 }", true, null)] + public void Validate_ReturnsError_IfInstanceDoesNotMatchExactlyOneSchemaSpecifiedByOneOf( + string instanceText, + bool expectedReturnValue, + string expectedErrorMessage) + { + var openApiSchema = new OpenApiSchema + { + OneOf = + [ + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + ] + }; + var instance = JToken.Parse(instanceText); + + var returnValue = Subject().Validate( + openApiSchema, + new OpenApiDocument(), + instance, + out IEnumerable errorMessages); + + Assert.Equal(expectedReturnValue, returnValue); + Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); + } - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("{}", false, "Path: . Required property(s) not present (oneOf[0])")] - [InlineData("{ \"p1\": 1, \"p2\": 2 }", false, "Path: . Instance matches multiple schemas in oneOf array")] - [InlineData("{ \"p1\": 1 }", true, null)] - public void Validate_ReturnsError_IfInstanceDoesNotMatchExactlyOneSchemaSpecifiedByOneOf( - string instanceText, - bool expectedReturnValue, - string expectedErrorMessage) + [Theory] + [InlineData("foo", "Invalid Reference identifier 'foo'.")] + [InlineData("ref", null)] + public void Validate_SupportsReferencedSchemas_IfDefinedInProvidedOpenApiDocument( + string referenceId, + string expectedExceptionMessage) + { + var openApiSchema = new OpenApiSchema { - var openApiSchema = new OpenApiSchema + Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = referenceId } + }; + var openApiDocument = new OpenApiDocument + { + Components = new OpenApiComponents { - OneOf = - [ - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } - ] - }; - var instance = JToken.Parse(instanceText); + Schemas = new Dictionary + { + ["ref"] = new OpenApiSchema { Type = JsonSchemaTypes.Number } + } + } + }; + var instance = JToken.Parse("1"); + var exception = Record.Exception(() => + { var returnValue = Subject().Validate( openApiSchema, - new OpenApiDocument(), + openApiDocument, instance, out IEnumerable errorMessages); - Assert.Equal(expectedReturnValue, returnValue); - Assert.Equal(expectedErrorMessage, errorMessages.FirstOrDefault()); - } - - [Theory] - [InlineData("foo", "Invalid Reference identifier 'foo'.")] - [InlineData("ref", null)] - public void Validate_SupportsReferencedSchemas_IfDefinedInProvidedOpenApiDocument( - string referenceId, - string expectedExceptionMessage) - { - var openApiSchema = new OpenApiSchema - { - Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = referenceId } - }; - var openApiDocument = new OpenApiDocument - { - Components = new OpenApiComponents - { - Schemas = new Dictionary - { - ["ref"] = new OpenApiSchema { Type = JsonSchemaTypes.Number } - } - } - }; - var instance = JToken.Parse("1"); - - var exception = Record.Exception(() => - { - var returnValue = Subject().Validate( - openApiSchema, - openApiDocument, - instance, - out IEnumerable errorMessages); - - Assert.True(returnValue); - }); - - Assert.Equal(expectedExceptionMessage, exception?.Message); - } + Assert.True(returnValue); + }); - private static JsonValidator Subject() => new(); + Assert.Equal(expectedExceptionMessage, exception?.Message); } + + private static JsonValidator Subject() => new(); } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs index 70a75b3f5b..e2adff58bb 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs @@ -4,395 +4,394 @@ using JsonSchemaType = string; -namespace Swashbuckle.AspNetCore.ApiTesting.Test +namespace Swashbuckle.AspNetCore.ApiTesting.Test; + +public class RequestValidatorTests { - public class RequestValidatorTests + [Theory] + [InlineData("/api/foobar", "/api/products", "Request URI '/api/foobar' does not match specified template '/api/products'")] + [InlineData("/api/products", "/api/products", null)] + public void Validate_ThrowsException_IfUriDoesNotMatchPathTemplate( + string uriString, + string pathTemplate, + string expectedErrorMessage) { - [Theory] - [InlineData("/api/foobar", "/api/products", "Request URI '/api/foobar' does not match specified template '/api/products'")] - [InlineData("/api/products", "/api/products", null)] - public void Validate_ThrowsException_IfUriDoesNotMatchPathTemplate( - string uriString, - string pathTemplate, - string expectedErrorMessage) + var openApiDocument = DocumentWithOperation(pathTemplate, OperationType.Get, new OpenApiOperation()); + var request = new HttpRequestMessage { - var openApiDocument = DocumentWithOperation(pathTemplate, OperationType.Get, new OpenApiOperation()); - var request = new HttpRequestMessage - { - RequestUri = new Uri(uriString, UriKind.Relative), - }; + RequestUri = new Uri(uriString, UriKind.Relative), + }; - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, pathTemplate, OperationType.Get); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - [Theory] - [InlineData("POST", OperationType.Get, "Request method 'POST' does not match specified operation type 'Get'")] - [InlineData("GET", OperationType.Get, null)] - public void Validate_ThrowsException_IfMethodDoesNotMatchOperationType( - string methodString, - OperationType operationType, - string expectedErrorMessage) + var exception = Record.Exception(() => { - var openApiDocument = DocumentWithOperation("/api/products", operationType, new OpenApiOperation()); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = new HttpMethod(methodString) - }; + Subject().Validate(request, openApiDocument, pathTemplate, OperationType.Get); + }); - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", operationType); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - [Theory] - [InlineData("/api/products", "Required parameter 'param' is not present")] - [InlineData("/api/products?param=foo", null)] - public void Validate_ThrowsException_IfRequiredQueryParameterIsNotPresent( - string uriString, - string expectedErrorMessage) + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + [Theory] + [InlineData("POST", OperationType.Get, "Request method 'POST' does not match specified operation type 'Get'")] + [InlineData("GET", OperationType.Get, null)] + public void Validate_ThrowsException_IfMethodDoesNotMatchOperationType( + string methodString, + OperationType operationType, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", operationType, new OpenApiOperation()); + var request = new HttpRequestMessage { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation - { - Parameters = new List - { - new OpenApiParameter - { - Name = "param", - In = ParameterLocation.Query, - Schema = new OpenApiSchema { Type = JsonSchemaTypes.String }, - Required = true - } - } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri(uriString, UriKind.Relative), - Method = HttpMethod.Get - }; + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = new HttpMethod(methodString) + }; - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - [Theory] - [InlineData(null, "Required parameter 'test-header' is not present")] - [InlineData("foo", null)] - public void Validate_ThrowsException_IfRequiredHeaderParameterIsNotPresent( - string parameterValue, - string expectedErrorMessage) + var exception = Record.Exception(() => + { + Subject().Validate(request, openApiDocument, "/api/products", operationType); + }); + + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + [Theory] + [InlineData("/api/products", "Required parameter 'param' is not present")] + [InlineData("/api/products?param=foo", null)] + public void Validate_ThrowsException_IfRequiredQueryParameterIsNotPresent( + string uriString, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + Parameters = new List { - Parameters = new List + new OpenApiParameter { - new OpenApiParameter - { - Name = "test-header", - In = ParameterLocation.Header, - Schema = new OpenApiSchema { Type = JsonSchemaTypes.String }, - Required = true - } + Name = "param", + In = ParameterLocation.Query, + Schema = new OpenApiSchema { Type = JsonSchemaTypes.String }, + Required = true } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = HttpMethod.Get, - }; - if (parameterValue != null) request.Headers.Add("test-header", parameterValue); + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri(uriString, UriKind.Relative), + Method = HttpMethod.Get + }; - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); - }); + var exception = Record.Exception(() => + { + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - public static TheoryData PathParameterTypeMismatchData => new() + [Theory] + [InlineData(null, "Required parameter 'test-header' is not present")] + [InlineData("foo", null)] + public void Validate_ThrowsException_IfRequiredHeaderParameterIsNotPresent( + string parameterValue, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + { + Parameters = new List + { + new OpenApiParameter + { + Name = "test-header", + In = ParameterLocation.Header, + Schema = new OpenApiSchema { Type = JsonSchemaTypes.String }, + Required = true + } + } + }); + var request = new HttpRequestMessage { - { "/api/products/foo", JsonSchemaTypes.Boolean, "Parameter 'param' is not of type 'boolean'" }, - { "/api/products/foo", JsonSchemaTypes.Number, "Parameter 'param' is not of type 'number'" }, - { "/api/products/true", JsonSchemaTypes.Boolean, null }, - { "/api/products/1", JsonSchemaTypes.Number, null }, - { "/api/products/foo", JsonSchemaTypes.String, null } + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = HttpMethod.Get, }; + if (parameterValue != null) request.Headers.Add("test-header", parameterValue); - [Theory] - [MemberData(nameof(PathParameterTypeMismatchData))] - public void Validate_ThrowsException_IfPathParameterIsNotOfSpecifiedType( - string uriString, - JsonSchemaType specifiedType, - string expectedErrorMessage) + var exception = Record.Exception(() => { - var openApiDocument = DocumentWithOperation("/api/products/{param}", OperationType.Get, new OpenApiOperation + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + }); + + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + public static TheoryData PathParameterTypeMismatchData => new() + { + { "/api/products/foo", JsonSchemaTypes.Boolean, "Parameter 'param' is not of type 'boolean'" }, + { "/api/products/foo", JsonSchemaTypes.Number, "Parameter 'param' is not of type 'number'" }, + { "/api/products/true", JsonSchemaTypes.Boolean, null }, + { "/api/products/1", JsonSchemaTypes.Number, null }, + { "/api/products/foo", JsonSchemaTypes.String, null } + }; + + [Theory] + [MemberData(nameof(PathParameterTypeMismatchData))] + public void Validate_ThrowsException_IfPathParameterIsNotOfSpecifiedType( + string uriString, + JsonSchemaType specifiedType, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products/{param}", OperationType.Get, new OpenApiOperation + { + Parameters = new List { - Parameters = new List + new OpenApiParameter { - new OpenApiParameter - { - Name = "param", - In = ParameterLocation.Path, - Schema = new OpenApiSchema { Type = specifiedType } - } + Name = "param", + In = ParameterLocation.Path, + Schema = new OpenApiSchema { Type = specifiedType } } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri(uriString, UriKind.Relative), - Method = HttpMethod.Get - }; + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri(uriString, UriKind.Relative), + Method = HttpMethod.Get + }; - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products/{param}", OperationType.Get); - }); + var exception = Record.Exception(() => + { + Subject().Validate(request, openApiDocument, "/api/products/{param}", OperationType.Get); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } #nullable enable - public static TheoryData QueryParameterTypeMismatchData => new() - { - { "/api/products?param=foo", JsonSchemaTypes.Boolean, null, "Parameter 'param' is not of type 'boolean'" }, - { "/api/products?param=foo", JsonSchemaTypes.Number, null, "Parameter 'param' is not of type 'number'" }, - { "/api/products?param=true", JsonSchemaTypes.Boolean, null, null }, - { "/api/products?param=1", JsonSchemaTypes.Number, null, null }, - { "/api/products?param=foo", JsonSchemaTypes.String, null, null }, - { "/api/products?param=1¶m=2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "/api/products?param=1¶m=foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'param' is not of type 'array[number]'" }, - }; - - [Theory] - [MemberData(nameof(QueryParameterTypeMismatchData))] - public void Validate_ThrowsException_IfQueryParameterIsNotOfSpecifiedType( - string path, - JsonSchemaType specifiedType, - JsonSchemaType? specifiedItemsType, - string? expectedErrorMessage) + public static TheoryData QueryParameterTypeMismatchData => new() + { + { "/api/products?param=foo", JsonSchemaTypes.Boolean, null, "Parameter 'param' is not of type 'boolean'" }, + { "/api/products?param=foo", JsonSchemaTypes.Number, null, "Parameter 'param' is not of type 'number'" }, + { "/api/products?param=true", JsonSchemaTypes.Boolean, null, null }, + { "/api/products?param=1", JsonSchemaTypes.Number, null, null }, + { "/api/products?param=foo", JsonSchemaTypes.String, null, null }, + { "/api/products?param=1¶m=2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, + { "/api/products?param=1¶m=foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'param' is not of type 'array[number]'" }, + }; + + [Theory] + [MemberData(nameof(QueryParameterTypeMismatchData))] + public void Validate_ThrowsException_IfQueryParameterIsNotOfSpecifiedType( + string path, + JsonSchemaType specifiedType, + JsonSchemaType? specifiedItemsType, + string? expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + Parameters = new List { - Parameters = new List + new OpenApiParameter { - new OpenApiParameter + Name = "param", + In = ParameterLocation.Query, + Schema = new OpenApiSchema { - Name = "param", - In = ParameterLocation.Query, - Schema = new OpenApiSchema - { - Type = specifiedType, - Items = specifiedItemsType != null ? new OpenApiSchema { Type = specifiedItemsType } : null - } + Type = specifiedType, + Items = specifiedItemsType != null ? new OpenApiSchema { Type = specifiedItemsType } : null } } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri(path, UriKind.Relative), - Method = HttpMethod.Get - }; - - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - public static TheoryData HeaderParameterTypeMismatchData => new() + } + }); + var request = new HttpRequestMessage { - { "foo", JsonSchemaTypes.Boolean, null, "Parameter 'test-header' is not of type 'boolean'" }, - { "foo", JsonSchemaTypes.Number, null, "Parameter 'test-header' is not of type 'number'" }, - { "true", JsonSchemaTypes.Boolean, null, null }, - { "1", JsonSchemaTypes.Number, null, null }, - { "foo", JsonSchemaTypes.String, null, null }, - { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'test-header' is not of type 'array[number]'" }, + RequestUri = new Uri(path, UriKind.Relative), + Method = HttpMethod.Get }; - [Theory] - [MemberData(nameof(HeaderParameterTypeMismatchData))] - public void Validate_ThrowsException_IfHeaderParameterIsNotOfSpecifiedType( - string parameterValue, - JsonSchemaType specifiedType, - JsonSchemaType? specifiedItemsType, - string? expectedErrorMessage) + var exception = Record.Exception(() => { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + }); + + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + public static TheoryData HeaderParameterTypeMismatchData => new() + { + { "foo", JsonSchemaTypes.Boolean, null, "Parameter 'test-header' is not of type 'boolean'" }, + { "foo", JsonSchemaTypes.Number, null, "Parameter 'test-header' is not of type 'number'" }, + { "true", JsonSchemaTypes.Boolean, null, null }, + { "1", JsonSchemaTypes.Number, null, null }, + { "foo", JsonSchemaTypes.String, null, null }, + { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, + { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'test-header' is not of type 'array[number]'" }, + }; + + [Theory] + [MemberData(nameof(HeaderParameterTypeMismatchData))] + public void Validate_ThrowsException_IfHeaderParameterIsNotOfSpecifiedType( + string parameterValue, + JsonSchemaType specifiedType, + JsonSchemaType? specifiedItemsType, + string? expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + { + Parameters = new List { - Parameters = new List + new OpenApiParameter { - new OpenApiParameter + Name = "test-header", + In = ParameterLocation.Header, + Schema = new OpenApiSchema { - Name = "test-header", - In = ParameterLocation.Header, - Schema = new OpenApiSchema - { - Type = specifiedType, - Items = (specifiedItemsType != null) ? new OpenApiSchema { Type = specifiedItemsType } : null - } + Type = specifiedType, + Items = (specifiedItemsType != null) ? new OpenApiSchema { Type = specifiedItemsType } : null } } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = HttpMethod.Get - }; - if (parameterValue != null) request.Headers.Add("test-header", parameterValue); + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = HttpMethod.Get + }; + if (parameterValue != null) request.Headers.Add("test-header", parameterValue); - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); - }); + var exception = Record.Exception(() => + { + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } #nullable restore - [Theory] - [InlineData(null, "Required content is not present")] - [InlineData("foo", null)] - public void Validate_ThrowsException_IfRequiredContentIsNotPresent( - string contentString, - string expectedErrorMessage) + [Theory] + [InlineData(null, "Required content is not present")] + [InlineData("foo", null)] + public void Validate_ThrowsException_IfRequiredContentIsNotPresent( + string contentString, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + RequestBody = new OpenApiRequestBody { - RequestBody = new OpenApiRequestBody + Required = true, + Content = new Dictionary { - Required = true, - Content = new Dictionary - { - [ "text/plain" ] = new OpenApiMediaType() - } + [ "text/plain" ] = new OpenApiMediaType() } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = HttpMethod.Post - }; - if (contentString != null) request.Content = new StringContent(contentString); + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = HttpMethod.Post + }; + if (contentString != null) request.Content = new StringContent(contentString); - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - [Theory] - [InlineData("application/foo", "Content media type 'application/foo' is not specified")] - [InlineData("application/json", null)] - public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( - string mediaType, - string expectedErrorMessage) + var exception = Record.Exception(() => + { + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); + }); + + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + [Theory] + [InlineData("application/foo", "Content media type 'application/foo' is not specified")] + [InlineData("application/json", null)] + public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( + string mediaType, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + RequestBody = new OpenApiRequestBody { - RequestBody = new OpenApiRequestBody + Content = new Dictionary { - Content = new Dictionary - { - [ "application/json" ] = new OpenApiMediaType() - } + [ "application/json" ] = new OpenApiMediaType() } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = HttpMethod.Post, - Content = new StringContent("{\"foo\":\"bar\"}", Encoding.UTF8, mediaType) - }; + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = HttpMethod.Post, + Content = new StringContent("{\"foo\":\"bar\"}", Encoding.UTF8, mediaType) + }; - var exception = Record.Exception(() => - { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); - }); - - Assert.Equal(expectedErrorMessage, exception?.Message); - } - - [Theory] - [InlineData("{\"prop1\":\"foo\"}", "Content does not match spec. Path: . Required property(s) not present")] - [InlineData("{\"prop1\":\"foo\",\"prop2\":\"bar\"}", null)] - public void Validate_DelegatesContentValidationToInjectedContentValidators( - string jsonString, - string expectedErrorMessage) + var exception = Record.Exception(() => { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); + }); + + Assert.Equal(expectedErrorMessage, exception?.Message); + } + + [Theory] + [InlineData("{\"prop1\":\"foo\"}", "Content does not match spec. Path: . Required property(s) not present")] + [InlineData("{\"prop1\":\"foo\",\"prop2\":\"bar\"}", null)] + public void Validate_DelegatesContentValidationToInjectedContentValidators( + string jsonString, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + { + RequestBody = new OpenApiRequestBody { - RequestBody = new OpenApiRequestBody + Content = new Dictionary { - Content = new Dictionary + [ "application/json" ] = new OpenApiMediaType { - [ "application/json" ] = new OpenApiMediaType + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - Required = new SortedSet { "prop1", "prop2" } - } + Type = JsonSchemaTypes.Object, + Required = new SortedSet { "prop1", "prop2" } } } } - }); - var request = new HttpRequestMessage - { - RequestUri = new Uri("/api/products", UriKind.Relative), - Method = HttpMethod.Post, - Content = new StringContent(jsonString, Encoding.UTF8, "application/json") - }; + } + }); + var request = new HttpRequestMessage + { + RequestUri = new Uri("/api/products", UriKind.Relative), + Method = HttpMethod.Post, + Content = new StringContent(jsonString, Encoding.UTF8, "application/json") + }; - var exception = Record.Exception(() => - { - Subject([new JsonContentValidator()]).Validate(request, openApiDocument, "/api/products", OperationType.Post); - }); + var exception = Record.Exception(() => + { + Subject([new JsonContentValidator()]).Validate(request, openApiDocument, "/api/products", OperationType.Post); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + { + return new OpenApiDocument { - return new OpenApiDocument + Paths = new OpenApiPaths { - Paths = new OpenApiPaths + [pathTemplate] = new OpenApiPathItem { - [pathTemplate] = new OpenApiPathItem + Operations = new Dictionary { - Operations = new Dictionary - { - [operationType] = operationSpec - } + [operationType] = operationSpec } - }, - Components = new OpenApiComponents - { - Schemas = new Dictionary() } - }; - } + }, + Components = new OpenApiComponents + { + Schemas = new Dictionary() + } + }; + } - private static RequestValidator Subject(IEnumerable contentValidators = null) - { - return new RequestValidator(contentValidators ?? []); - } + private static RequestValidator Subject(IEnumerable contentValidators = null) + { + return new RequestValidator(contentValidators ?? []); } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs index 76e8f47a2b..86ef205636 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs @@ -5,261 +5,260 @@ using JsonSchemaType = string; -namespace Swashbuckle.AspNetCore.ApiTesting.Test +namespace Swashbuckle.AspNetCore.ApiTesting.Test; + +public class ResponseValidatorTests { - public class ResponseValidatorTests + [Theory] + [InlineData(HttpStatusCode.InternalServerError, "Status code '500' does not match expected value '200'")] + [InlineData(HttpStatusCode.OK, null)] + public void Validate_ThrowsException_IfStatusCodeDifferentToExpectedResponseCode( + HttpStatusCode statusCode, + string expectedErrorMessage) { - [Theory] - [InlineData(HttpStatusCode.InternalServerError, "Status code '500' does not match expected value '200'")] - [InlineData(HttpStatusCode.OK, null)] - public void Validate_ThrowsException_IfStatusCodeDifferentToExpectedResponseCode( - HttpStatusCode statusCode, - string expectedErrorMessage) + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation - { - Responses = new OpenApiResponses - { - [ "200" ] = new OpenApiResponse(), - [ "500" ] = new OpenApiResponse() - } - }); - var response = new HttpResponseMessage + Responses = new OpenApiResponses { - StatusCode = statusCode - }; + [ "200" ] = new OpenApiResponse(), + [ "500" ] = new OpenApiResponse() + } + }); + var response = new HttpResponseMessage + { + StatusCode = statusCode + }; - var exception = Record.Exception(() => - { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Get, "200"); - }); + var exception = Record.Exception(() => + { + Subject().Validate(response, openApiDocument, "/api/products", OperationType.Get, "200"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - [Theory] - [InlineData(null, "Required header 'test-header' is not present")] - [InlineData("foo", null)] - public void Validate_ThrowsException_IfRequiredHeaderIsNotPresent( - string headerValue, - string expectedErrorMessage) + [Theory] + [InlineData(null, "Required header 'test-header' is not present")] + [InlineData("foo", null)] + public void Validate_ThrowsException_IfRequiredHeaderIsNotPresent( + string headerValue, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses + [ "201" ] = new OpenApiResponse { - [ "201" ] = new OpenApiResponse + Headers = new Dictionary { - Headers = new Dictionary + [ "test-header" ] = new OpenApiHeader { - [ "test-header" ] = new OpenApiHeader - { - Required = true - } + Required = true } } } - }); - var response = new HttpResponseMessage - { - StatusCode = HttpStatusCode.Created - }; - if (headerValue != null) response.Headers.Add("test-header", headerValue); + } + }); + var response = new HttpResponseMessage + { + StatusCode = HttpStatusCode.Created + }; + if (headerValue != null) response.Headers.Add("test-header", headerValue); - var exception = Record.Exception(() => - { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); - }); + var exception = Record.Exception(() => + { + Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } #nullable enable - public static TheoryData HeaderTypeValidationData => new() - { - { "foo", JsonSchemaTypes.Boolean, null, "Header 'test-header' is not of type 'boolean'" }, - { "foo", JsonSchemaTypes.Number, null, "Header 'test-header' is not of type 'number'" }, - { "true", JsonSchemaTypes.Boolean, null, null }, - { "1", JsonSchemaTypes.Number, null, null }, - { "foo", JsonSchemaTypes.String, null, null }, - { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Header 'test-header' is not of type 'array[number]'" }, - }; + public static TheoryData HeaderTypeValidationData => new() + { + { "foo", JsonSchemaTypes.Boolean, null, "Header 'test-header' is not of type 'boolean'" }, + { "foo", JsonSchemaTypes.Number, null, "Header 'test-header' is not of type 'number'" }, + { "true", JsonSchemaTypes.Boolean, null, null }, + { "1", JsonSchemaTypes.Number, null, null }, + { "foo", JsonSchemaTypes.String, null, null }, + { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, + { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Header 'test-header' is not of type 'array[number]'" }, + }; - [Theory] - [MemberData(nameof(HeaderTypeValidationData))] - public void Validate_ThrowsException_IfHeaderIsNotOfSpecifiedType( - string headerValue, - JsonSchemaType specifiedType, - JsonSchemaType? specifiedItemsType, - string? expectedErrorMessage) + [Theory] + [MemberData(nameof(HeaderTypeValidationData))] + public void Validate_ThrowsException_IfHeaderIsNotOfSpecifiedType( + string headerValue, + JsonSchemaType specifiedType, + JsonSchemaType? specifiedItemsType, + string? expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses + [ "201" ] = new OpenApiResponse { - [ "201" ] = new OpenApiResponse + Headers = new Dictionary { - Headers = new Dictionary + [ "test-header" ] = new OpenApiHeader { - [ "test-header" ] = new OpenApiHeader + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = specifiedType, - Items = specifiedItemsType != null ? new OpenApiSchema { Type = specifiedItemsType } : null - } + Type = specifiedType, + Items = specifiedItemsType != null ? new OpenApiSchema { Type = specifiedItemsType } : null } } } } - }); - var response = new HttpResponseMessage - { - StatusCode = HttpStatusCode.Created - }; - response.Headers.Add("test-header", headerValue); + } + }); + var response = new HttpResponseMessage + { + StatusCode = HttpStatusCode.Created + }; + response.Headers.Add("test-header", headerValue); - var exception = Record.Exception(() => - { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); - }); + var exception = Record.Exception(() => + { + Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } #nullable restore - [Theory] - [InlineData(null, "Expected content is not present")] - [InlineData("foo", null)] - public void Validate_ThrowsException_IfExpectedContentIsNotPresent( - string contentString, - string expectedErrorMessage) + [Theory] + [InlineData(null, "Expected content is not present")] + [InlineData("foo", null)] + public void Validate_ThrowsException_IfExpectedContentIsNotPresent( + string contentString, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses + [ "200" ] = new OpenApiResponse { - [ "200" ] = new OpenApiResponse + Content = new Dictionary { - Content = new Dictionary - { - ["text/plain"] = new OpenApiMediaType() - } + ["text/plain"] = new OpenApiMediaType() } } - }); - var response = new HttpResponseMessage - { - }; - if (contentString != null) response.Content = new StringContent(contentString); + } + }); + var response = new HttpResponseMessage + { + }; + if (contentString != null) response.Content = new StringContent(contentString); - var exception = Record.Exception(() => - { - Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); - }); + var exception = Record.Exception(() => + { + Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - [Theory] - [InlineData("application/foo", "Content media type 'application/foo' is not specified")] - [InlineData("application/json", null)] - public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( - string mediaType, - string expectedErrorMessage) + [Theory] + [InlineData("application/foo", "Content media type 'application/foo' is not specified")] + [InlineData("application/json", null)] + public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( + string mediaType, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses + [ "200" ] = new OpenApiResponse { - [ "200" ] = new OpenApiResponse + Content = new Dictionary { - Content = new Dictionary - { - ["application/json"] = new OpenApiMediaType() - } + ["application/json"] = new OpenApiMediaType() } } - }); - var response = new HttpResponseMessage - { - Content = new StringContent("{\"foo\":\"bar\"}", Encoding.UTF8, mediaType) - }; + } + }); + var response = new HttpResponseMessage + { + Content = new StringContent("{\"foo\":\"bar\"}", Encoding.UTF8, mediaType) + }; - var exception = Record.Exception(() => - { - Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); - }); + var exception = Record.Exception(() => + { + Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - [Theory] - [InlineData("{\"prop1\":\"foo\"}", "Content does not match spec. Path: . Required property(s) not present")] - [InlineData("{\"prop1\":\"foo\",\"prop2\":\"bar\"}", null)] - public void Validate_DelegatesContentValidationToInjectedContentValidators( - string jsonString, - string expectedErrorMessage) + [Theory] + [InlineData("{\"prop1\":\"foo\"}", "Content does not match spec. Path: . Required property(s) not present")] + [InlineData("{\"prop1\":\"foo\",\"prop2\":\"bar\"}", null)] + public void Validate_DelegatesContentValidationToInjectedContentValidators( + string jsonString, + string expectedErrorMessage) + { + var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + Responses = new OpenApiResponses { - Responses = new OpenApiResponses + [ "200" ] = new OpenApiResponse { - [ "200" ] = new OpenApiResponse + Content = new Dictionary { - Content = new Dictionary + ["application/json"] = new OpenApiMediaType { - ["application/json"] = new OpenApiMediaType + Schema = new OpenApiSchema { - Schema = new OpenApiSchema - { - Type = JsonSchemaTypes.Object, - Required = new SortedSet { "prop1", "prop2" } - } + Type = JsonSchemaTypes.Object, + Required = new SortedSet { "prop1", "prop2" } } } } } - }); - var response = new HttpResponseMessage - { - Content = new StringContent(jsonString, Encoding.UTF8, "application/json") - }; + } + }); + var response = new HttpResponseMessage + { + Content = new StringContent(jsonString, Encoding.UTF8, "application/json") + }; - var exception = Record.Exception(() => - { - Subject([new JsonContentValidator()]).Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); - }); + var exception = Record.Exception(() => + { + Subject([new JsonContentValidator()]).Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + }); - Assert.Equal(expectedErrorMessage, exception?.Message); - } + Assert.Equal(expectedErrorMessage, exception?.Message); + } - private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + { + return new OpenApiDocument { - return new OpenApiDocument + Paths = new OpenApiPaths { - Paths = new OpenApiPaths + [pathTemplate] = new OpenApiPathItem { - [pathTemplate] = new OpenApiPathItem + Operations = new Dictionary { - Operations = new Dictionary - { - [operationType] = operationSpec - } + [operationType] = operationSpec } - }, - Components = new OpenApiComponents - { - Schemas = new Dictionary() } - }; - } + }, + Components = new OpenApiComponents + { + Schemas = new Dictionary() + } + }; + } - private static ResponseValidator Subject(IEnumerable contentValidators = null) - { - return new ResponseValidator(contentValidators ?? []); - } + private static ResponseValidator Subject(IEnumerable contentValidators = null) + { + return new ResponseValidator(contentValidators ?? []); } } diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/CommandRunnerTests.cs b/test/Swashbuckle.AspNetCore.Cli.Test/CommandRunnerTests.cs index 2190c078be..774223f9b4 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/CommandRunnerTests.cs +++ b/test/Swashbuckle.AspNetCore.Cli.Test/CommandRunnerTests.cs @@ -1,108 +1,107 @@ using Xunit; -namespace Swashbuckle.AspNetCore.Cli.Test +namespace Swashbuckle.AspNetCore.Cli.Test; + +public static class CommandRunnerTests { - public static class CommandRunnerTests + [Fact] + public static void Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredMetadata() { - [Fact] - public static void Run_ParsesArgumentsAndExecutesCommands_AccordingToConfiguredMetadata() - { - var receivedValues = new List(); - var subject = new CommandRunner("test", "a test", new StringWriter()); - subject.SubCommand("cmd1", "", c => { - c.Option("--opt1", ""); - c.Option("--opt2", "", true); - c.Argument("arg1", ""); - c.OnRun((namedArgs) => - { - receivedValues.Add(namedArgs["--opt1"]); - receivedValues.Add(namedArgs["--opt2"]); - receivedValues.Add(namedArgs["arg1"]); - return 2; - }); + var receivedValues = new List(); + var subject = new CommandRunner("test", "a test", new StringWriter()); + subject.SubCommand("cmd1", "", c => { + c.Option("--opt1", ""); + c.Option("--opt2", "", true); + c.Argument("arg1", ""); + c.OnRun((namedArgs) => + { + receivedValues.Add(namedArgs["--opt1"]); + receivedValues.Add(namedArgs["--opt2"]); + receivedValues.Add(namedArgs["arg1"]); + return 2; }); - subject.SubCommand("cmd2", "", c => { - c.Option("--opt1", ""); - c.Option("--opt2", "", true); - c.Argument("arg1", ""); - c.OnRun((namedArgs) => - { - receivedValues.Add(namedArgs["--opt1"]); - receivedValues.Add(namedArgs["--opt2"]); - receivedValues.Add(namedArgs["arg1"]); - return 3; - }); + }); + subject.SubCommand("cmd2", "", c => { + c.Option("--opt1", ""); + c.Option("--opt2", "", true); + c.Argument("arg1", ""); + c.OnRun((namedArgs) => + { + receivedValues.Add(namedArgs["--opt1"]); + receivedValues.Add(namedArgs["--opt2"]); + receivedValues.Add(namedArgs["arg1"]); + return 3; }); + }); - var cmd1ExitCode = subject.Run(["cmd1", "--opt1", "foo", "--opt2", "bar"]); - var cmd2ExitCode = subject.Run(["cmd2", "--opt1", "blah", "--opt2", "dblah"]); + var cmd1ExitCode = subject.Run(["cmd1", "--opt1", "foo", "--opt2", "bar"]); + var cmd2ExitCode = subject.Run(["cmd2", "--opt1", "blah", "--opt2", "dblah"]); - Assert.Equal(2, cmd1ExitCode); - Assert.Equal(3, cmd2ExitCode); - Assert.Equal(["foo", null, "bar", "blah", null, "dblah"], [.. receivedValues]); - } + Assert.Equal(2, cmd1ExitCode); + Assert.Equal(3, cmd2ExitCode); + Assert.Equal(["foo", null, "bar", "blah", null, "dblah"], [.. receivedValues]); + } - [Fact] - public static void Run_PrintsAvailableCommands_WhenUnexpectedCommandIsProvided() - { - var output = new StringWriter(); - var subject = new CommandRunner("test", "a test", output); - subject.SubCommand("cmd", "does something", c => { - }); + [Fact] + public static void Run_PrintsAvailableCommands_WhenUnexpectedCommandIsProvided() + { + var output = new StringWriter(); + var subject = new CommandRunner("test", "a test", output); + subject.SubCommand("cmd", "does something", c => { + }); - var exitCode = subject.Run(["foo"]); + var exitCode = subject.Run(["foo"]); - Assert.StartsWith("a test", output.ToString()); - Assert.Contains("Commands:", output.ToString()); - Assert.Contains("cmd: does something", output.ToString()); - } + Assert.StartsWith("a test", output.ToString()); + Assert.Contains("Commands:", output.ToString()); + Assert.Contains("cmd: does something", output.ToString()); + } - [Fact] - public static void Run_PrintsAvailableCommands_WhenHelpOptionIsProvided() - { - var output = new StringWriter(); - var subject = new CommandRunner("test", "a test", output); - subject.SubCommand("cmd", "does something", c => { - }); + [Fact] + public static void Run_PrintsAvailableCommands_WhenHelpOptionIsProvided() + { + var output = new StringWriter(); + var subject = new CommandRunner("test", "a test", output); + subject.SubCommand("cmd", "does something", c => { + }); - var exitCode = subject.Run(["--help"]); + var exitCode = subject.Run(["--help"]); - Assert.StartsWith("a test", output.ToString()); - Assert.Contains("Commands:", output.ToString()); - Assert.Contains("cmd: does something", output.ToString()); - } + Assert.StartsWith("a test", output.ToString()); + Assert.Contains("Commands:", output.ToString()); + Assert.Contains("cmd: does something", output.ToString()); + } - [Theory] - [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt2", "foo" }, true)] - [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1" }, true)] - [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1", "--opt2" }, true)] - [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1", "foo" }, false)] - [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd" }, true)] - [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "--opt1" }, true)] - [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo", "bar" }, true)] - [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo" }, false)] - public static void Run_PrintsCommandUsage_WhenUnexpectedArgumentsAreProvided( - string[] optionNames, - string[] argNames, - string[] providedArgs, - bool shouldPrintUsage) + [Theory] + [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt2", "foo" }, true)] + [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1" }, true)] + [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1", "--opt2" }, true)] + [InlineData(new[] { "--opt1" }, new string[0], new[] { "cmd", "--opt1", "foo" }, false)] + [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd" }, true)] + [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "--opt1" }, true)] + [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo", "bar" }, true)] + [InlineData(new string[0], new[] { "arg1" }, new[] { "cmd", "foo" }, false)] + public static void Run_PrintsCommandUsage_WhenUnexpectedArgumentsAreProvided( + string[] optionNames, + string[] argNames, + string[] providedArgs, + bool shouldPrintUsage) + { + var output = new StringWriter(); + var subject = new CommandRunner("test", "a test", output); + subject.SubCommand("cmd", "a command", c => { - var output = new StringWriter(); - var subject = new CommandRunner("test", "a test", output); - subject.SubCommand("cmd", "a command", c => - { - foreach (var name in optionNames) - c.Option(name, ""); - foreach (var name in argNames) - c.Argument(name, ""); - }); + foreach (var name in optionNames) + c.Option(name, ""); + foreach (var name in argNames) + c.Argument(name, ""); + }); - subject.Run(providedArgs); + subject.Run(providedArgs); - if (shouldPrintUsage) - Assert.StartsWith("Usage: test cmd", output.ToString()); - else - Assert.Empty(output.ToString()); - } + if (shouldPrintUsage) + Assert.StartsWith("Usage: test cmd", output.ToString()); + else + Assert.Empty(output.ToString()); } } diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs index f26c4920b8..bf9fe4e494 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs +++ b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs @@ -3,288 +3,287 @@ using Swashbuckle.AspNetCore.TestSupport.Utilities; using Xunit; -namespace Swashbuckle.AspNetCore.Cli.Test +namespace Swashbuckle.AspNetCore.Cli.Test; + +public static class ToolTests { - public static class ToolTests + [Fact] + public static void Can_Output_Swagger_Document_Names() { - [Fact] - public static void Can_Output_Swagger_Document_Names() - { - var result = RunToStringCommand((outputPath) => - [ - "list", - "--output", - outputPath, - Path.Combine(Directory.GetCurrentDirectory(), "MultipleVersions.dll") - ], nameof(Can_Output_Swagger_Document_Names)); - var expected = $"\"1.0\"{Environment.NewLine}\"2.0\"{Environment.NewLine}"; - Assert.Equal(expected, result); - } - - [Fact] - public static void Throws_When_Startup_Assembly_Does_Not_Exist() - { - string[] args = ["tofile", "--output", "swagger.json", "--openapiversion", "2.0", "./does_not_exist.dll", "v1"]; - Assert.Throws(() => Program.Main(args)); - } - - [Theory] - [InlineData("a")] - [InlineData("1.9")] - [InlineData("3.2")] - public static void Error_When_OpenApiVersion_Is_Not_Supported(string version) - { - string[] args = - [ - "tofile", - "--output", - "swagger.json", - "--openapiversion", - version, - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]; - Assert.NotEqual(0, Program.Main(args)); - } - - [Fact] - public static void Can_Generate_Swagger_Json_v2_OpenApiVersion() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - "--openapiversion", - "2.0", - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]); - - Assert.Equal("2.0", document.RootElement.GetProperty("swagger").GetString()); - - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } - - [Fact] - public static void Can_Generate_Swagger_Json_v2_SerializeAsV2() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - "--serializeasv2", - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]); - - Assert.Equal("2.0", document.RootElement.GetProperty("swagger").GetString()); - - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } + var result = RunToStringCommand((outputPath) => + [ + "list", + "--output", + outputPath, + Path.Combine(Directory.GetCurrentDirectory(), "MultipleVersions.dll") + ], nameof(Can_Output_Swagger_Document_Names)); + var expected = $"\"1.0\"{Environment.NewLine}\"2.0\"{Environment.NewLine}"; + Assert.Equal(expected, result); + } - [Fact] - public static void Can_Generate_Swagger_Json_v3() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]); + [Fact] + public static void Throws_When_Startup_Assembly_Does_Not_Exist() + { + string[] args = ["tofile", "--output", "swagger.json", "--openapiversion", "2.0", "./does_not_exist.dll", "v1"]; + Assert.Throws(() => Program.Main(args)); + } - Assert.StartsWith("3.0.", document.RootElement.GetProperty("openapi").GetString()); + [Theory] + [InlineData("a")] + [InlineData("1.9")] + [InlineData("3.2")] + public static void Error_When_OpenApiVersion_Is_Not_Supported(string version) + { + string[] args = + [ + "tofile", + "--output", + "swagger.json", + "--openapiversion", + version, + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]; + Assert.NotEqual(0, Program.Main(args)); + } - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } + [Fact] + public static void Can_Generate_Swagger_Json_v2_OpenApiVersion() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "2.0", + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]); + + Assert.Equal("2.0", document.RootElement.GetProperty("swagger").GetString()); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - [Fact] - public static void Can_Generate_Swagger_Json_v3_OpenApiVersion() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - "--openapiversion", - "3.0", - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]); + [Fact] + public static void Can_Generate_Swagger_Json_v2_SerializeAsV2() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--serializeasv2", + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]); + + Assert.Equal("2.0", document.RootElement.GetProperty("swagger").GetString()); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - Assert.StartsWith("3.0.", document.RootElement.GetProperty("openapi").GetString()); + [Fact] + public static void Can_Generate_Swagger_Json_v3() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]); + + Assert.StartsWith("3.0.", document.RootElement.GetProperty("openapi").GetString()); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } + [Fact] + public static void Can_Generate_Swagger_Json_v3_OpenApiVersion() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "3.0", + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]); + + Assert.StartsWith("3.0.", document.RootElement.GetProperty("openapi").GetString()); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - [Fact] - public static void Overwrites_Existing_File() + [Fact] + public static void Overwrites_Existing_File() + { + using var document = RunToJsonCommand((outputPath) => { - using var document = RunToJsonCommand((outputPath) => - { - File.WriteAllText(outputPath, new string('x', 100_000)); - - return - [ - "tofile", - "--output", - outputPath, - "--openapiversion", - "2.0", - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ]; - }); - - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } + File.WriteAllText(outputPath, new string('x', 100_000)); - [Fact] - public static void CustomDocumentSerializer_Writes_Custom_V2_Document() - { - using var document = RunToJsonCommand((outputPath) => + return [ "tofile", "--output", outputPath, "--openapiversion", "2.0", - Path.Combine(Directory.GetCurrentDirectory(), "CustomDocumentSerializer.dll"), - "v1" - ]); - - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); - } - - [Fact] - public static void CustomDocumentSerializer_Writes_Custom_V3_Document() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - Path.Combine(Directory.GetCurrentDirectory(), - "CustomDocumentSerializer.dll"), + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), "v1" - ]); + ]; + }); - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); - } + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - [Fact] - public static void Can_Generate_Swagger_Json_ForTopLevelApp() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - "--openapiversion", - "2.0", - Path.Combine(Directory.GetCurrentDirectory(), "MinimalApp.dll"), - "v1" - ]); + [Fact] + public static void CustomDocumentSerializer_Writes_Custom_V2_Document() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "2.0", + Path.Combine(Directory.GetCurrentDirectory(), "CustomDocumentSerializer.dll"), + "v1" + ]); + + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); + } - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var path = paths.GetProperty("/WeatherForecast"); - Assert.True(path.TryGetProperty("get", out _)); - } + [Fact] + public static void CustomDocumentSerializer_Writes_Custom_V3_Document() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + Path.Combine(Directory.GetCurrentDirectory(), + "CustomDocumentSerializer.dll"), + "v1" + ]); + + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); + } - [Fact] - public static void Does_Not_Run_Crashing_HostedService() - { - using var document = RunToJsonCommand((outputPath) => - [ - "tofile", - "--output", - outputPath, - Path.Combine(Directory.GetCurrentDirectory(), "MinimalAppWithHostedServices.dll"), - "v1" - ]); + [Fact] + public static void Can_Generate_Swagger_Json_ForTopLevelApp() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "2.0", + Path.Combine(Directory.GetCurrentDirectory(), "MinimalApp.dll"), + "v1" + ]); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var path = paths.GetProperty("/WeatherForecast"); + Assert.True(path.TryGetProperty("get", out _)); + } - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var path = paths.GetProperty("/ShouldContain"); - Assert.True(path.TryGetProperty("get", out _)); - } + [Fact] + public static void Does_Not_Run_Crashing_HostedService() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + Path.Combine(Directory.GetCurrentDirectory(), "MinimalAppWithHostedServices.dll"), + "v1" + ]); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var path = paths.GetProperty("/ShouldContain"); + Assert.True(path.TryGetProperty("get", out _)); + } - [Fact] - public static void Creates_New_Folder_Path() - { - using var document = RunToJsonCommand(outputPath => - [ - "tofile", - "--output", - outputPath, - "--openapiversion", - "2.0", - Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), - "v1" - ], GenerateRandomString(5)); + [Fact] + public static void Creates_New_Folder_Path() + { + using var document = RunToJsonCommand(outputPath => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "2.0", + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ], GenerateRandomString(5)); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } - // verify one of the endpoints - var paths = document.RootElement.GetProperty("paths"); - var productsPath = paths.GetProperty("/products"); - Assert.True(productsPath.TryGetProperty("post", out _)); - } + private static string RunToStringCommand(Func setup, string subOutputPath = default) + { + using var temporaryDirectory = new TemporaryDirectory(); - private static string RunToStringCommand(Func setup, string subOutputPath = default) - { - using var temporaryDirectory = new TemporaryDirectory(); + var outputPath = !string.IsNullOrEmpty(subOutputPath) + ? Path.Combine(temporaryDirectory.Path, subOutputPath, "swagger.json") + : Path.Combine(temporaryDirectory.Path, "swagger.json"); - var outputPath = !string.IsNullOrEmpty(subOutputPath) - ? Path.Combine(temporaryDirectory.Path, subOutputPath, "swagger.json") - : Path.Combine(temporaryDirectory.Path, "swagger.json"); + string[] args = setup(outputPath); - string[] args = setup(outputPath); + Assert.Equal(0, Program.Main(args)); - Assert.Equal(0, Program.Main(args)); + return File.ReadAllText(outputPath); + } - return File.ReadAllText(outputPath); - } + private static JsonDocument RunToJsonCommand(Func setup, string subOutputPath = default) + { + string json = RunToStringCommand(setup, subOutputPath); + return JsonDocument.Parse(json); + } - private static JsonDocument RunToJsonCommand(Func setup, string subOutputPath = default) - { - string json = RunToStringCommand(setup, subOutputPath); - return JsonDocument.Parse(json); - } + private static string GenerateRandomString(int length) + { + const string Choices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var result = new StringBuilder(length); - private static string GenerateRandomString(int length) + for (int i = 0; i < length; i++) { - const string Choices = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - var result = new StringBuilder(length); - - for (int i = 0; i < length; i++) - { - result.Append(Choices[Random.Shared.Next(Choices.Length)]); - } - - return result.ToString(); + result.Append(Choices[Random.Shared.Next(Choices.Length)]); } + return result.ToString(); } + } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs index 9dfbbc5c7d..05b3b43e2a 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs @@ -5,108 +5,107 @@ using Microsoft.Extensions.Options; using Swashbuckle.AspNetCore.Swagger; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +[Collection("TestSite")] +public class CustomDocumentSerializerTests { - [Collection("TestSite")] - public class CustomDocumentSerializerTests + [Fact] + public async Task TestSite_Writes_Custom_V3_Document() { - [Fact] - public async Task TestSite_Writes_Custom_V3_Document() - { - var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); - var client = testSite.BuildClient(); + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); + var client = testSite.BuildClient(); - var swaggerResponse = await client.GetAsync($"/swagger/v1/swagger.json"); + var swaggerResponse = await client.GetAsync($"/swagger/v1/swagger.json"); - swaggerResponse.EnsureSuccessStatusCode(); - var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); - using var document = JsonDocument.Parse(contentStream); + swaggerResponse.EnsureSuccessStatusCode(); + var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); + using var document = JsonDocument.Parse(contentStream); - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); - } + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); + } - [Fact] - public async Task TestSite_Writes_Custom_V2_Document() - { - var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); - var client = testSite.BuildClient(); + [Fact] + public async Task TestSite_Writes_Custom_V2_Document() + { + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); + var client = testSite.BuildClient(); - var swaggerResponse = await client.GetAsync($"/swagger/v1/swaggerv2.json"); + var swaggerResponse = await client.GetAsync($"/swagger/v1/swaggerv2.json"); - swaggerResponse.EnsureSuccessStatusCode(); - var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); - using var document = JsonDocument.Parse(contentStream); + swaggerResponse.EnsureSuccessStatusCode(); + var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); + using var document = JsonDocument.Parse(contentStream); - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); - } + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); + } - [Fact] - public async Task DocumentProvider_Writes_Custom_V3_Document() - { - var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); - var server = testSite.BuildServer(); - var services = server.Host.Services; + [Fact] + public async Task DocumentProvider_Writes_Custom_V3_Document() + { + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); + var server = testSite.BuildServer(); + var services = server.Host.Services; - var documentProvider = services.GetService(); - using var stream = new MemoryStream(); + var documentProvider = services.GetService(); + using var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) - { - await documentProvider.GenerateAsync("v1", writer); - await writer.FlushAsync(); - } + using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) + { + await documentProvider.GenerateAsync("v1", writer); + await writer.FlushAsync(); + } - stream.Position = 0L; + stream.Position = 0L; - using var document = JsonDocument.Parse(stream); + using var document = JsonDocument.Parse(stream); - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); - } + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); + } - [Fact] - public async Task DocumentProvider_Writes_Custom_V2_Document() - { - await DocumentProviderWritesCustomV2Document( - (options) => options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0); - } + [Fact] + public async Task DocumentProvider_Writes_Custom_V2_Document() + { + await DocumentProviderWritesCustomV2Document( + (options) => options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0); + } - [Obsolete] - [Fact] - public async Task DocumentProvider_Writes_Custom_V2_Document_SerializeAsV2() - => await DocumentProviderWritesCustomV2Document((options) => options.SerializeAsV2 = true); + [Obsolete] + [Fact] + public async Task DocumentProvider_Writes_Custom_V2_Document_SerializeAsV2() + => await DocumentProviderWritesCustomV2Document((options) => options.SerializeAsV2 = true); - private static async Task DocumentProviderWritesCustomV2Document(Action configure) - { - var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); - var server = testSite.BuildServer(); - var services = server.Host.Services; + private static async Task DocumentProviderWritesCustomV2Document(Action configure) + { + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup)); + var server = testSite.BuildServer(); + var services = server.Host.Services; - var documentProvider = services.GetService(); - var options = services.GetService>(); + var documentProvider = services.GetService(); + var options = services.GetService>(); - configure(options.Value); + configure(options.Value); - using var stream = new MemoryStream(); + using var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) - { - await documentProvider.GenerateAsync("v1", writer); - await writer.FlushAsync(); - } + using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) + { + await documentProvider.GenerateAsync("v1", writer); + await writer.FlushAsync(); + } - stream.Position = 0L; + stream.Position = 0L; - using var document = JsonDocument.Parse(stream); + using var document = JsonDocument.Parse(stream); - // verify that the custom serializer wrote the swagger info - var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); - Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); - } + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest2.0", swaggerInfo); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs index 63a6081270..f3aaeb3b41 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs @@ -2,68 +2,67 @@ using Microsoft.Extensions.ApiDescriptions; using Swashbuckle.AspNetCore.Swagger; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +[Collection("TestSite")] +public class DocumentProviderTests { - [Collection("TestSite")] - public class DocumentProviderTests + [Theory] + [InlineData(typeof(Basic.Startup), new[] { "v1" })] + [InlineData(typeof(CustomUIConfig.Startup), new[] { "v1" })] + [InlineData(typeof(CustomUIIndex.Startup), new[] { "v1" })] + [InlineData(typeof(GenericControllers.Startup), new[] { "v1" })] + [InlineData(typeof(MultipleVersions.Startup), new[] { "1.0", "2.0" })] + [InlineData(typeof(OAuth2Integration.Startup), new[] { "v1" })] + public void DocumentProvider_ExposesAllDocumentNames(Type startupType, string[] expectedNames) { - [Theory] - [InlineData(typeof(Basic.Startup), new[] { "v1" })] - [InlineData(typeof(CustomUIConfig.Startup), new[] { "v1" })] - [InlineData(typeof(CustomUIIndex.Startup), new[] { "v1" })] - [InlineData(typeof(GenericControllers.Startup), new[] { "v1" })] - [InlineData(typeof(MultipleVersions.Startup), new[] { "1.0", "2.0" })] - [InlineData(typeof(OAuth2Integration.Startup), new[] { "v1" })] - public void DocumentProvider_ExposesAllDocumentNames(Type startupType, string[] expectedNames) - { - var testSite = new TestSite(startupType); - var server = testSite.BuildServer(); - var services = server.Host.Services; - var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); + var testSite = new TestSite(startupType); + var server = testSite.BuildServer(); + var services = server.Host.Services; + var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); - var documentNames = documentProvider.GetDocumentNames(); - - Assert.Equal(expectedNames, documentNames); - } + var documentNames = documentProvider.GetDocumentNames(); - [Theory] - [InlineData(typeof(Basic.Startup), "v1")] - [InlineData(typeof(CustomUIConfig.Startup), "v1")] - [InlineData(typeof(CustomUIIndex.Startup), "v1")] - [InlineData(typeof(GenericControllers.Startup), "v1")] - [InlineData(typeof(MultipleVersions.Startup), "2.0")] - [InlineData(typeof(OAuth2Integration.Startup), "v1")] - public async Task DocumentProvider_ExposesGeneratedSwagger(Type startupType, string documentName) - { - var testSite = new TestSite(startupType); - var server = testSite.BuildServer(); - var services = server.Host.Services; + Assert.Equal(expectedNames, documentNames); + } - var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); - using var stream = new MemoryStream(); - using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) - { - await documentProvider.GenerateAsync(documentName, writer); - await writer.FlushAsync(); - } + [Theory] + [InlineData(typeof(Basic.Startup), "v1")] + [InlineData(typeof(CustomUIConfig.Startup), "v1")] + [InlineData(typeof(CustomUIIndex.Startup), "v1")] + [InlineData(typeof(GenericControllers.Startup), "v1")] + [InlineData(typeof(MultipleVersions.Startup), "2.0")] + [InlineData(typeof(OAuth2Integration.Startup), "v1")] + public async Task DocumentProvider_ExposesGeneratedSwagger(Type startupType, string documentName) + { + var testSite = new TestSite(startupType); + var server = testSite.BuildServer(); + var services = server.Host.Services; - stream.Position = 0L; - var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(stream); - Assert.NotNull(diagnostic); - Assert.Empty(diagnostic.Errors); + var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); + using var stream = new MemoryStream(); + using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) + { + await documentProvider.GenerateAsync(documentName, writer); + await writer.FlushAsync(); } - [Fact] - public async Task DocumentProvider_ThrowsUnknownDocument_IfUnknownDocumentName() - { - var testSite = new TestSite(typeof(Basic.Startup)); - var server = testSite.BuildServer(); - var services = server.Host.Services; + stream.Position = 0L; + var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(stream); + Assert.NotNull(diagnostic); + Assert.Empty(diagnostic.Errors); + } - var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); - using var writer = new StringWriter(); - await Assert.ThrowsAsync( - () => documentProvider.GenerateAsync("NotADocument", writer)); - } + [Fact] + public async Task DocumentProvider_ThrowsUnknownDocument_IfUnknownDocumentName() + { + var testSite = new TestSite(typeof(Basic.Startup)); + var server = testSite.BuildServer(); + var services = server.Host.Services; + + var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); + using var writer = new StringWriter(); + await Assert.ThrowsAsync( + () => documentProvider.GenerateAsync("NotADocument", writer)); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs index a01fca06a4..08819b3b6a 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/ReDocIntegrationTests.cs @@ -3,148 +3,147 @@ using Swashbuckle.AspNetCore.ReDoc; using ReDocApp = ReDoc; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +[Collection("TestSite")] +public class ReDocIntegrationTests { - [Collection("TestSite")] - public class ReDocIntegrationTests + [Fact] + public async Task RoutePrefix_RedirectsToIndexUrl() + { + var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); + + var response = await client.GetAsync("/api-docs"); + + Assert.Equal(HttpStatusCode.MovedPermanently, response.StatusCode); + Assert.Equal("api-docs/index.html", response.Headers.Location.ToString()); + } + + [Fact] + public async Task IndexUrl_ReturnsEmbeddedVersionOfTheRedocUI() + { + var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); + + var htmlResponse = await client.GetAsync("/api-docs/index.html"); + var cssResponse = await client.GetAsync("/api-docs/index.css"); + var jsResponse = await client.GetAsync("/api-docs/redoc.standalone.js"); + + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + } + + [Fact] + public async Task RedocMiddleware_ReturnsInitializerScript() + { + var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); + + var response = await client.GetAsync("/api-docs/index.js"); + var content = await response.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Contains("Redoc.init", content); + Assert.DoesNotContain("%(DocumentTitle)", content); + Assert.DoesNotContain("%(HeadContent)", content); + Assert.DoesNotContain("%(SpecUrl)", content); + Assert.DoesNotContain("%(ConfigObject)", content); + } + + [Fact] + public async Task IndexUrl_IgnoresUrlCase() + { + var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); + + var htmlResponse = await client.GetAsync("/Api-Docs/index.html"); + var cssResponse = await client.GetAsync("/Api-Docs/index.css"); + var jsInitResponse = await client.GetAsync("/Api-Docs/index.js"); + var jsRedocResponse = await client.GetAsync("/Api-Docs/redoc.standalone.js"); + + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsInitResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsRedocResponse.StatusCode); + } + + [Theory] + [InlineData("/redoc/1.0/index.html", "/redoc/1.0/index.js", "/swagger/1.0/swagger.json")] + [InlineData("/redoc/2.0/index.html", "/redoc/2.0/index.js", "/swagger/2.0/swagger.json")] + public async Task RedocMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string swaggerPath) + { + var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); + + var htmlResponse = await client.GetAsync(htmlUrl); + var jsResponse = await client.GetAsync(jsUrl); + var content = await jsResponse.Content.ReadAsStringAsync(); + + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + Assert.Contains(swaggerPath, content); + } + + [Fact] + public void ReDocOptions_Extensions() { - [Fact] - public async Task RoutePrefix_RedirectsToIndexUrl() - { - var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - - var response = await client.GetAsync("/api-docs"); - - Assert.Equal(HttpStatusCode.MovedPermanently, response.StatusCode); - Assert.Equal("api-docs/index.html", response.Headers.Location.ToString()); - } - - [Fact] - public async Task IndexUrl_ReturnsEmbeddedVersionOfTheRedocUI() - { - var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - - var htmlResponse = await client.GetAsync("/api-docs/index.html"); - var cssResponse = await client.GetAsync("/api-docs/index.css"); - var jsResponse = await client.GetAsync("/api-docs/redoc.standalone.js"); - - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - } - - [Fact] - public async Task RedocMiddleware_ReturnsInitializerScript() - { - var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - - var response = await client.GetAsync("/api-docs/index.js"); - var content = await response.Content.ReadAsStringAsync(); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Contains("Redoc.init", content); - Assert.DoesNotContain("%(DocumentTitle)", content); - Assert.DoesNotContain("%(HeadContent)", content); - Assert.DoesNotContain("%(SpecUrl)", content); - Assert.DoesNotContain("%(ConfigObject)", content); - } - - [Fact] - public async Task IndexUrl_IgnoresUrlCase() - { - var client = new TestSite(typeof(ReDocApp.Startup)).BuildClient(); - - var htmlResponse = await client.GetAsync("/Api-Docs/index.html"); - var cssResponse = await client.GetAsync("/Api-Docs/index.css"); - var jsInitResponse = await client.GetAsync("/Api-Docs/index.js"); - var jsRedocResponse = await client.GetAsync("/Api-Docs/redoc.standalone.js"); - - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, jsInitResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, jsRedocResponse.StatusCode); - } - - [Theory] - [InlineData("/redoc/1.0/index.html", "/redoc/1.0/index.js", "/swagger/1.0/swagger.json")] - [InlineData("/redoc/2.0/index.html", "/redoc/2.0/index.js", "/swagger/2.0/swagger.json")] - public async Task RedocMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string swaggerPath) - { - var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); - - var htmlResponse = await client.GetAsync(htmlUrl); - var jsResponse = await client.GetAsync(jsUrl); - var content = await jsResponse.Content.ReadAsStringAsync(); - - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - Assert.Contains(swaggerPath, content); - } - - [Fact] - public void ReDocOptions_Extensions() - { - // Arrange - var options = new ReDocOptions(); - - // Act and Assert - Assert.NotNull(options.IndexStream); - Assert.Null(options.JsonSerializerOptions); - Assert.Null(options.SpecUrl); - Assert.Equal("API Docs", options.DocumentTitle); - Assert.Equal(string.Empty, options.HeadContent); - Assert.Equal("api-docs", options.RoutePrefix); - - Assert.NotNull(options.ConfigObject); - Assert.NotNull(options.ConfigObject.AdditionalItems); - Assert.Empty(options.ConfigObject.AdditionalItems); - Assert.Null(options.ConfigObject.ScrollYOffset); - Assert.Equal("all", options.ConfigObject.ExpandResponses); - Assert.False(options.ConfigObject.DisableSearch); - Assert.False(options.ConfigObject.HideDownloadButton); - Assert.False(options.ConfigObject.HideHostname); - Assert.False(options.ConfigObject.HideLoading); - Assert.False(options.ConfigObject.NativeScrollbars); - Assert.False(options.ConfigObject.NoAutoAuth); - Assert.False(options.ConfigObject.OnlyRequiredInSamples); - Assert.False(options.ConfigObject.PathInMiddlePanel); - Assert.False(options.ConfigObject.RequiredPropsFirst); - Assert.False(options.ConfigObject.SortPropsAlphabetically); - Assert.False(options.ConfigObject.UntrustedSpec); - - // Act - options.DisableSearch(); - options.EnableUntrustedSpec(); - options.ExpandResponses("response"); - options.HideDownloadButton(); - options.HideHostname(); - options.HideLoading(); - options.InjectStylesheet("custom.css", "screen and (max-width: 700px)"); - options.NativeScrollbars(); - options.NoAutoAuth(); - options.OnlyRequiredInSamples(); - options.PathInMiddlePanel(); - options.RequiredPropsFirst(); - options.ScrollYOffset(42); - options.SortPropsAlphabetically(); - options.SpecUrl("spec.json"); - - // Assert - Assert.Equal("" + Environment.NewLine, options.HeadContent); - Assert.Equal("spec.json", options.SpecUrl); - Assert.Equal("response", options.ConfigObject.ExpandResponses); - Assert.Equal(42, options.ConfigObject.ScrollYOffset); - Assert.True(options.ConfigObject.DisableSearch); - Assert.True(options.ConfigObject.HideDownloadButton); - Assert.True(options.ConfigObject.HideHostname); - Assert.True(options.ConfigObject.HideLoading); - Assert.True(options.ConfigObject.NativeScrollbars); - Assert.True(options.ConfigObject.NoAutoAuth); - Assert.True(options.ConfigObject.OnlyRequiredInSamples); - Assert.True(options.ConfigObject.PathInMiddlePanel); - Assert.True(options.ConfigObject.RequiredPropsFirst); - Assert.True(options.ConfigObject.SortPropsAlphabetically); - Assert.True(options.ConfigObject.UntrustedSpec); - } + // Arrange + var options = new ReDocOptions(); + + // Act and Assert + Assert.NotNull(options.IndexStream); + Assert.Null(options.JsonSerializerOptions); + Assert.Null(options.SpecUrl); + Assert.Equal("API Docs", options.DocumentTitle); + Assert.Equal(string.Empty, options.HeadContent); + Assert.Equal("api-docs", options.RoutePrefix); + + Assert.NotNull(options.ConfigObject); + Assert.NotNull(options.ConfigObject.AdditionalItems); + Assert.Empty(options.ConfigObject.AdditionalItems); + Assert.Null(options.ConfigObject.ScrollYOffset); + Assert.Equal("all", options.ConfigObject.ExpandResponses); + Assert.False(options.ConfigObject.DisableSearch); + Assert.False(options.ConfigObject.HideDownloadButton); + Assert.False(options.ConfigObject.HideHostname); + Assert.False(options.ConfigObject.HideLoading); + Assert.False(options.ConfigObject.NativeScrollbars); + Assert.False(options.ConfigObject.NoAutoAuth); + Assert.False(options.ConfigObject.OnlyRequiredInSamples); + Assert.False(options.ConfigObject.PathInMiddlePanel); + Assert.False(options.ConfigObject.RequiredPropsFirst); + Assert.False(options.ConfigObject.SortPropsAlphabetically); + Assert.False(options.ConfigObject.UntrustedSpec); + + // Act + options.DisableSearch(); + options.EnableUntrustedSpec(); + options.ExpandResponses("response"); + options.HideDownloadButton(); + options.HideHostname(); + options.HideLoading(); + options.InjectStylesheet("custom.css", "screen and (max-width: 700px)"); + options.NativeScrollbars(); + options.NoAutoAuth(); + options.OnlyRequiredInSamples(); + options.PathInMiddlePanel(); + options.RequiredPropsFirst(); + options.ScrollYOffset(42); + options.SortPropsAlphabetically(); + options.SpecUrl("spec.json"); + + // Assert + Assert.Equal("" + Environment.NewLine, options.HeadContent); + Assert.Equal("spec.json", options.SpecUrl); + Assert.Equal("response", options.ConfigObject.ExpandResponses); + Assert.Equal(42, options.ConfigObject.ScrollYOffset); + Assert.True(options.ConfigObject.DisableSearch); + Assert.True(options.ConfigObject.HideDownloadButton); + Assert.True(options.ConfigObject.HideHostname); + Assert.True(options.ConfigObject.HideLoading); + Assert.True(options.ConfigObject.NativeScrollbars); + Assert.True(options.ConfigObject.NoAutoAuth); + Assert.True(options.ConfigObject.OnlyRequiredInSamples); + Assert.True(options.ConfigObject.PathInMiddlePanel); + Assert.True(options.ConfigObject.RequiredPropsFirst); + Assert.True(options.ConfigObject.SortPropsAlphabetically); + Assert.True(options.ConfigObject.UntrustedSpec); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs index 215b52b647..7b7c845940 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerAndSwaggerUIIntegrationTests.cs @@ -1,23 +1,22 @@ using System.Net; using Microsoft.AspNetCore.Mvc.Testing; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +public class SwaggerAndSwaggerUIIntegrationTests { - public class SwaggerAndSwaggerUIIntegrationTests + [Theory] + [InlineData("/swagger/index.html", "text/html")] + [InlineData("/swagger/v1.json", "application/json")] + [InlineData("/swagger/v1.yaml", "text/yaml")] + [InlineData("/swagger/v1.yml", "text/yaml")] + public async Task SwaggerDocWithoutSubdirectory(string path, string mediaType) { - [Theory] - [InlineData("/swagger/index.html", "text/html")] - [InlineData("/swagger/v1.json", "application/json")] - [InlineData("/swagger/v1.yaml", "text/yaml")] - [InlineData("/swagger/v1.yml", "text/yaml")] - public async Task SwaggerDocWithoutSubdirectory(string path, string mediaType) - { - var client = new WebApplicationFactory().CreateClient(); + var client = new WebApplicationFactory().CreateClient(); - var response = await client.GetAsync(path); + var response = await client.GetAsync(path); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType); - } + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(mediaType, response.Content.Headers.ContentType?.MediaType); } -} \ No newline at end of file +} diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs index 8394eae0ea..080b0b1576 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs @@ -1,210 +1,209 @@ using System.Globalization; using System.Reflection; -#if NET8_0_OR_GREATER +#if NET #endif using System.Text; using System.Text.Json; using Microsoft.OpenApi.Any; using ReDocApp = ReDoc; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +[Collection("TestSite")] +public class SwaggerIntegrationTests { - [Collection("TestSite")] - public class SwaggerIntegrationTests + [Theory] + [InlineData(typeof(Basic.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(CliExample.Startup), "/swagger/v1/swagger_net8.0.json")] + [InlineData(typeof(ConfigFromFile.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(CustomUIIndex.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(GenericControllers.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(MultipleVersions.Startup), "/swagger/1.0/swagger.json")] + [InlineData(typeof(MultipleVersions.Startup), "/swagger/2.0/swagger.json")] + [InlineData(typeof(NSwagClientExample.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(OAuth2Integration.Startup), "/resource-server/swagger/v1/swagger.json")] + [InlineData(typeof(ReDocApp.Startup), "/swagger/v1/swagger.json")] + [InlineData(typeof(TestFirst.Startup), "/swagger/v1-generated/openapi.json")] + public async Task SwaggerEndpoint_ReturnsValidSwaggerJson( + Type startupType, + string swaggerRequestUri) { - [Theory] - [InlineData(typeof(Basic.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(CliExample.Startup), "/swagger/v1/swagger_net8.0.json")] - [InlineData(typeof(ConfigFromFile.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(CustomUIIndex.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(GenericControllers.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(MultipleVersions.Startup), "/swagger/1.0/swagger.json")] - [InlineData(typeof(MultipleVersions.Startup), "/swagger/2.0/swagger.json")] - [InlineData(typeof(NSwagClientExample.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(OAuth2Integration.Startup), "/resource-server/swagger/v1/swagger.json")] - [InlineData(typeof(ReDocApp.Startup), "/swagger/v1/swagger.json")] - [InlineData(typeof(TestFirst.Startup), "/swagger/v1-generated/openapi.json")] - public async Task SwaggerEndpoint_ReturnsValidSwaggerJson( - Type startupType, - string swaggerRequestUri) - { - var testSite = new TestSite(startupType); - using var client = testSite.BuildClient(); + var testSite = new TestSite(startupType); + using var client = testSite.BuildClient(); - await AssertValidSwaggerJson(client, swaggerRequestUri); - } + await AssertValidSwaggerJson(client, swaggerRequestUri); + } - [Fact] - public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq() - { - var testSite = new TestSiteAutofaq(typeof(CliExampleWithFactory.Startup)); - using var client = testSite.BuildClient(); + [Fact] + public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq() + { + var testSite = new TestSiteAutofaq(typeof(CliExampleWithFactory.Startup)); + using var client = testSite.BuildClient(); - await AssertValidSwaggerJson(client, "/swagger/v1/swagger_net8.0.json"); - } + await AssertValidSwaggerJson(client, "/swagger/v1/swagger_net8.0.json"); + } - [Fact] - public async Task SwaggerEndpoint_ReturnsNotFound_IfUnknownSwaggerDocument() - { - var testSite = new TestSite(typeof(Basic.Startup)); - using var client = testSite.BuildClient(); + [Fact] + public async Task SwaggerEndpoint_ReturnsNotFound_IfUnknownSwaggerDocument() + { + var testSite = new TestSite(typeof(Basic.Startup)); + using var client = testSite.BuildClient(); - using var swaggerResponse = await client.GetAsync("/swagger/v2/swagger.json"); + using var swaggerResponse = await client.GetAsync("/swagger/v2/swagger.json"); - Assert.Equal(System.Net.HttpStatusCode.NotFound, swaggerResponse.StatusCode); - } + Assert.Equal(System.Net.HttpStatusCode.NotFound, swaggerResponse.StatusCode); + } - [Fact] - public async Task SwaggerEndpoint_DoesNotReturnByteOrderMark() - { - var testSite = new TestSite(typeof(Basic.Startup)); - using var client = testSite.BuildClient(); + [Fact] + public async Task SwaggerEndpoint_DoesNotReturnByteOrderMark() + { + var testSite = new TestSite(typeof(Basic.Startup)); + using var client = testSite.BuildClient(); - using var swaggerResponse = await client.GetAsync("/swagger/v1/swagger.json"); + using var swaggerResponse = await client.GetAsync("/swagger/v1/swagger.json"); - swaggerResponse.EnsureSuccessStatusCode(); - var contentBytes = await swaggerResponse.Content.ReadAsByteArrayAsync(); - var bomBytes = Encoding.UTF8.GetPreamble(); - Assert.NotEqual(bomBytes, contentBytes.Take(bomBytes.Length)); - } + swaggerResponse.EnsureSuccessStatusCode(); + var contentBytes = await swaggerResponse.Content.ReadAsByteArrayAsync(); + var bomBytes = Encoding.UTF8.GetPreamble(); + Assert.NotEqual(bomBytes, contentBytes.Take(bomBytes.Length)); + } + + [Theory] + [InlineData("en-US")] + [InlineData("sv-SE")] + public async Task SwaggerEndpoint_ReturnsCorrectPriceExample_ForDifferentCultures(string culture) + { + var testSite = new TestSite(typeof(Basic.Startup)); + using var client = testSite.BuildClient(); - [Theory] - [InlineData("en-US")] - [InlineData("sv-SE")] - public async Task SwaggerEndpoint_ReturnsCorrectPriceExample_ForDifferentCultures(string culture) + using var swaggerResponse = await client.GetAsync($"/swagger/v1/swagger.json?culture={culture}"); + + swaggerResponse.EnsureSuccessStatusCode(); + using var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); + var currentCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + try { - var testSite = new TestSite(typeof(Basic.Startup)); - using var client = testSite.BuildClient(); - - using var swaggerResponse = await client.GetAsync($"/swagger/v1/swagger.json?culture={culture}"); - - swaggerResponse.EnsureSuccessStatusCode(); - using var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); - var currentCulture = CultureInfo.CurrentCulture; - CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; - try - { - var openApiDocument = await OpenApiDocumentLoader.LoadAsync(contentStream); - var example = openApiDocument.Components.Schemas["Product"].Example; - var exampleObject = Assert.IsType(example); - double price = Assert.IsType(exampleObject["price"]).Value; - Assert.Equal(14.37, price); - } - finally - { - CultureInfo.CurrentCulture = currentCulture; - } + var openApiDocument = await OpenApiDocumentLoader.LoadAsync(contentStream); + var example = openApiDocument.Components.Schemas["Product"].Example; + var exampleObject = Assert.IsType(example); + double price = Assert.IsType(exampleObject["price"]).Value; + Assert.Equal(14.37, price); } - - [Theory] - [InlineData("/swagger/v1/swagger.json", "openapi", "3.0.4")] - [InlineData("/swagger/v1/swaggerv2.json", "swagger", "2.0")] - public async Task SwaggerMiddleware_CanBeConfiguredMultipleTimes( - string swaggerUrl, - string expectedVersionProperty, - string expectedVersionValue) + finally { - using var client = new TestSite(typeof(Basic.Startup)).BuildClient(); + CultureInfo.CurrentCulture = currentCulture; + } + } - using var response = await client.GetAsync(swaggerUrl); + [Theory] + [InlineData("/swagger/v1/swagger.json", "openapi", "3.0.4")] + [InlineData("/swagger/v1/swaggerv2.json", "swagger", "2.0")] + public async Task SwaggerMiddleware_CanBeConfiguredMultipleTimes( + string swaggerUrl, + string expectedVersionProperty, + string expectedVersionValue) + { + using var client = new TestSite(typeof(Basic.Startup)).BuildClient(); - response.EnsureSuccessStatusCode(); - using var contentStream = await response.Content.ReadAsStreamAsync(); + using var response = await client.GetAsync(swaggerUrl); - var json = await JsonSerializer.DeserializeAsync(contentStream); - Assert.Equal(expectedVersionValue, json.GetProperty(expectedVersionProperty).GetString()); - } + response.EnsureSuccessStatusCode(); + using var contentStream = await response.Content.ReadAsStreamAsync(); - [Theory] - [InlineData(typeof(MinimalApp.Program), "/swagger/v1/swagger.json")] - [InlineData(typeof(TopLevelSwaggerDoc.Program), "/swagger/v1.json")] - [InlineData(typeof(MvcWithNullable.Program), "/swagger/v1/swagger.json")] - [InlineData(typeof(WebApi.Program), "/swagger/v1/swagger.json")] - [InlineData(typeof(WebApi.Aot.Program), "/swagger/v1/swagger.json")] - public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_Without_Startup( - Type entryPointType, - string swaggerRequestUri) - { - await SwaggerEndpointReturnsValidSwaggerJson(entryPointType, swaggerRequestUri); - } + var json = await JsonSerializer.DeserializeAsync(contentStream); + Assert.Equal(expectedVersionValue, json.GetProperty(expectedVersionProperty).GetString()); + } - [Fact] - public async Task TypesAreRenderedCorrectly() - { - using var application = new TestApplication(); - using var client = application.CreateDefaultClient(); + [Theory] + [InlineData(typeof(MinimalApp.Program), "/swagger/v1/swagger.json")] + [InlineData(typeof(TopLevelSwaggerDoc.Program), "/swagger/v1.json")] + [InlineData(typeof(MvcWithNullable.Program), "/swagger/v1/swagger.json")] + [InlineData(typeof(WebApi.Program), "/swagger/v1/swagger.json")] + [InlineData(typeof(WebApi.Aot.Program), "/swagger/v1/swagger.json")] + public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_Without_Startup( + Type entryPointType, + string swaggerRequestUri) + { + await SwaggerEndpointReturnsValidSwaggerJson(entryPointType, swaggerRequestUri); + } - using var response = await client.GetAsync("/swagger/v1/swagger.json"); + [Fact] + public async Task TypesAreRenderedCorrectly() + { + using var application = new TestApplication(); + using var client = application.CreateDefaultClient(); - var content = await response.Content.ReadAsStringAsync(); + using var response = await client.GetAsync("/swagger/v1/swagger.json"); - Assert.True(response.IsSuccessStatusCode, content); + var content = await response.Content.ReadAsStringAsync(); - using var swaggerResponse = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); + Assert.True(response.IsSuccessStatusCode, content); - var weatherForecase = swaggerResponse.RootElement - .GetProperty("components") - .GetProperty("schemas") - .GetProperty("WeatherForecast"); + using var swaggerResponse = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); - Assert.Equal("object", weatherForecase.GetProperty("type").GetString()); + var weatherForecase = swaggerResponse.RootElement + .GetProperty("components") + .GetProperty("schemas") + .GetProperty("WeatherForecast"); - var properties = weatherForecase.GetProperty("properties"); - Assert.Equal(4, properties.EnumerateObject().Count()); + Assert.Equal("object", weatherForecase.GetProperty("type").GetString()); - Assert.Multiple( - [ - () => Assert.Equal("string", properties.GetProperty("date").GetProperty("type").GetString()), - () => Assert.Equal("date", properties.GetProperty("date").GetProperty("format").GetString()), - () => Assert.Equal("integer", properties.GetProperty("temperatureC").GetProperty("type").GetString()), - () => Assert.Equal("int32", properties.GetProperty("temperatureC").GetProperty("format").GetString()), - () => Assert.Equal("string", properties.GetProperty("summary").GetProperty("type").GetString()), - () => Assert.True(properties.GetProperty("summary").GetProperty("nullable").GetBoolean()), - () => Assert.Equal("integer", properties.GetProperty("temperatureF").GetProperty("type").GetString()), - () => Assert.Equal("int32", properties.GetProperty("temperatureF").GetProperty("format").GetString()), - () => Assert.True(properties.GetProperty("temperatureF").GetProperty("readOnly").GetBoolean()), - ]); - } + var properties = weatherForecase.GetProperty("properties"); + Assert.Equal(4, properties.EnumerateObject().Count()); - private static async Task SwaggerEndpointReturnsValidSwaggerJson(Type entryPointType, string swaggerRequestUri) - { - using var client = GetHttpClientForTestApplication(entryPointType); - await AssertValidSwaggerJson(client, swaggerRequestUri); - } + Assert.Multiple( + [ + () => Assert.Equal("string", properties.GetProperty("date").GetProperty("type").GetString()), + () => Assert.Equal("date", properties.GetProperty("date").GetProperty("format").GetString()), + () => Assert.Equal("integer", properties.GetProperty("temperatureC").GetProperty("type").GetString()), + () => Assert.Equal("int32", properties.GetProperty("temperatureC").GetProperty("format").GetString()), + () => Assert.Equal("string", properties.GetProperty("summary").GetProperty("type").GetString()), + () => Assert.True(properties.GetProperty("summary").GetProperty("nullable").GetBoolean()), + () => Assert.Equal("integer", properties.GetProperty("temperatureF").GetProperty("type").GetString()), + () => Assert.Equal("int32", properties.GetProperty("temperatureF").GetProperty("format").GetString()), + () => Assert.True(properties.GetProperty("temperatureF").GetProperty("readOnly").GetBoolean()), + ]); + } - internal static HttpClient GetHttpClientForTestApplication(Type entryPointType) - { - var applicationType = typeof(TestApplication<>).MakeGenericType(entryPointType); - var application = (IDisposable)Activator.CreateInstance(applicationType); - Assert.NotNull(application); + private static async Task SwaggerEndpointReturnsValidSwaggerJson(Type entryPointType, string swaggerRequestUri) + { + using var client = GetHttpClientForTestApplication(entryPointType); + await AssertValidSwaggerJson(client, swaggerRequestUri); + } - var createClientMethod = applicationType - .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .FirstOrDefault(m => m.Name == "CreateDefaultClient" && m.GetParameters().Length == 1) - ?? throw new InvalidOperationException($"The method CreateDefaultClient was not found on TestApplication<{entryPointType.FullName}>."); + internal static HttpClient GetHttpClientForTestApplication(Type entryPointType) + { + var applicationType = typeof(TestApplication<>).MakeGenericType(entryPointType); + var application = (IDisposable)Activator.CreateInstance(applicationType); + Assert.NotNull(application); - // Pass null for DelegatingHandler[] - var parameters = new object[] { null }; + var createClientMethod = applicationType + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .FirstOrDefault(m => m.Name == "CreateDefaultClient" && m.GetParameters().Length == 1) + ?? throw new InvalidOperationException($"The method CreateDefaultClient was not found on TestApplication<{entryPointType.FullName}>."); - var clientObject = (IDisposable)createClientMethod.Invoke(application, parameters); - if (clientObject is not HttpClient client) - { - throw new InvalidOperationException($"The method CreateDefaultClient on TestApplication<{entryPointType.FullName}> did not return an HttpClient."); - } + // Pass null for DelegatingHandler[] + var parameters = new object[] { null }; - return client; + var clientObject = (IDisposable)createClientMethod.Invoke(application, parameters); + if (clientObject is not HttpClient client) + { + throw new InvalidOperationException($"The method CreateDefaultClient on TestApplication<{entryPointType.FullName}> did not return an HttpClient."); } - private static async Task AssertValidSwaggerJson(HttpClient client, string swaggerRequestUri) - { - using var swaggerResponse = await client.GetAsync(swaggerRequestUri); + return client; + } - Assert.True(swaggerResponse.IsSuccessStatusCode, $"IsSuccessStatusCode is false. Response: '{await swaggerResponse.Content.ReadAsStringAsync()}'"); - using var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); - var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(contentStream); - Assert.NotNull(diagnostic); - Assert.Empty(diagnostic.Errors); - } + private static async Task AssertValidSwaggerJson(HttpClient client, string swaggerRequestUri) + { + using var swaggerResponse = await client.GetAsync(swaggerRequestUri); + + Assert.True(swaggerResponse.IsSuccessStatusCode, $"IsSuccessStatusCode is false. Response: '{await swaggerResponse.Content.ReadAsStringAsync()}'"); + using var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(); + var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(contentStream); + Assert.NotNull(diagnostic); + Assert.Empty(diagnostic.Errors); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs index 5259e9eaf5..1c53a060bf 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerUIIntegrationTests.cs @@ -1,173 +1,172 @@ using System.Net; -namespace Swashbuckle.AspNetCore.IntegrationTests +namespace Swashbuckle.AspNetCore.IntegrationTests; + +[Collection("TestSite")] +public class SwaggerUIIntegrationTests { - [Collection("TestSite")] - public class SwaggerUIIntegrationTests + [Theory] + [InlineData(typeof(Basic.Startup), "/", "index.html")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger", "swagger/index.html")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/", "index.html")] + public async Task RoutePrefix_RedirectsToPathRelativeIndexUrl( + Type startupType, + string requestPath, + string expectedRedirectPath) { - [Theory] - [InlineData(typeof(Basic.Startup), "/", "index.html")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger", "swagger/index.html")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/", "index.html")] - public async Task RoutePrefix_RedirectsToPathRelativeIndexUrl( - Type startupType, - string requestPath, - string expectedRedirectPath) - { - var client = new TestSite(startupType).BuildClient(); + var client = new TestSite(startupType).BuildClient(); - var response = await client.GetAsync(requestPath); + var response = await client.GetAsync(requestPath); - Assert.Equal(HttpStatusCode.MovedPermanently, response.StatusCode); - Assert.Equal(expectedRedirectPath, response.Headers.Location.ToString()); - } + Assert.Equal(HttpStatusCode.MovedPermanently, response.StatusCode); + Assert.Equal(expectedRedirectPath, response.Headers.Location.ToString()); + } - [Theory] - [InlineData(typeof(Basic.Startup), "/index.html", "/swagger-ui.js", "/index.css", "/swagger-ui.css")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/swagger/swagger-ui.js", "swagger/index.css", "/swagger/swagger-ui.css")] - public async Task IndexUrl_ReturnsEmbeddedVersionOfTheSwaggerUI( - Type startupType, - string htmlPath, - string swaggerUijsPath, - string indexCssPath, - string swaggerUiCssPath) - { - var client = new TestSite(startupType).BuildClient(); + [Theory] + [InlineData(typeof(Basic.Startup), "/index.html", "/swagger-ui.js", "/index.css", "/swagger-ui.css")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/swagger/swagger-ui.js", "swagger/index.css", "/swagger/swagger-ui.css")] + public async Task IndexUrl_ReturnsEmbeddedVersionOfTheSwaggerUI( + Type startupType, + string htmlPath, + string swaggerUijsPath, + string indexCssPath, + string swaggerUiCssPath) + { + var client = new TestSite(startupType).BuildClient(); - var htmlResponse = await client.GetAsync(htmlPath); - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + var htmlResponse = await client.GetAsync(htmlPath); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - var jsResponse = await client.GetAsync(swaggerUijsPath); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + var jsResponse = await client.GetAsync(swaggerUijsPath); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - var cssResponse = await client.GetAsync(indexCssPath); - Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + var cssResponse = await client.GetAsync(indexCssPath); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); - cssResponse = await client.GetAsync(swaggerUiCssPath); - Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); - } + cssResponse = await client.GetAsync(swaggerUiCssPath); + Assert.Equal(HttpStatusCode.OK, cssResponse.StatusCode); + } - [Theory] - [InlineData(typeof(Basic.Startup), "/index.js")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.js")] - public async Task SwaggerUIMiddleware_ReturnsInitializerScript( - Type startupType, - string indexJsPath) - { - var client = new TestSite(startupType).BuildClient(); - - var jsResponse = await client.GetAsync(indexJsPath); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - - var jsContent = await jsResponse.Content.ReadAsStringAsync(); - Assert.Contains("SwaggerUIBundle", jsContent); - Assert.DoesNotContain("%(DocumentTitle)", jsContent); - Assert.DoesNotContain("%(HeadContent)", jsContent); - Assert.DoesNotContain("%(StylesPath)", jsContent); - Assert.DoesNotContain("%(ScriptBundlePath)", jsContent); - Assert.DoesNotContain("%(ScriptPresetsPath)", jsContent); - Assert.DoesNotContain("%(ConfigObject)", jsContent); - Assert.DoesNotContain("%(OAuthConfigObject)", jsContent); - Assert.DoesNotContain("%(Interceptors)", jsContent); - } + [Theory] + [InlineData(typeof(Basic.Startup), "/index.js")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.js")] + public async Task SwaggerUIMiddleware_ReturnsInitializerScript( + Type startupType, + string indexJsPath) + { + var client = new TestSite(startupType).BuildClient(); + + var jsResponse = await client.GetAsync(indexJsPath); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + + var jsContent = await jsResponse.Content.ReadAsStringAsync(); + Assert.Contains("SwaggerUIBundle", jsContent); + Assert.DoesNotContain("%(DocumentTitle)", jsContent); + Assert.DoesNotContain("%(HeadContent)", jsContent); + Assert.DoesNotContain("%(StylesPath)", jsContent); + Assert.DoesNotContain("%(ScriptBundlePath)", jsContent); + Assert.DoesNotContain("%(ScriptPresetsPath)", jsContent); + Assert.DoesNotContain("%(ConfigObject)", jsContent); + Assert.DoesNotContain("%(OAuthConfigObject)", jsContent); + Assert.DoesNotContain("%(Interceptors)", jsContent); + } - [Fact] - public async Task IndexUrl_DefinesPlugins() - { - var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); + [Fact] + public async Task IndexUrl_DefinesPlugins() + { + var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); - var jsResponse = await client.GetAsync("/swagger/index.js"); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + var jsResponse = await client.GetAsync("/swagger/index.js"); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - var jsContent = await jsResponse.Content.ReadAsStringAsync(); - Assert.Contains("\"plugins\":[\"customPlugin1\",\"customPlugin2\"]", jsContent); - } + var jsContent = await jsResponse.Content.ReadAsStringAsync(); + Assert.Contains("\"plugins\":[\"customPlugin1\",\"customPlugin2\"]", jsContent); + } - [Fact] - public async Task IndexUrl_DoesntDefinePlugins() - { - var client = new TestSite(typeof(Basic.Startup)).BuildClient(); + [Fact] + public async Task IndexUrl_DoesntDefinePlugins() + { + var client = new TestSite(typeof(Basic.Startup)).BuildClient(); - var jsResponse = await client.GetAsync("/index.js"); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - var jsContent = await jsResponse.Content.ReadAsStringAsync(); - Assert.DoesNotContain("\"plugins\"", jsContent); - } + var jsResponse = await client.GetAsync("/index.js"); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + var jsContent = await jsResponse.Content.ReadAsStringAsync(); + Assert.DoesNotContain("\"plugins\"", jsContent); + } - [Fact] - public async Task IndexUrl_ReturnsCustomPageTitleAndStylesheets_IfConfigured() - { - var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); + [Fact] + public async Task IndexUrl_ReturnsCustomPageTitleAndStylesheets_IfConfigured() + { + var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); - var response = await client.GetAsync("/swagger/index.html"); - var content = await response.Content.ReadAsStringAsync(); + var response = await client.GetAsync("/swagger/index.html"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Contains("CustomUIConfig", content); - Assert.Contains("", content); - } + Assert.Contains("CustomUIConfig", content); + Assert.Contains("", content); + } - [Fact] - public async Task IndexUrl_ReturnsCustomIndexHtml_IfConfigured() - { - var client = new TestSite(typeof(CustomUIIndex.Startup)).BuildClient(); + [Fact] + public async Task IndexUrl_ReturnsCustomIndexHtml_IfConfigured() + { + var client = new TestSite(typeof(CustomUIIndex.Startup)).BuildClient(); - var response = await client.GetAsync("/swagger/index.html"); - var content = await response.Content.ReadAsStringAsync(); + var response = await client.GetAsync("/swagger/index.html"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Contains("Example.com", content); - } + Assert.Contains("Example.com", content); + } - [Fact] - public async Task IndexUrl_ReturnsInterceptors_IfConfigured() - { - var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); + [Fact] + public async Task IndexUrl_ReturnsInterceptors_IfConfigured() + { + var client = new TestSite(typeof(CustomUIConfig.Startup)).BuildClient(); - var response = await client.GetAsync("/swagger/index.js"); - var content = await response.Content.ReadAsStringAsync(); + var response = await client.GetAsync("/swagger/index.js"); + var content = await response.Content.ReadAsStringAsync(); - Assert.Contains("\"RequestInterceptorFunction\":", content); - Assert.Contains("\"ResponseInterceptorFunction\":", content); - } + Assert.Contains("\"RequestInterceptorFunction\":", content); + Assert.Contains("\"ResponseInterceptorFunction\":", content); + } + + [Theory] + [InlineData("/swagger/index.html", "/swagger/index.js", new[] { "Version 1.0", "Version 2.0" })] + [InlineData("/swagger/1.0/index.html", "/swagger/1.0/index.js", new[] { "Version 1.0" })] + [InlineData("/swagger/2.0/index.html", "/swagger/2.0/index.js", new[] { "Version 2.0" })] + public async Task SwaggerUIMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string[] versions) + { + var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); + + var htmlResponse = await client.GetAsync(htmlUrl); + var jsResponse = await client.GetAsync(jsUrl); + var content = await jsResponse.Content.ReadAsStringAsync(); - [Theory] - [InlineData("/swagger/index.html", "/swagger/index.js", new[] { "Version 1.0", "Version 2.0" })] - [InlineData("/swagger/1.0/index.html", "/swagger/1.0/index.js", new[] { "Version 1.0" })] - [InlineData("/swagger/2.0/index.html", "/swagger/2.0/index.js", new[] { "Version 2.0" })] - public async Task SwaggerUIMiddleware_CanBeConfiguredMultipleTimes(string htmlUrl, string jsUrl, string[] versions) + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); + foreach (var version in versions) { - var client = new TestSite(typeof(MultipleVersions.Startup)).BuildClient(); - - var htmlResponse = await client.GetAsync(htmlUrl); - var jsResponse = await client.GetAsync(jsUrl); - var content = await jsResponse.Content.ReadAsStringAsync(); - - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - Assert.Equal(HttpStatusCode.OK, jsResponse.StatusCode); - foreach (var version in versions) - { - Assert.Contains(version, content); - } + Assert.Contains(version, content); } + } - [Theory] - [InlineData(typeof(Basic.Startup), "/index.html", "./swagger-ui.css", "./swagger-ui-bundle.js", "./swagger-ui-standalone-preset.js")] - [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/ext/custom-stylesheet.css", "/ext/custom-javascript.js", "/ext/custom-javascript.js")] - public async Task IndexUrl_Returns_ExpectedAssetPaths( - Type startupType, - string htmlPath, - string cssPath, - string scriptBundlePath, - string scriptPresetsPath) - { - var client = new TestSite(startupType).BuildClient(); + [Theory] + [InlineData(typeof(Basic.Startup), "/index.html", "./swagger-ui.css", "./swagger-ui-bundle.js", "./swagger-ui-standalone-preset.js")] + [InlineData(typeof(CustomUIConfig.Startup), "/swagger/index.html", "/ext/custom-stylesheet.css", "/ext/custom-javascript.js", "/ext/custom-javascript.js")] + public async Task IndexUrl_Returns_ExpectedAssetPaths( + Type startupType, + string htmlPath, + string cssPath, + string scriptBundlePath, + string scriptPresetsPath) + { + var client = new TestSite(startupType).BuildClient(); - var htmlResponse = await client.GetAsync(htmlPath); - Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); + var htmlResponse = await client.GetAsync(htmlPath); + Assert.Equal(HttpStatusCode.OK, htmlResponse.StatusCode); - var content = await htmlResponse.Content.ReadAsStringAsync(); - Assert.Contains($"", content); - Assert.Contains($" - From 6f77d1e7fe1509b931cc78f1d9b7b10f5699f252 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Wed, 10 Sep 2025 16:24:32 +0100 Subject: [PATCH 546/680] .NET 10 preparation (#3565) - Remove usage of `IWebHost` in tests. - Style tweaks and use of newer C# features. - Update NuGet packages to their latest versions. - Assert OpenAPI documents have no warnings. --- Directory.Packages.props | 8 +-- src/Swashbuckle.AspNetCore.Cli/Program.cs | 2 + .../AnnotationsDocumentFilterTests.cs | 10 ++-- .../AnnotationsParameterFilterTests.cs | 5 +- .../AnnotationsRequestBodyFilterTests.cs | 2 +- .../ApiTestRunnerBaseTests.cs | 6 +-- .../JsonValidatorTests.cs | 8 +-- .../RequestValidatorTests.cs | 2 +- .../ResponseValidatorTests.cs | 4 +- .../CustomDocumentSerializerTests.cs | 4 +- .../DocumentProviderTests.cs | 7 +-- .../SwaggerIntegrationTests.cs | 3 +- ...hbuckle.AspNetCore.IntegrationTests.csproj | 8 +-- .../TestSite.cs | 49 ++++++++++++++----- .../TestSiteAutofaq.cs | 37 ++++---------- .../VerifyTests.cs | 2 +- .../MvcWithNullable/MvcWithNullable.csproj | 2 +- .../OAuth2Integration.csproj | 4 +- test/WebSites/ReDoc/Startup.cs | 1 + .../WebApi/EndPoints/OpenApiEndpoints.cs | 1 + test/WebSites/WebApi/WebApi.csproj | 2 +- 21 files changed, 87 insertions(+), 80 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 431e84c03d..29848393ea 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -8,12 +8,12 @@ - - + + - - + + diff --git a/src/Swashbuckle.AspNetCore.Cli/Program.cs b/src/Swashbuckle.AspNetCore.Cli/Program.cs index da548a470f..ceea11ceeb 100644 --- a/src/Swashbuckle.AspNetCore.Cli/Program.cs +++ b/src/Swashbuckle.AspNetCore.Cli/Program.cs @@ -255,10 +255,12 @@ private static IServiceProvider GetServiceProvider(Assembly startupAssembly) return host.Services; } +#pragma warning disable ASPDEPR008 if (TryGetCustomHost(startupAssembly, "SwaggerWebHostFactory", "CreateWebHost", out IWebHost webHost)) { return webHost.Services; } +#pragma warning restore ASPDEPR008 try { diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs index 9349fe26eb..41369ca8cf 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs @@ -1,7 +1,7 @@ using Microsoft.OpenApi.Models; -using Xunit; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.TestSupport; +using Xunit; namespace Swashbuckle.AspNetCore.Annotations.Test; @@ -13,7 +13,7 @@ public void Apply_CreatesMetadataForControllerNameTag_FromSwaggerTagAttribute() var document = new OpenApiDocument(); var apiDescription = ApiDescriptionFactory.Create(c => nameof(c.ActionWithNoAttributes)); var filterContext = new DocumentFilterContext( - apiDescriptions: new[] { apiDescription }, + apiDescriptions: [apiDescription], schemaGenerator: null, schemaRepository: null); @@ -24,8 +24,6 @@ public void Apply_CreatesMetadataForControllerNameTag_FromSwaggerTagAttribute() Assert.Equal("http://tempuri.org/", tag.ExternalDocs.Url.ToString()); } - private AnnotationsDocumentFilter Subject() - { - return new AnnotationsDocumentFilter(); - } + private static AnnotationsDocumentFilter Subject() + => new(); } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs index 48af7a7665..6fa6a8ae4f 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs @@ -61,8 +61,5 @@ public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParamete Assert.True(parameter.Required); } - private AnnotationsParameterFilter Subject() - { - return new AnnotationsParameterFilter(); - } + private static AnnotationsParameterFilter Subject() => new(); } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs index fc2a93f672..374f380391 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs @@ -61,7 +61,7 @@ public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerReques var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); Subject().Apply(requestBody, context); - + Assert.Equal("Description for StringWithSwaggerRequestBodyAttribute", requestBody.Description); Assert.True(requestBody.Required); } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs index e85c83e622..85c3a812d5 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs @@ -26,7 +26,7 @@ public async Task TestAsync_ThrowsException_IfOperationNotFound() { c.OpenApiDocs.Add("v1", new OpenApiDocument()); }); - + var exception = await Assert.ThrowsAsync(() => subject.TestAsync( "v1", "GetProducts", @@ -70,7 +70,7 @@ public async Task TestAsync_ThrowsException_IfExpectedStatusCodeIs2xxAndRequestD ], Responses = new OpenApiResponses { - [ "200" ] = new OpenApiResponse() + [ "200" ] = new OpenApiResponse() } } } @@ -118,7 +118,7 @@ public async Task TestAsync_ThrowsException_IfResponseDoesNotMatchSpec( Responses = new OpenApiResponses { ["400"] = new OpenApiResponse(), - ["200"] = new OpenApiResponse() + ["200"] = new OpenApiResponse() } } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs index 0154284568..2eed054370 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs @@ -418,7 +418,7 @@ public void Validate_ReturnsError_IfRequiredPropertyNotPresent( var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Object, - Required = new SortedSet(schemaRequired) + Required = new SortedSet(schemaRequired), }; var instance = JToken.Parse(instanceText); @@ -541,7 +541,7 @@ public void Validate_ReturnsError_IfInstanceDoesNotMatchAllSchemasSpecifiedByAll [ new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } }, ] }; var instance = JToken.Parse(instanceText); @@ -570,7 +570,7 @@ public void Validate_ReturnsError_IfInstanceDoesNotMatchAnySchemaSpecifiedByAnyO [ new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } }, ] }; var instance = JToken.Parse(instanceText); @@ -600,7 +600,7 @@ public void Validate_ReturnsError_IfInstanceDoesNotMatchExactlyOneSchemaSpecifie [ new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p1" } }, new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p2" } }, - new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } } + new OpenApiSchema { Type = JsonSchemaTypes.Object, Required = new SortedSet { "p3" } }, ] }; var instance = JToken.Parse(instanceText); diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs index 38090f27ff..dc0bc1d1ae 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs @@ -392,6 +392,6 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat private static RequestValidator Subject(IEnumerable contentValidators = null) { - return new RequestValidator(contentValidators ?? []); + return new(contentValidators ?? []); } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs index e6cb61db64..bb5eafe01e 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs @@ -215,7 +215,7 @@ public void Validate_DelegatesContentValidationToInjectedContentValidators( Schema = new OpenApiSchema { Type = JsonSchemaTypes.Object, - Required = new SortedSet { "prop1", "prop2" } + Required = new SortedSet { "prop1", "prop2" }, } } } @@ -259,6 +259,6 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat private static ResponseValidator Subject(IEnumerable contentValidators = null) { - return new ResponseValidator(contentValidators ?? []); + return new(contentValidators ?? []); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs index 19dc653086..cdf8f9c6b4 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs @@ -49,7 +49,7 @@ public async Task DocumentProvider_Writes_Custom_V3_Document() { var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup), outputHelper); var server = testSite.BuildServer(); - var services = server.Host.Services; + var services = server.Services; var documentProvider = services.GetService(); using var stream = new MemoryStream(); @@ -80,7 +80,7 @@ private async Task DocumentProviderWritesCustomV2Document(Action { var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup), outputHelper); var server = testSite.BuildServer(); - var services = server.Host.Services; + var services = server.Services; var documentProvider = services.GetService(); var options = services.GetService>(); diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs index 8bf64cf30b..60c8f31701 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/DocumentProviderTests.cs @@ -18,7 +18,7 @@ public void DocumentProvider_ExposesAllDocumentNames(Type startupType, string[] { var testSite = new TestSite(startupType, outputHelper); var server = testSite.BuildServer(); - var services = server.Host.Services; + var services = server.Services; var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); var documentNames = documentProvider.GetDocumentNames(); @@ -37,7 +37,7 @@ public async Task DocumentProvider_ExposesGeneratedSwagger(Type startupType, str { var testSite = new TestSite(startupType, outputHelper); var server = testSite.BuildServer(); - var services = server.Host.Services; + var services = server.Services; var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); using var stream = new MemoryStream(); @@ -51,6 +51,7 @@ public async Task DocumentProvider_ExposesGeneratedSwagger(Type startupType, str var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(stream); Assert.NotNull(diagnostic); Assert.Empty(diagnostic.Errors); + Assert.Empty(diagnostic.Warnings); } [Fact] @@ -58,7 +59,7 @@ public async Task DocumentProvider_ThrowsUnknownDocument_IfUnknownDocumentName() { var testSite = new TestSite(typeof(Basic.Startup), outputHelper); var server = testSite.BuildServer(); - var services = server.Host.Services; + var services = server.Services; var documentProvider = (IDocumentProvider)services.GetService(typeof(IDocumentProvider)); using var writer = new StringWriter(); diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs index 98a8c8bca2..55ee5c5f94 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs @@ -36,7 +36,7 @@ public async Task SwaggerEndpoint_ReturnsValidSwaggerJson( [Fact] public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq() { - var testSite = new TestSiteAutofaq(typeof(CliExampleWithFactory.Startup)); + var testSite = new TestSiteAutofaq(typeof(CliExampleWithFactory.Startup), outputHelper); using var client = testSite.BuildClient(); await AssertValidSwaggerJson(client, "/swagger/v1/swagger_net8.0.json"); @@ -205,5 +205,6 @@ private static async Task AssertValidSwaggerJson(HttpClient client, string swagg var (_, diagnostic) = await OpenApiDocumentLoader.LoadWithDiagnosticsAsync(contentStream); Assert.NotNull(diagnostic); Assert.Empty(diagnostic.Errors); + Assert.Empty(diagnostic.Warnings); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj index 050373b86a..3d3a84a83e 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj @@ -30,13 +30,13 @@
- - + + - - + + diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs index b6fdbab42c..9af7057754 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSite.cs @@ -4,29 +4,33 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Swashbuckle.AspNetCore.IntegrationTests; public class TestSite(Type startupType, ITestOutputHelper outputHelper) { - public TestServer BuildServer() + private IHost _host; + private TestServer _server; + + public virtual TestServer BuildServer() { - var startupAssembly = startupType.Assembly; - var applicationName = startupAssembly.GetName().Name; + if (_server is null) + { + var builder = new HostBuilder(); - var builder = new WebHostBuilder() - .UseEnvironment("Development") - .UseSolutionRelativeContentRoot(Path.Combine("test", "WebSites", applicationName), "*.slnx") - .UseStartup(startupType); + Configure(builder); - builder.ConfigureTestServices((services) => - { - services.AddLogging((logging) => logging.ClearProviders().AddXUnit(outputHelper)); - services.AddTransient(); - }); + builder.ConfigureWebHost(Configure); + + _host = builder.Build(); + _host.Start(); - return new(builder); + _server = _host.GetTestServer(); + } + + return _server; } public HttpClient BuildClient() @@ -35,6 +39,25 @@ public HttpClient BuildClient() return server.CreateClient(); } + protected virtual void Configure(IHostBuilder builder) + { + builder.ConfigureServices((services) => + { + services.AddLogging((logging) => logging.ClearProviders().AddXUnit(outputHelper)); + services.AddTransient(); + }); + } + + protected virtual void Configure(IWebHostBuilder builder) + { + var applicationName = startupType.Assembly.GetName().Name; + + builder.UseEnvironment("Development") + .UseSolutionRelativeContentRoot(Path.Combine("test", "WebSites", applicationName), "*.slnx") + .UseStartup(startupType) + .UseTestServer(); + } + private sealed class LocalizationStartupFilter : IStartupFilter { public Action Configure(Action next) diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSiteAutofaq.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSiteAutofaq.cs index 2ce4e835e7..43efd9d779 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/TestSiteAutofaq.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/TestSiteAutofaq.cs @@ -1,38 +1,21 @@ -using System.Reflection; -using Autofac.Extensions.DependencyInjection; +using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Hosting; namespace Swashbuckle.AspNetCore.IntegrationTests; -public class TestSiteAutofaq +public class TestSiteAutofaq(Type startupType, ITestOutputHelper outputHelper) + : TestSite(startupType, outputHelper) { - private readonly Type _startupType; - - public TestSiteAutofaq(Type startupType) + protected override void Configure(IHostBuilder builder) { - _startupType = startupType; + base.Configure(builder); + builder.UseServiceProviderFactory(new AutofacServiceProviderFactory()); } - public TestServer BuildServer() + protected override void Configure(IWebHostBuilder builder) { - var startupAssembly = _startupType.Assembly; - var applicationName = startupAssembly.GetName().Name; - - var hostBuilder = new WebHostBuilder() - .UseEnvironment("Development") - .ConfigureServices(services => services.AddAutofac()) - .UseSolutionRelativeContentRoot(Path.Combine("test", "WebSites", applicationName), "*.slnx") - .UseStartup(_startupType); - - return new TestServer(hostBuilder); - } - - public HttpClient BuildClient() - { - var server = BuildServer(); - var client = server.CreateClient(); - - return client; + builder.ConfigureServices((services) => services.AddAutofac()); + base.Configure(builder); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/VerifyTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/VerifyTests.cs index 847824a73e..ffe2aec60d 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/VerifyTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/VerifyTests.cs @@ -42,7 +42,7 @@ public async Task SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq() var startupType = typeof(CliExampleWithFactory.Startup); const string swaggerRequestUri = "/swagger/v1/swagger_net8.0.json"; - var testSite = new TestSiteAutofaq(startupType); + var testSite = new TestSiteAutofaq(startupType, outputHelper); using var client = testSite.BuildClient(); using var swaggerResponse = await client.GetAsync(swaggerRequestUri, TestContext.Current.CancellationToken); diff --git a/test/WebSites/MvcWithNullable/MvcWithNullable.csproj b/test/WebSites/MvcWithNullable/MvcWithNullable.csproj index 1a95be247f..f4465a728b 100644 --- a/test/WebSites/MvcWithNullable/MvcWithNullable.csproj +++ b/test/WebSites/MvcWithNullable/MvcWithNullable.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj index 56cc1d3b57..5bbc961286 100644 --- a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj +++ b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj @@ -11,12 +11,12 @@ - + - + diff --git a/test/WebSites/ReDoc/Startup.cs b/test/WebSites/ReDoc/Startup.cs index a41f56fe1d..e2a96e9732 100644 --- a/test/WebSites/ReDoc/Startup.cs +++ b/test/WebSites/ReDoc/Startup.cs @@ -28,6 +28,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseEndpoints(endpoints => { endpoints.MapControllers(); + endpoints.MapGet("/", () => Results.Redirect("api-docs")); }); app.UseSwagger(c => diff --git a/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs b/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs index cd48ac6313..04d3a629d5 100644 --- a/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs +++ b/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs @@ -98,6 +98,7 @@ public static IEndpointRouteBuilder MapWithOpenApiEndpoints(this IEndpointRouteB return app; } } + record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); diff --git a/test/WebSites/WebApi/WebApi.csproj b/test/WebSites/WebApi/WebApi.csproj index 503306e6ff..c509238d96 100644 --- a/test/WebSites/WebApi/WebApi.csproj +++ b/test/WebSites/WebApi/WebApi.csproj @@ -19,7 +19,7 @@ - + From 9008b30810bf6c80b8c91d6ef7721839e25ae4bf Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Mon, 15 Sep 2025 14:59:25 +0100 Subject: [PATCH 547/680] Update zizmor - Update zizmor to 1.13.0. - Add zizmor configuration file to disable noisy rules. --- .github/workflows/actions-lint.yml | 2 +- .github/zizmor.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml index 0b0cc16e25..80873bf148 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/actions-lint.yml @@ -16,7 +16,7 @@ permissions: {} env: FORCE_COLOR: 3 TERM: xterm - ZIZMOR_VERSION: '1.12.0' + ZIZMOR_VERSION: '1.13.0' jobs: lint: diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000000..ad6d776482 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,5 @@ +rules: + anonymous-definition: + disable: true + undocumented-permissions: + disable: true From 1c2b15c104a545259cf500fc17a67a169ccb94bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:07:45 +0100 Subject: [PATCH 548/680] Bump zizmorcore/zizmor-action from 0.1.2 to 0.2.0 (#3570) --- .github/workflows/actions-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml index 80873bf148..5f42ab341d 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/actions-lint.yml @@ -45,7 +45,7 @@ jobs: args: -color - name: Lint workflows with zizmor - uses: zizmorcore/zizmor-action@5ca5fc7a4779c5263a3ffa0e1f693009994446d1 # v0.1.2 + uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 with: persona: pedantic version: ${{ env.ZIZMOR_VERSION }} From 0bc9eb504a33c64a4469efb72cd7026285a4abf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:08:15 +0100 Subject: [PATCH 549/680] Bump the xunit group with 1 update (#3571) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 29848393ea..ae399227f3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 6ddd16c353e37a2b1280485120adac57d9f61446 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:08:38 +0100 Subject: [PATCH 550/680] Bump anchore/sbom-action from 0.20.5 to 0.20.6 (#3569) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f435840475..fc91e876e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -114,7 +114,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@da167eac915b4e86f08b264dbdbc867b61be6f0c # v0.20.5 + uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 if: runner.os == 'Windows' with: artifact-name: Swashbuckle.AspNetCore.spdx.json From a0dbf30a373e08e540fbee92a38ce01d3f11e953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:15:17 +0000 Subject: [PATCH 551/680] Bump github/codeql-action from 3.30.2 to 3.30.3 (#3568) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 39dc434cba..89a15dd0bd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@d3678e237b9c32a6c9bffb3315c335f976f3549f # v3.30.2 + uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@d3678e237b9c32a6c9bffb3315c335f976f3549f # v3.30.2 + uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index c23a2ef829..85fd0869d3 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@d3678e237b9c32a6c9bffb3315c335f976f3549f # v3.30.2 + uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 with: sarif_file: results.sarif From a097d5d63acf4c48893d87072f01a6f07ef9c69c Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 19 Sep 2025 08:37:09 +0100 Subject: [PATCH 552/680] Update NuGet packages Update various NuGet dependencies to their latest versions. --- Directory.Packages.props | 10 +++++----- .../OAuth2Integration/OAuth2Integration.csproj | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ae399227f3..05d323108b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@  - + @@ -18,11 +18,11 @@ - + - - - + + + diff --git a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj index 5bbc961286..52db6fe4a1 100644 --- a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj +++ b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj @@ -12,12 +12,12 @@ - + - + From 2fde3e9cdd66745c5ae7135eff24d68afa184de6 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Tue, 23 Sep 2025 13:30:05 +0100 Subject: [PATCH 553/680] Fix anchors Fix two broken documentation anchors --- README.md | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index e11f115a57..7a4392e6ed 100644 --- a/README.md +++ b/README.md @@ -221,54 +221,54 @@ Check out the table below for the full list of possible configuration options. | **Component** | **Configuration and Customization** | | ------------- | ----------------------------------- | -| **Swashbuckle.AspNetCore.Swagger** | [Change the Path for Swagger JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-swagger-json-endpoints) | -| | [Modify Swagger with Request Context](docs/configure-and-customize-swagger.md#modify-swagger-with-request-context) | -| | [Serialize Swagger JSON in the 2.0 format](docs/configure-and-customize-swagger.md#serialize-swagger-in-the-20-format) | -| | [Working with Virtual Directories and Reverse Proxies](docs/configure-and-customize-swagger.md#working-with-virtual-directories-and-reverse-proxies) | -| | [Customizing how the OpenAPI document is serialized](docs/configure-and-customize-swagger.md#customizing-how-the-openapi-document-is-serialized) | -| **Swashbuckle.AspNetCore.SwaggerGen** | [Assign Explicit OperationIds](docs/configure-and-customize-swaggergen.md#assign-explicit-operationids) | -| | [List Operations Responses](docs/configure-and-customize-swaggergen.md#list-operation-responses) | -| | [Flag Required Parameters and Schema Properties](docs/configure-and-customize-swaggergen.md#flag-required-parameters-and-schema-properties) | -| | [Handle Forms and File Uploads](docs/configure-and-customize-swaggergen.md#handle-forms-and-file-uploads) | -| | [Handle File Downloads](docs/configure-and-customize-swaggergen.md#handle-file-downloads) | -| | [Include Descriptions from XML Comments](docs/configure-and-customize-swaggergen.md#include-descriptions-from-xml-comments) | -| | [Provide Global API Metadata](docs/configure-and-customize-swaggergen.md#provide-global-api-metadata) | -| | [Generate Multiple Swagger Documents](docs/configure-and-customize-swaggergen.md#generate-multiple-swagger-documents) | -| | [Omit Obsolete Operations and/or Schema Properties](docs/configure-and-customize-swaggergen.md#omit-obsolete-operations-andor-schema-properties) | -| | [Omit Arbitrary Operations](docs/configure-and-customize-swaggergen.md#omit-arbitrary-operations) | -| | [Customize Operation Tags (e.g. for UI Grouping)](docs/configure-and-customize-swaggergen.md#customize-operation-tags-eg-for-ui-grouping) | -| | [Change Operation Sort Order (e.g. for UI Sorting)](docs/configure-and-customize-swaggergen.md#change-operation-sort-order-eg-for-ui-sorting) | -| | [Customize Schema Ids](docs/configure-and-customize-swaggergen.md#customize-schema-ids) | -| | [Override Schema for Specific Types](docs/configure-and-customize-swaggergen.md#override-schema-for-specific-types) | -| | [Extend Generator with Operation, Schema & Document Filters](docs/configure-and-customize-swaggergen.md#extend-generator-with-operation-schema--document-filters) | -| | [Add Security Definitions and Requirements](docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements) | -| | [Add Security Definitions and Requirements for Bearer auth](docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements-for-bearer-auth) | -| | [Inheritance and Polymorphism](docs/configure-and-customize-swaggergen.md#inheritance-and-polymorphism) | -| **Swashbuckle.AspNetCore.SwaggerUI** | [Change Relative Path to the UI](docs/configure-and-customize-swaggerui.md#change-relative-path-to-the-ui) | -| | [Change Document Title](docs/configure-and-customize-swaggerui.md#change-document-title) | -| | [Change CSS or JS Paths](docs/configure-and-customize-swaggerui.md#change-css-or-js-paths) | -| | [List Multiple Swagger Documents](docs/configure-and-customize-swaggerui.md#list-multiple-swagger-documents) | -| | [Apply swagger-ui Parameters](docs/configure-and-customize-swaggerui.md#apply-swagger-ui-parameters) | -| | [Inject Custom JavaScript](docs/configure-and-customize-swaggerui.md#inject-custom-javascript) | -| | [Inject Custom CSS](docs/configure-and-customize-swaggerui.md#inject-custom-css) | -| | [Customize index.html](docs/configure-and-customize-swaggerui.md#customize-indexhtml) | -| | [Enable OAuth2.0 Flows](docs/configure-and-customize-swaggerui.md#enable-oauth20-flows) | -| | [Use client-side request and response interceptors](docs/configure-and-customize-swaggerui.md#use-client-side-request-and-response-interceptors) | -| **Swashbuckle.AspNetCore.Annotations** | [Install and Enable Annotations](docs/configure-and-customize-annotations.md#install-and-enable-annotations) | -| | [Enrich Operation Metadata](docs/configure-and-customize-annotations.md#enrich-operation-metadata) | -| | [Enrich Response Metadata](docs/configure-and-customize-annotations.md#enrich-response-metadata) | -| | [Enrich Parameter Metadata](docs/configure-and-customize-annotations.md#enrich-parameter-metadata) | -| | [Enrich RequestBody Metadata](docs/configure-and-customize-annotations.md#enrich-requestbody-metadata) | -| | [Enrich Schema Metadata](docs/configure-and-customize-annotations.md#enrich-schema-metadata) | -| | [Apply Schema Filters to Specific Types](docs/configure-and-customize-annotations.md#apply-schema-filters-to-specific-types) | -| | [Add Tag Metadata](docs/configure-and-customize-annotations.md#add-tag-metadata) | -| **Swashbuckle.AspNetCore.Cli** | [Retrieve Swagger Directly from a Startup Assembly](docs/configure-and-customize-cli.md#retrieve-swagger-directly-from-a-startup-assembly) | -| | [Use the CLI Tool with a Custom Host Configuration](docs/configure-and-customize-cli.md#use-the-cli-tool-with-a-custom-host-configuration) | -| **Swashbuckle.AspNetCore.ReDoc** | [Change Relative Path to the UI](docs/configure-and-customize-redoc.md#change-relative-path-to-the-ui) | -| | [Change Document Title](docs/configure-and-customize-redoc.md#change-document-title) | -| | [Apply Redoc Parameters](docs/configure-and-customize-redoc.md#apply-redoc-parameters) | -| | [Inject Custom CSS](docs/configure-and-customize-redoc.md#inject-custom-css) | -| | [Customize index.html](docs/configure-and-customize-redoc.md#customize-indexhtml) | +| **Swashbuckle.AspNetCore.Swagger** | [Change the Path for Swagger JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-swagger-json-endpoints) | +| | [Modify Swagger with Request Context](docs/configure-and-customize-swagger.md#modify-swagger-with-request-context) | +| | [Serialize Swagger JSON in the 2.0 format](docs/configure-and-customize-swagger.md#serialize-swagger-in-the-20-format) | +| | [Working with Virtual Directories and Reverse Proxies](docs/configure-and-customize-swagger.md#working-with-virtual-directories-and-reverse-proxies) | +| | [Customizing how the OpenAPI document is serialized](docs/configure-and-customize-swagger.md#customizing-how-the-openapi-document-is-serialized) | +| **Swashbuckle.AspNetCore.SwaggerGen** | [Assign Explicit OperationIds](docs/configure-and-customize-swaggergen.md#assign-explicit-operationids) | +| | [List Operations Responses](docs/configure-and-customize-swaggergen.md#list-operation-responses) | +| | [Flag Required Parameters and Schema Properties](docs/configure-and-customize-swaggergen.md#flag-required-parameters-and-schema-properties) | +| | [Handle Forms and File Uploads](docs/configure-and-customize-swaggergen.md#handle-forms-and-file-uploads) | +| | [Handle File Downloads](docs/configure-and-customize-swaggergen.md#handle-file-downloads) | +| | [Include Descriptions from XML Comments](docs/configure-and-customize-swaggergen.md#include-descriptions-from-xml-comments) | +| | [Provide Global API Metadata](docs/configure-and-customize-swaggergen.md#provide-global-api-metadata) | +| | [Generate Multiple Swagger Documents](docs/configure-and-customize-swaggergen.md#generate-multiple-swagger-documents) | +| | [Omit Obsolete Operations and/or Schema Properties](docs/configure-and-customize-swaggergen.md#omit-obsolete-operations-andor-schema-properties) | +| | [Omit Arbitrary Operations](docs/configure-and-customize-swaggergen.md#omit-arbitrary-operations) | +| | [Customize Operation Tags (e.g. for UI Grouping)](docs/configure-and-customize-swaggergen.md#customize-operation-tags-eg-for-ui-grouping) | +| | [Change Operation Sort Order (e.g. for UI Sorting)](docs/configure-and-customize-swaggergen.md#change-operation-sort-order-eg-for-ui-sorting) | +| | [Customize Schema Ids](docs/configure-and-customize-swaggergen.md#customize-schema-ids) | +| | [Override Schema for Specific Types](docs/configure-and-customize-swaggergen.md#override-schema-for-specific-types) | +| | [Extend Generator with Operation, Schema & Document Filters](docs/configure-and-customize-swaggergen.md#extend-generator-with-operation-schema-and-document-filters) | +| | [Add Security Definitions and Requirements](docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements) | +| | [Add Security Definitions and Requirements for Bearer auth](docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements-for-bearer-authentication) | +| | [Inheritance and Polymorphism](docs/configure-and-customize-swaggergen.md#inheritance-and-polymorphism) | +| **Swashbuckle.AspNetCore.SwaggerUI** | [Change Relative Path to the UI](docs/configure-and-customize-swaggerui.md#change-relative-path-to-the-ui) | +| | [Change Document Title](docs/configure-and-customize-swaggerui.md#change-document-title) | +| | [Change CSS or JS Paths](docs/configure-and-customize-swaggerui.md#change-css-or-js-paths) | +| | [List Multiple Swagger Documents](docs/configure-and-customize-swaggerui.md#list-multiple-swagger-documents) | +| | [Apply swagger-ui Parameters](docs/configure-and-customize-swaggerui.md#apply-swagger-ui-parameters) | +| | [Inject Custom JavaScript](docs/configure-and-customize-swaggerui.md#inject-custom-javascript) | +| | [Inject Custom CSS](docs/configure-and-customize-swaggerui.md#inject-custom-css) | +| | [Customize index.html](docs/configure-and-customize-swaggerui.md#customize-indexhtml) | +| | [Enable OAuth2.0 Flows](docs/configure-and-customize-swaggerui.md#enable-oauth20-flows) | +| | [Use client-side request and response interceptors](docs/configure-and-customize-swaggerui.md#use-client-side-request-and-response-interceptors) | +| **Swashbuckle.AspNetCore.Annotations** | [Install and Enable Annotations](docs/configure-and-customize-annotations.md#install-and-enable-annotations) | +| | [Enrich Operation Metadata](docs/configure-and-customize-annotations.md#enrich-operation-metadata) | +| | [Enrich Response Metadata](docs/configure-and-customize-annotations.md#enrich-response-metadata) | +| | [Enrich Parameter Metadata](docs/configure-and-customize-annotations.md#enrich-parameter-metadata) | +| | [Enrich RequestBody Metadata](docs/configure-and-customize-annotations.md#enrich-requestbody-metadata) | +| | [Enrich Schema Metadata](docs/configure-and-customize-annotations.md#enrich-schema-metadata) | +| | [Apply Schema Filters to Specific Types](docs/configure-and-customize-annotations.md#apply-schema-filters-to-specific-types) | +| | [Add Tag Metadata](docs/configure-and-customize-annotations.md#add-tag-metadata) | +| **Swashbuckle.AspNetCore.Cli** | [Retrieve Swagger Directly from a Startup Assembly](docs/configure-and-customize-cli.md#retrieve-swagger-directly-from-a-startup-assembly) | +| | [Use the CLI Tool with a Custom Host Configuration](docs/configure-and-customize-cli.md#use-the-cli-tool-with-a-custom-host-configuration) | +| **Swashbuckle.AspNetCore.ReDoc** | [Change Relative Path to the UI](docs/configure-and-customize-redoc.md#change-relative-path-to-the-ui) | +| | [Change Document Title](docs/configure-and-customize-redoc.md#change-document-title) | +| | [Apply Redoc Parameters](docs/configure-and-customize-redoc.md#apply-redoc-parameters) | +| | [Inject Custom CSS](docs/configure-and-customize-redoc.md#inject-custom-css) | +| | [Customize index.html](docs/configure-and-customize-redoc.md#customize-indexhtml) | From 4b481a179f2412a24062ae9330535885f373520f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:21:42 +0000 Subject: [PATCH 554/680] Bump the xunit group with 1 update (#3578) Bumps Verify.XunitV3 from 30.16.0 to 30.18.0 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 30.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 05d323108b..6bbca4604c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From ff236ab2cc2294bd282656e6812fca4b42c1a5e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:17:45 +0100 Subject: [PATCH 555/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3580) --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 5490ac213a..11a2a7219d 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.0" + "swagger-ui-dist": "5.29.1" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.0.tgz", - "integrity": "sha512-gqs7Md3AxP4mbpXAq31o5QW+wGUZsUzVatg70yXpUR245dfIis5jAzufBd+UQM/w2xSfrhvA1eqsrgnl2PbezQ==", + "version": "5.29.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.1.tgz", + "integrity": "sha512-qyjpz0qgcomRr41a5Aye42o69TKwCeHM9F8htLGVeUMKekNS6qAqz9oS7CtSvgGJSppSNAYAIh7vrfrSdHj9zw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 687608c626..3dbf46501f 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.0" + "swagger-ui-dist": "5.29.1" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From 4a812b251916fd7834c60cbb1a3d02919159214a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:04:28 +0100 Subject: [PATCH 556/680] Bump version (#3581) * Bump version Bump version to 9.0.6 for the next release. Signed-off-by: github-actions[bot] * Update version table Update for 9.0.5. --------- Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: Martin Costello --- Directory.Build.props | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f458ed0e6d..632c05e9b3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,7 @@ package-readme.md See $(PackageProjectUrl)/releases for details. false - 9.0.4 + 9.0.5 true git $(PackageProjectUrl).git @@ -37,7 +37,7 @@ snupkg true true - 9.0.5 + 9.0.6 false diff --git a/README.md b/README.md index 7a4392e6ed..670f028571 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ for more details. | Swashbuckle Version | ASP.NET Core | Swagger/OpenAPI Spec. | swagger-ui | Redoc | |----------|----------|----------|----------|----------| | [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/master/) | >= 8.0.0 | 2.0, 3.0 | [5.x.x](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [2.x.x](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [9.0.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.4) | >= 8.0.0 | 2.0, 3.0 | [5.27.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.27.1) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | +| [9.0.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.5) | >= 8.0.0 | 2.0, 3.0 | [5.29.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.1) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | 2.0, 3.0 | [5.22.0](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | 2.0, 3.0 | [5.20.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [2.4.0](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | | [6.9.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.9.0) | >= 6.0.0, 2.1.x | 2.0, 3.0 | [5.17.14](https://github.com/swagger-api/swagger-ui/releases/tag/v5.17.14) | [2.1.5](https://github.com/Redocly/redoc/releases/tag/v2.1.5)| From 7bb3a43b0d98a982daa49d4cb0261a8648880e69 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Mon, 29 Sep 2025 10:08:25 +0100 Subject: [PATCH 557/680] Update zizmor (#3582) Updated zizmor from 1.13.0 to 1.14.1. --- .github/workflows/actions-lint.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml index 5f42ab341d..5e5d4ef39e 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/actions-lint.yml @@ -16,7 +16,7 @@ permissions: {} env: FORCE_COLOR: 3 TERM: xterm - ZIZMOR_VERSION: '1.13.0' + ZIZMOR_VERSION: '1.14.1' jobs: lint: @@ -45,8 +45,7 @@ jobs: args: -color - name: Lint workflows with zizmor - uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 + uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 with: persona: pedantic version: ${{ env.ZIZMOR_VERSION }} - From 549e65b14c081283803635bd7bbb0f285d0569a2 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Mon, 29 Sep 2025 11:54:40 +0100 Subject: [PATCH 558/680] Dynamic version badges (#3583) Updated compatibility table with dynamic badge links for swagger-ui and Redoc versions from CI. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 670f028571..ba5c67d7d6 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ for more details. | Swashbuckle Version | ASP.NET Core | Swagger/OpenAPI Spec. | swagger-ui | Redoc | |----------|----------|----------|----------|----------| -| [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/master/) | >= 8.0.0 | 2.0, 3.0 | [5.x.x](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [2.x.x](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/master/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | +| [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/HEAD/) | >= 8.0.0 | 2.0, 3.0 | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | | [9.0.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.5) | >= 8.0.0 | 2.0, 3.0 | [5.29.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.1) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | 2.0, 3.0 | [5.22.0](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | 2.0, 3.0 | [5.20.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [2.4.0](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | From 6d68ce40b888fdd7d8dd80e989cc454ac8a6638e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:48:38 +0000 Subject: [PATCH 559/680] Bump redoc from 2.5.0 to 2.5.1 in /src/Swashbuckle.AspNetCore.ReDoc (#3587) Bumps [redoc](https://github.com/Redocly/redoc) from 2.5.0 to 2.5.1. - [Release notes](https://github.com/Redocly/redoc/releases) - [Changelog](https://github.com/Redocly/redoc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Redocly/redoc/compare/v2.5.0...v2.5.1) --- updated-dependencies: - dependency-name: redoc dependency-version: 2.5.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 44 +++++++++---------- src/Swashbuckle.AspNetCore.ReDoc/package.json | 16 +++---- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json index 6e7ec4e54d..10d8cf399b 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json @@ -8,7 +8,7 @@ "name": "swashbuckle", "version": "1.0.0", "dependencies": { - "redoc": "2.5.0" + "redoc": "2.5.1" } }, "node_modules/@babel/runtime": { @@ -495,9 +495,9 @@ } }, "node_modules/mobx": { - "version": "6.13.5", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.5.tgz", - "integrity": "sha512-/HTWzW2s8J1Gqt+WmUj5Y0mddZk+LInejADc79NJadrWla3rHzmRHki/mnEUH1AvOmbNTZ1BRbKxr8DSgfdjMA==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.15.0.tgz", + "integrity": "sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==", "license": "MIT", "peer": true, "funding": { @@ -506,12 +506,12 @@ } }, "node_modules/mobx-react": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.1.1.tgz", - "integrity": "sha512-gVV7AdSrAAxqXOJ2bAbGa5TkPqvITSzaPiiEkzpW4rRsMhSec7C2NBCJYILADHKp2tzOAIETGRsIY0UaCV5aEw==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.2.0.tgz", + "integrity": "sha512-dkGWCx+S0/1mfiuFfHRH8D9cplmwhxOV5CkXMp38u6rQGG2Pv3FWYztS0M7ncR6TyPRQKaTG/pnitInoYE9Vrw==", "license": "MIT", "dependencies": { - "mobx-react-lite": "^4.0.7" + "mobx-react-lite": "^4.1.0" }, "funding": { "type": "opencollective", @@ -519,7 +519,7 @@ }, "peerDependencies": { "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18" + "react": "^16.8.0 || ^17 || ^18 || ^19" }, "peerDependenciesMeta": { "react-dom": { @@ -531,12 +531,12 @@ } }, "node_modules/mobx-react-lite": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.0.7.tgz", - "integrity": "sha512-RjwdseshK9Mg8On5tyJZHtGD+J78ZnCnRaxeQDSiciKVQDUbfZcXhmld0VMxAwvcTnPEHZySGGewm467Fcpreg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.1.tgz", + "integrity": "sha512-iUxiMpsvNraCKXU+yPotsOncNNmyeS2B5DKL+TL6Tar/xm+wwNJAubJmtRSeAoYawdZqwv8Z/+5nPRHeQxTiXg==", "license": "MIT", "dependencies": { - "use-sync-external-store": "^1.2.0" + "use-sync-external-store": "^1.4.0" }, "funding": { "type": "opencollective", @@ -544,7 +544,7 @@ }, "peerDependencies": { "mobx": "^6.9.0", - "react": "^16.8.0 || ^17 || ^18" + "react": "^16.8.0 || ^17 || ^18 || ^19" }, "peerDependenciesMeta": { "react-dom": { @@ -853,9 +853,9 @@ } }, "node_modules/redoc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.0.tgz", - "integrity": "sha512-NpYsOZ1PD9qFdjbLVBZJWptqE+4Y6TkUuvEOqPUmoH7AKOmPcE+hYjotLxQNTqVoWL4z0T2uxILmcc8JGDci+Q==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.1.tgz", + "integrity": "sha512-LmqA+4A3CmhTllGG197F0arUpmChukAj9klfSdxNRemT9Hr07xXr7OGKu4PHzBs359sgrJ+4JwmOlM7nxLPGMg==", "license": "MIT", "dependencies": { "@redocly/openapi-core": "^1.4.0", @@ -867,7 +867,7 @@ "lunr": "^2.3.9", "mark.js": "^8.11.1", "marked": "^4.3.0", - "mobx-react": "^9.1.1", + "mobx-react": "9.2.0", "openapi-sampler": "^1.5.0", "path-browserify": "^1.0.1", "perfect-scrollbar": "^1.5.5", @@ -1135,12 +1135,12 @@ "license": "BSD" }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/webidl-conversions": { diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package.json b/src/Swashbuckle.AspNetCore.ReDoc/package.json index 726a399b96..5636bd4774 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package.json @@ -1,8 +1,8 @@ -{ - "name": "swashbuckle", - "version": "1.0.0", - "private": true, - "dependencies": { - "redoc": "2.5.0" - } -} +{ + "name": "swashbuckle", + "version": "1.0.0", + "private": true, + "dependencies": { + "redoc": "2.5.1" + } +} From 2d2067af846aba77679deb84c4a133df8e85a584 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:57:05 +0000 Subject: [PATCH 560/680] Bump ossf/scorecard-action from 2.4.2 to 2.4.3 (#3590) Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.4.2 to 2.4.3. - [Release notes](https://github.com/ossf/scorecard-action/releases) - [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md) - [Commits](https://github.com/ossf/scorecard-action/compare/05b42c624433fc40578a4040d5cf5e36ddca8cde...4eaacf0543bb3f2c246792bd56e8cdeffafb205a) --- updated-dependencies: - dependency-name: ossf/scorecard-action dependency-version: 2.4.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 85fd0869d3..0438c85fd0 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -27,7 +27,7 @@ jobs: show-progress: false - name: Run analysis - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: publish_results: true results_file: results.sarif From 21a3668e8982b83d831b696964bae8ad01225179 Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Wed, 1 Oct 2025 11:58:11 +0100 Subject: [PATCH 561/680] Add documentation snippets (#3592) - Use mdsnippets to get code snippets in markdown from compile-validated code. - Use badges for dependency versions in README. - Add dependency of Microsoft.OpenApi to the README. - Put OpenAPI first (and in bold to indicate the default), and 2.0 for Swagger second. --- .config/dotnet-tools.json | 6 + .github/workflows/update-documentation.yml | 96 ++ README.md | 252 ++++-- Swashbuckle.AspNetCore.slnx | 1 + docs/configure-and-customize-annotations.md | 178 +++- docs/configure-and-customize-cli.md | 30 +- docs/configure-and-customize-redoc.md | 62 +- docs/configure-and-customize-swagger.md | 87 +- docs/configure-and-customize-swaggergen.md | 826 ++++++++++++------ docs/configure-and-customize-swaggerui.md | 148 +++- mdsnippets.json | 5 + package-readme.md | 4 +- .../ApiExplorerGetsOnlyConvention.cs | 14 + .../ApiExplorerGroupPerVersionConvention.cs | 16 + .../AuthResponsesOperationFilter.cs | 24 + .../AutoRestSchemaFilter.cs | 26 + test/WebSites/DocumentationSnippets/Circle.cs | 3 + .../CustomDocumentSerializer.cs | 14 + .../DictionaryTKeyEnumTValueSchemaFilter.cs | 32 + .../DocumentationSnippets.csproj | 19 + .../ExampleController.cs | 16 + .../IServiceCollectionExtensions.cs | 274 ++++++ test/WebSites/DocumentationSnippets/Item.cs | 11 + .../DocumentationSnippets/ItemSchemaFilter.cs | 19 + .../DocumentationSnippets/ItemsController.cs | 11 + test/WebSites/DocumentationSnippets/MyEnum.cs | 6 + .../DocumentationSnippets/NewProduct.cs | 13 + .../DocumentationSnippets/PagingOptions.cs | 13 + .../DocumentationSnippets/PhoneNumber.cs | 12 + .../WebSites/DocumentationSnippets/Product.cs | 18 + .../DocumentationSnippets/ProductLine.cs | 24 + .../ProductsController.cs | 192 ++++ .../WebSites/DocumentationSnippets/Program.cs | 18 + .../Properties/launchSettings.json | 12 + .../DocumentationSnippets/Rectangle.cs | 3 + .../SecurityRequirementsOperationFilter.cs | 41 + test/WebSites/DocumentationSnippets/Shape.cs | 11 + .../ShapeWithDiscriminator.cs | 22 + .../SwaggerGenOptionsExtensions.cs | 33 + .../SwaggerHostFactory.cs | 17 + .../TagDescriptionsDocumentFilter.cs | 18 + .../WebApplicationExtensions.cs | 241 +++++ 42 files changed, 2351 insertions(+), 517 deletions(-) create mode 100644 .github/workflows/update-documentation.yml create mode 100644 mdsnippets.json create mode 100644 test/WebSites/DocumentationSnippets/ApiExplorerGetsOnlyConvention.cs create mode 100644 test/WebSites/DocumentationSnippets/ApiExplorerGroupPerVersionConvention.cs create mode 100644 test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/Circle.cs create mode 100644 test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs create mode 100644 test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/DocumentationSnippets.csproj create mode 100644 test/WebSites/DocumentationSnippets/ExampleController.cs create mode 100644 test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs create mode 100644 test/WebSites/DocumentationSnippets/Item.cs create mode 100644 test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/ItemsController.cs create mode 100644 test/WebSites/DocumentationSnippets/MyEnum.cs create mode 100644 test/WebSites/DocumentationSnippets/NewProduct.cs create mode 100644 test/WebSites/DocumentationSnippets/PagingOptions.cs create mode 100644 test/WebSites/DocumentationSnippets/PhoneNumber.cs create mode 100644 test/WebSites/DocumentationSnippets/Product.cs create mode 100644 test/WebSites/DocumentationSnippets/ProductLine.cs create mode 100644 test/WebSites/DocumentationSnippets/ProductsController.cs create mode 100644 test/WebSites/DocumentationSnippets/Program.cs create mode 100644 test/WebSites/DocumentationSnippets/Properties/launchSettings.json create mode 100644 test/WebSites/DocumentationSnippets/Rectangle.cs create mode 100644 test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/Shape.cs create mode 100644 test/WebSites/DocumentationSnippets/ShapeWithDiscriminator.cs create mode 100644 test/WebSites/DocumentationSnippets/SwaggerGenOptionsExtensions.cs create mode 100644 test/WebSites/DocumentationSnippets/SwaggerHostFactory.cs create mode 100644 test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs create mode 100644 test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ff246d94af..1945a747eb 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -7,6 +7,12 @@ "commands": [ "dotnet-validate" ] + }, + "markdownsnippets.tool": { + "version": "27.0.2", + "commands": [ + "mdsnippets" + ] } } } \ No newline at end of file diff --git a/.github/workflows/update-documentation.yml b/.github/workflows/update-documentation.yml new file mode 100644 index 0000000000..7888d43868 --- /dev/null +++ b/.github/workflows/update-documentation.yml @@ -0,0 +1,96 @@ +name: update-documentation + +on: + push: + branches: [ master ] + paths: [ '**/*.md', 'test/WebSites/DocumentationSnippets/**' ] + workflow_dispatch: + +permissions: {} + +jobs: + update-docs: + name: update-docs + runs-on: ubuntu-latest + if: github.event.repository.fork == false + + permissions: + contents: write + pull-requests: write + + steps: + + - name: Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + filter: 'tree:0' + persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits + show-progress: false + + - name: Setup .NET SDK + uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + + - name: Update documentation + id: update-docs + shell: pwsh + env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + GIT_COMMIT_USER_EMAIL: '41898282+github-actions[bot]@users.noreply.github.com' + GIT_COMMIT_USER_NAME: 'github-actions[bot]' + run: | + $ErrorActionPreference = "Stop" + $ProgressPreference = "SilentlyContinue" + + dotnet tool restore + dotnet mdsnippets + + $GitStatus = (git status --porcelain) + if ([string]::IsNullOrEmpty($GitStatus)) { + Write-Output "No changes to commit." + exit 0 + } + + $BranchName = "docs/update-docs" + "branchName=$BranchName" >> ${env:GITHUB_OUTPUT} + + git config user.email ${env:GIT_COMMIT_USER_EMAIL} | Out-Null + git config user.name ${env:GIT_COMMIT_USER_NAME} | Out-Null + git remote set-url "${env:GITHUB_SERVER_URL}/${env:GITHUB_REPOSITORY}.git" | Out-Null + git fetch origin | Out-Null + git rev-parse --verify --quiet ("remotes/origin/" + $BranchName) | Out-Null + + if ($LASTEXITCODE -eq 0) { + Write-Output "Branch $BranchName already exists." + exit 0 + } + + git checkout -b $BranchName + git add . + git commit -m "Update the code-snippets in the documentation" -s + git push -u origin $BranchName + "updated-docs=true" >> ${env:GITHUB_OUTPUT} + + - name: Create pull request + if: steps.update-docs.outputs.updated-docs == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + BASE_BRANCH_NAME: ${{ github.event.repository.default_branch }} + HEAD_BRANCH_NAME: ${{ steps.update-docs.outputs.branchName }} + with: + script: | + const { repo, owner } = context.repo; + const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + const { data: pr } = await github.rest.pulls.create({ + title: 'Update the code-snippets in the documentation', + owner, + repo, + head: process.env.HEAD_BRANCH_NAME, + base: process.env.BASE_BRANCH_NAME, + body: [ + 'This PR updates the code-snippets in the documentation.', + '', + `This pull request was generated by [GitHub Actions](${workflowUrl}).` + ].join('\n') + }); + core.notice(`Created pull request ${owner}/${repo}#${pr.number}: ${pr.html_url}`); diff --git a/README.md b/README.md index ba5c67d7d6..34d7b93581 100644 --- a/README.md +++ b/README.md @@ -9,115 +9,147 @@ [![Help Wanted][help-wanted-badge]][help-wanted-issues] -[Swagger][swagger] tooling for APIs built with ASP.NET Core. +[OpenAPI][swagger] (Swagger) tooling for APIs built with ASP.NET Core. Generate beautiful API documentation, including a UI to explore and test operations, directly from your application code. -In addition to its [Swagger 2.0 and OpenAPI 3.0][swagger-specification] generator, Swashbuckle also provides an embedded -version of the awesome [swagger-ui][swagger-ui] project that's powered by the generated Swagger JSON documents. This means -you can complement your API with living documentation that's always in sync with the latest code. Best of all, it requires -minimal coding and maintenance, allowing you to focus on building an awesome API. +In addition to its [Swagger 2.0 and OpenAPI 3.0/3.1][swagger-specification] generator, Swashbuckle.AspNetCore also provides +an embedded version of the awesome [swagger-ui][swagger-ui] project that's powered by the generated OpenAPI JSON documents. +This means you can complement your API with living documentation that's always in sync with the latest code. Best of all, it +requires minimal coding and maintenance, allowing you to focus on building an awesome API. -But that's not all... +But that's not all! -Once you have an API that can describe itself with a Swagger document, you've opened the treasure chest of Swagger-based +Once you have an API that can describe itself with a OpenAPI document, you've opened the treasure chest of OpenAPI-based tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen][swagger-codegen] for more details. ## Compatibility -| Swashbuckle Version | ASP.NET Core | Swagger/OpenAPI Spec. | swagger-ui | Redoc | -|----------|----------|----------|----------|----------| -| [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/HEAD/) | >= 8.0.0 | 2.0, 3.0 | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [9.0.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.5) | >= 8.0.0 | 2.0, 3.0 | [5.29.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.1) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | -| [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | 2.0, 3.0 | [5.22.0](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [2.5.0](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | -| [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | 2.0, 3.0 | [5.20.1](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [2.4.0](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | -| [6.9.0](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v6.9.0) | >= 6.0.0, 2.1.x | 2.0, 3.0 | [5.17.14](https://github.com/swagger-api/swagger-ui/releases/tag/v5.17.14) | [2.1.5](https://github.com/Redocly/redoc/releases/tag/v2.1.5)| +| Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | +|----------------------------------------------------------------------------------|------------------------|-----------------------|-----------------------|------------|-------| +| [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/HEAD/) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | +| [9.0.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.5) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | +| [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | +| [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | ## Getting Started -1. Install the kitchen-sink NuGet package into your ASP.NET Core application: +First install the kitchen-sink NuGet package into your ASP.NET Core application: - ```terminal - dotnet add package Swashbuckle.AspNetCore - ``` +```terminal +dotnet add package Swashbuckle.AspNetCore +``` -2. Register the Swagger generator in your application's startup path, defining one or more Swagger documents. For example: +Next, register the OpenAPI (Swagger) generator in your application's startup path, defining one or more OpenAPI documents. For example: - ```csharp - using Microsoft.OpenApi.Models; - ``` + + +
+```cs +using Microsoft.OpenApi.Models; - ```csharp - services.AddMvc(); +var builder = WebApplication.CreateBuilder(args); - services.AddSwaggerGen(options => - { - options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); - }); - ``` +builder.Services.AddMvc(); -3. Ensure your API endpoints and any parameters are decorated with `[Http*]` and `[From*]` attributes, where appropriate. +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); +}); - ```csharp - [HttpPost] - public void CreateProduct([FromBody] Product product) - { - // Implementation goes here - } - ``` +var app = builder.Build(); - ```csharp - [HttpGet] - public IEnumerable SearchProducts([FromQuery]string keywords) - { - // Implementation goes here - } - ``` +app.UseSwagger(); + +app.Run(); +``` +snippet source | anchor + + + + +Ensure your API endpoints and any parameters are decorated with `[Http*]` and `[From*]` attributes, where appropriate. + + + + +```cs +[HttpPost] +public void CreateProduct([FromBody] Product product) +{ + // Implementation goes here +} + +[HttpGet] +public IEnumerable SearchProducts([FromQuery] string keywords) +{ + // Implementation goes here + return []; +} +``` +snippet source | anchor + + > [!NOTE] > If you omit the explicit parameter bindings, the generator will describe them as "query" parameters by default. -4. Expose the Swagger/OpenAPI JSON document endpoint(s) using one of following methods: +Then, expose the OpenAPI JSON document endpoint(s) using one of following methods: - - Add endpoints if you're using endpoint-based routing: +- Add endpoints if you're using endpoint-based routing: - ```csharp - app.MapEndpoints(endpoints => - { - // Your own endpoints go here, and then... - endpoints.MapSwagger(); - }); - ``` + + + +```cs +// Your own endpoints go here, and then... +app.MapSwagger(); +``` +snippet source | anchor + + - - Inserting the Swagger/OpenAPI middleware: +- Adding the OpenAPI middleware: - ```csharp - app.UseSwagger(); - ``` + + + +```cs +app.UseSwagger(); +``` +snippet source | anchor + + - At this point, you can spin up your application and view the generated Swagger document at `/swagger/v1/swagger.json`. +At this point, you can launch your application and view the generated OpenAPI document at `/swagger/v1/swagger.json`. -5. Optionally, insert the [swagger-ui][swagger-ui] middleware if you want to expose interactive documentation, specifying the Swagger document(s) to power it from: +Finally, you can optionally add the [swagger-ui][swagger-ui] middleware to expose interactive documentation, specifying the OpenAPI document(s) to power it from: - ```csharp - app.UseSwaggerUI(options => - { - options.SwaggerEndpoint("v1/swagger.json", "My API V1"); - }); - ``` + + + +```cs +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("v1/swagger.json", "My API V1"); +}); +``` +snippet source | anchor + + - Now you can restart your application and check out the auto-generated, interactive documentation at `/swagger`. +Now you can restart your application and view the auto-generated, interactive documentation at `/swagger`. ## System.Text.Json (STJ) vs Newtonsoft.Json (Json.NET) -In versions of Swashbuckle.AspNetCore prior to `5.0.0`, Swashbuckle would generate Schemas (descriptions of the data types exposed by an API) based +In versions of Swashbuckle.AspNetCore prior to `5.0.0`, Swashbuckle.AspNetCore would generate Schemas (descriptions of the data types exposed by an API) based on the behavior of the [Newtonsoft.Json serializer][newtonsoft-json]. This made sense because that was the serializer that shipped with ASP.NET Core at the time. However, since ASP.NET Core 3.0, ASP.NET Core introduces a new serializer, [System.Text.Json (STJ)][system-text-json] out-of-the-box. -If you want to use Newtonsoft.Json instead, you need to install a separate package and explicitly opt-in. By default Swashbuckle.AspNetCore will assume -you're using the System.Text.Json serializer and generate Schemas based on its behavior. If you're using Newtonsoft.Json then you'll need to install a -separate Swashbuckle package, [Swashbuckle.AspNetCore.Newtonsoft][swashbuckle-aspnetcore-newtonsoft] to explicitly opt-in. +If you want to use Newtonsoft.Json instead, you must install a separate package and explicitly opt-in. By default Swashbuckle.AspNetCore will assume +that you're using the System.Text.Json serializer and generate schemas based on its behavior. If you're using Newtonsoft.Json, then you'll need to install a +separate Swashbuckle.AspNetCore package, [Swashbuckle.AspNetCore.Newtonsoft][swashbuckle-aspnetcore-newtonsoft] to explicitly opt-in. Below is an example of how to do this for ASP.NET Core MVC: @@ -125,7 +157,10 @@ Below is an example of how to do this for ASP.NET Core MVC: dotnet add package Swashbuckle.AspNetCore.Newtonsoft ``` -```csharp + + + +```cs services.AddMvc(); services.AddSwaggerGen(options => @@ -135,64 +170,88 @@ services.AddSwaggerGen(options => services.AddSwaggerGenNewtonsoftSupport(); ``` +snippet source | anchor + + ## Swashbuckle, ApiExplorer, and Routing -Swashbuckle relies heavily on `ApiExplorer`, the API metadata layer that ships with ASP.NET Core. If you're using the `AddMvc(...)` helper methods to -bootstrap the MVC stack, then API Explorer will be automatically registered and Swashbuckle will work without issue. +Swashbuckle relies heavily on [`ApiExplorer`][api-explorer], the API metadata layer that ships with ASP.NET Core. If you're using the `AddMvc(...)` +helper methods to bootstrap the MVC stack, then API Explorer will be automatically registered and Swashbuckle.AspNetCore should work without issue. However, if you're using `AddMvcCore(...)` for a more paired-down MVC stack, you'll need to explicitly add the API Explorer services: -```csharp + + + +```cs services.AddMvcCore() .AddApiExplorer(); ``` +snippet source | anchor + + Additionally, if you are using _[conventional routing][conventional-routing]_ (as opposed to attribute routing), any controllers and the actions on those controllers that use conventional routing will not be represented in API Explorer, which means Swashbuckle won't be able to find those controllers and -generate Swagger operations from them. +generate OpenAPI operations for them. For instance: -```csharp + + + +```cs app.UseMvc(routes => { - // SwaggerGen won't find controllers that are routed via this technique. - routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); + // SwaggerGen won't find controllers that are routed via this technique. + routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); }); ``` +snippet source | anchor + + -You **must** use attribute routing for any controllers that you want represented in your Swagger document(s): +You **must** use attribute routing for any controllers that you want represented in your OpenAPI document(s): -```csharp + + + +```cs [Route("example")] public class ExampleController : Controller { [HttpGet("")] - public IActionResult DoStuff() { /* Your implementation */ } + public IActionResult DoStuff() + { + // Your implementation + return Empty; + } } ``` +snippet source | anchor + + Refer to the [ASP.NET Core MVC routing documentation][mvc-routing] for more information. ## Components -Swashbuckle consists of multiple components that can be used together or individually depending on your needs. +Swashbuckle.AspNetCore consists of multiple components that can be used together or individually depending on your needs. -At its core, there's a Swagger generator, middleware to expose Swagger (OpenAPI) documentation as JSON endpoints, and a +At its core, there's an OpenAPI generator, middleware to expose OpenAPI (Swagger) documentation as JSON endpoints, and a packaged version of the [swagger-ui][swagger-ui]. These three packages can be installed with the [`Swashbuckle.AspNetCore`][package-download] -_"metapackage"_ and will work together seamlessly (see [Getting Started](#getting-started)) to provide beautiful API documentation -that is automatically generated from your code. +_"metapackage"_ and will work together (see [Getting Started](#getting-started)) to provide API documentation that is automatically generated from your code. -Additionally, there's add-on packages (CLI tools, [an alternate UI using Redoc][redoc] etc.) that you can optionally install and configure as needed. +Additionally, there are add-on packages (CLI tools, [an alternate UI using Redoc][redoc] etc.) that you can install and configure as needed. ### "Core" Packages | **Package** | **NuGet** | **Description** | |-------------|--------------------|-----------------| -| [Swashbuckle.AspNetCore.Swagger][swashbuckle-aspnetcore-swagger] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Swagger?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swagger] | Exposes Swagger JSON endpoints. It expects an implementation of `ISwaggerProvider` to be registered in the DI container, which it queries to retrieve `OpenApiDocument` instance(s) that are then exposed as serialized JSON. | +| [Swashbuckle.AspNetCore.Swagger][swashbuckle-aspnetcore-swagger] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Swagger?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swagger] | Exposes OpenAPI JSON endpoints. It expects an implementation of `ISwaggerProvider` to be registered in the DI container, which it queries to retrieve `OpenApiDocument` instance(s) that are then exposed as serialized JSON. | | [Swashbuckle.AspNetCore.SwaggerGen][swashbuckle-aspnetcore-swaggergen] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.SwaggerGen?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swaggergen] | Injects an implementation of `ISwaggerProvider` that can be used by the above component. This particular implementation generates `OpenApiDocument` instance(s) from your application endpoints (controllers, minimal endpoints etc.). | -| [Swashbuckle.AspNetCore.SwaggerUI][swashbuckle-aspnetcore-swaggerui] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.SwaggerUI?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swaggerui] | Exposes an embedded version of [swagger-ui][swagger-ui]. You specify the API endpoints where it can obtain Swagger documents from, and it uses them to power interactive documentation for your API. | +| [Swashbuckle.AspNetCore.SwaggerUI][swashbuckle-aspnetcore-swaggerui] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.SwaggerUI?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swaggerui] | Exposes an embedded version of [swagger-ui][swagger-ui]. You specify the API endpoints where it can obtain OpenAPI documents from, and it uses them to power interactive documentation for your API. | ### Additional Packages @@ -208,21 +267,21 @@ These packages are provided by the .NET open-source community. | **Package** | **NuGet** | **Description** | |-------------|--------------------|-----------------| -| [Swashbuckle.AspNetCore.Filters][swashbuckle-aspnetcore-filters] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Filters?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-filters] | Some useful Swashbuckle filters which add additional documentation, e.g. request and response examples, authorization information, etc. See its README for more details. | +| [Swashbuckle.AspNetCore.Filters][swashbuckle-aspnetcore-filters] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Filters?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-filters] | Some useful Swashbuckle.AspNetCore filters which add additional documentation, e.g. request and response examples, authorization information, etc. See its README for more details. | | [Unchase.Swashbuckle.AspNetCore.Extensions][unchase-swashbuckle-aspnetcore-extensions] | [![NuGet](https://img.shields.io/nuget/v/Unchase.Swashbuckle.AspNetCore.Extensions?logo=nuget&label=Latest&color=blue)][unchase-swashbuckle-aspnetcore-extensions] | Some useful extensions (filters), which add additional documentation, e.g. hide `PathItems` for unaccepted roles, fix enumerations for client code generation, etc. See its README for more details. | -| [MicroElements.Swashbuckle.FluentValidation][microelements-swashbuckle-fluentvalidation] | [![NuGet](https://img.shields.io/nuget/v/MicroElements.Swashbuckle.FluentValidation?logo=nuget&label=Latest&color=blue)][microelements-swashbuckle-fluentvalidation] | Use [FluentValidation][fluentvalidation] rules instead of ComponentModel attributes to augment generated Swagger Schemas. | +| [MicroElements.Swashbuckle.FluentValidation][microelements-swashbuckle-fluentvalidation] | [![NuGet](https://img.shields.io/nuget/v/MicroElements.Swashbuckle.FluentValidation?logo=nuget&label=Latest&color=blue)][microelements-swashbuckle-fluentvalidation] | Use [FluentValidation][fluentvalidation] rules instead of ComponentModel attributes to augment generated OpenAPI schemas. | | [MMLib.SwaggerForOcelot][mmlib-swaggerforocelot] | [![NuGet](https://img.shields.io/nuget/v/MMLib.SwaggerForOcelot?logo=nuget&label=Latest&color=blue)][mmlib-swaggerforocelot] | Aggregate documentations over microservices directly on [Ocelot API Gateway][ocelot]. | ## Configuration and Customization -The steps described above will get you up and running with minimal set up. However, Swashbuckle offers a lot of flexibility to customize as you see fit. +The steps described above will get you up and running with minimal set up. However, Swashbuckle.AspNetCore offers a lot of flexibility to customize as you see fit. Check out the table below for the full list of possible configuration options. | **Component** | **Configuration and Customization** | | ------------- | ----------------------------------- | -| **Swashbuckle.AspNetCore.Swagger** | [Change the Path for Swagger JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-swagger-json-endpoints) | -| | [Modify Swagger with Request Context](docs/configure-and-customize-swagger.md#modify-swagger-with-request-context) | +| **Swashbuckle.AspNetCore.Swagger** | [Change the Path for OpenAPI JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-openapi-json-endpoints) | +| | [Modify OpenAPI with Request Context](docs/configure-and-customize-swagger.md#modify-openapi-with-request-context) | | | [Serialize Swagger JSON in the 2.0 format](docs/configure-and-customize-swagger.md#serialize-swagger-in-the-20-format) | | | [Working with Virtual Directories and Reverse Proxies](docs/configure-and-customize-swagger.md#working-with-virtual-directories-and-reverse-proxies) | | | [Customizing how the OpenAPI document is serialized](docs/configure-and-customize-swagger.md#customizing-how-the-openapi-document-is-serialized) | @@ -233,7 +292,7 @@ Check out the table below for the full list of possible configuration options. | | [Handle File Downloads](docs/configure-and-customize-swaggergen.md#handle-file-downloads) | | | [Include Descriptions from XML Comments](docs/configure-and-customize-swaggergen.md#include-descriptions-from-xml-comments) | | | [Provide Global API Metadata](docs/configure-and-customize-swaggergen.md#provide-global-api-metadata) | -| | [Generate Multiple Swagger Documents](docs/configure-and-customize-swaggergen.md#generate-multiple-swagger-documents) | +| | [Generate Multiple OpenAPI Documents](docs/configure-and-customize-swaggergen.md#generate-multiple-openapi-documents) | | | [Omit Obsolete Operations and/or Schema Properties](docs/configure-and-customize-swaggergen.md#omit-obsolete-operations-andor-schema-properties) | | | [Omit Arbitrary Operations](docs/configure-and-customize-swaggergen.md#omit-arbitrary-operations) | | | [Customize Operation Tags (e.g. for UI Grouping)](docs/configure-and-customize-swaggergen.md#customize-operation-tags-eg-for-ui-grouping) | @@ -247,7 +306,7 @@ Check out the table below for the full list of possible configuration options. | **Swashbuckle.AspNetCore.SwaggerUI** | [Change Relative Path to the UI](docs/configure-and-customize-swaggerui.md#change-relative-path-to-the-ui) | | | [Change Document Title](docs/configure-and-customize-swaggerui.md#change-document-title) | | | [Change CSS or JS Paths](docs/configure-and-customize-swaggerui.md#change-css-or-js-paths) | -| | [List Multiple Swagger Documents](docs/configure-and-customize-swaggerui.md#list-multiple-swagger-documents) | +| | [List Multiple OpenAPI Documents](docs/configure-and-customize-swaggerui.md#list-multiple-openapi-documents) | | | [Apply swagger-ui Parameters](docs/configure-and-customize-swaggerui.md#apply-swagger-ui-parameters) | | | [Inject Custom JavaScript](docs/configure-and-customize-swaggerui.md#inject-custom-javascript) | | | [Inject Custom CSS](docs/configure-and-customize-swaggerui.md#inject-custom-css) | @@ -262,7 +321,7 @@ Check out the table below for the full list of possible configuration options. | | [Enrich Schema Metadata](docs/configure-and-customize-annotations.md#enrich-schema-metadata) | | | [Apply Schema Filters to Specific Types](docs/configure-and-customize-annotations.md#apply-schema-filters-to-specific-types) | | | [Add Tag Metadata](docs/configure-and-customize-annotations.md#add-tag-metadata) | -| **Swashbuckle.AspNetCore.Cli** | [Retrieve Swagger Directly from a Startup Assembly](docs/configure-and-customize-cli.md#retrieve-swagger-directly-from-a-startup-assembly) | +| **Swashbuckle.AspNetCore.Cli** | [Retrieve OpenAPI Directly from a Startup Assembly](docs/configure-and-customize-cli.md#retrieve-openapi-directly-from-a-startup-assembly) | | | [Use the CLI Tool with a Custom Host Configuration](docs/configure-and-customize-cli.md#use-the-cli-tool-with-a-custom-host-configuration) | | **Swashbuckle.AspNetCore.ReDoc** | [Change Relative Path to the UI](docs/configure-and-customize-redoc.md#change-relative-path-to-the-ui) | | | [Change Document Title](docs/configure-and-customize-redoc.md#change-document-title) | @@ -272,6 +331,7 @@ Check out the table below for the full list of possible configuration options. +[api-explorer]: https://andrewlock.net/introduction-to-the-apiexplorer-in-asp-net-core/ "Introduction to the ApiExplorer in ASP.NET Core" [build-badge]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions/workflows/build.yml/badge.svg?branch=master&event=push [build-status]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush "Continuous Integration for this project" [conventional-routing]: https://learn.microsoft.com/aspnet/core/mvc/controllers/routing#conventional-routing "Routing to controller actions in ASP.NET Core - Conventional routing" diff --git a/Swashbuckle.AspNetCore.slnx b/Swashbuckle.AspNetCore.slnx index 4cb76b574b..63e77ce466 100644 --- a/Swashbuckle.AspNetCore.slnx +++ b/Swashbuckle.AspNetCore.slnx @@ -65,6 +65,7 @@ + diff --git a/docs/configure-and-customize-annotations.md b/docs/configure-and-customize-annotations.md index b467bdc8c2..1ec8f84c95 100644 --- a/docs/configure-and-customize-annotations.md +++ b/docs/configure-and-customize-annotations.md @@ -2,39 +2,52 @@ ## Install and Enable Annotations -1. Install the following NuGet package into your ASP.NET Core application. +First install the following NuGet package into your ASP.NET Core application. - ```terminal - dotnet add package Swashbuckle.AspNetCore.Annotations - ``` +```terminal +dotnet add package Swashbuckle.AspNetCore.Annotations +``` -2. In your application's startup path, enable annotations within the Swagger configuration callback: +Next in your application's startup path, enable annotations within the Swagger configuration callback: - ```csharp - services.AddSwaggerGen(options => - { - // Other setup, then... - options.EnableAnnotations(); - }); - ``` + + + +```cs +services.AddSwaggerGen(options => +{ + // Other setup, then... + options.EnableAnnotations(); +}); +``` +snippet source | anchor + + ## Enrich Operation Metadata Once annotations have been enabled, you can enrich the generated Operation metadata by decorating actions with `[SwaggerOperation]`. -```csharp + + + +```cs [HttpPost] [SwaggerOperation( Summary = "Creates a new product", Description = "Requires admin privileges", OperationId = "CreateProduct", - Tags = new[] { "Purchase", "Products" } + Tags = ["Purchase", "Products"] )] public IActionResult Create([FromBody] Product product) { //... + return Ok(); } ``` +snippet source | anchor + + ## Enrich Response Metadata @@ -44,65 +57,92 @@ can be returned by an action. These attributes can be combined with XML comments include human-friendly descriptions with each response in the generated document. If you'd prefer to do all of this with a single attribute, and avoid the use of XML comments, you can use one or more `[SwaggerResponse]` instead: -```csharp + + + +```cs [HttpPost] [SwaggerResponse(201, "The product was created", typeof(Product))] [SwaggerResponse(400, "The product data is invalid")] -public IActionResult Create([FromBody] Product product) +public IActionResult Post([FromBody] Product product) { //... + return Created(); } ``` +snippet source | anchor + + ## Enrich Parameter Metadata You can annotate path, query or header-bound parameters or properties (i.e. decorated with `[FromRoute]`, `[FromQuery]` -or `[FromHeader]`) with a `SwaggerParameterAttribute` to enrich the corresponding `Parameter` metadata that's generated by Swashbuckle: +or `[FromHeader]`) with a `SwaggerParameterAttribute` to enrich the corresponding `Parameter` metadata that's generated by Swashbuckle.AspNetCore: -```csharp + + + +```cs [HttpGet] public IActionResult GetProducts( [FromQuery, SwaggerParameter("Search keywords", Required = true)] string keywords) { //... + return Ok(); } ``` +snippet source | anchor + + ## Enrich RequestBody Metadata You can annotate body-bound parameters or properties (i.e. decorated with `[FromBody]`) with `[SwaggerRequestBody]` to enrich -the corresponding `RequestBody` metadata that's generated by Swashbuckle: +the corresponding `RequestBody` metadata that's generated by Swashbuckle.AspNetCore: -```csharp + + + +```cs [HttpPost] -public IActionResult CreateProduct( +public IActionResult SubmitProduct( [FromBody, SwaggerRequestBody("The product payload", Required = true)] Product product) { //... + return Created(); } ``` +snippet source | anchor + + ## Enrich Schema Metadata -You can annotate classes or properties with `[SwaggerSchema]` to enrich the corresponding `Schema` metadata that's generated by Swashbuckle: +You can annotate classes or properties with `[SwaggerSchema]` to enrich the corresponding `Schema` metadata that's generated by Swashbuckle.AspNetCore: -```csharp -[SwaggerSchema(Required = new[] { "Description" })] + + + +```cs +[SwaggerSchema(Required = ["Description"])] public class Product { [SwaggerSchema("The product identifier", ReadOnly = true)] public int Id { get; set; } [SwaggerSchema("The product description")] - public string Description { get; set; } + public string Description { get; set; } = string.Empty; [SwaggerSchema("The date it was created", Format = "date")] public DateTime DateCreated { get; set; } } ``` +snippet source | anchor + + > [!NOTE] -> In Swagger/OpenAPI, serialized objects **and** contained properties are represented as `Schema` instances, hence why this annotation can +> In OpenAPI, serialized objects **and** contained properties are represented as `Schema` instances, hence why this annotation can > be applied to both classes and properties. Also, `required` properties are specified as an array of property names on the top-level schema > as opposed to a flag on each individual property. @@ -114,48 +154,66 @@ customizing **all** generated Schemas. However, there may be cases where it's pr Schema. For example, if you'd like to include an example for a specific type in your API. This can be done by decorating the type with `[SwaggerSchemaFilter]`: -📝 `Product.cs` +📝 `Item.cs` -```csharp -[SwaggerSchemaFilter(typeof(ProductSchemaFilter))] -public class Product + + + +```cs +[SwaggerSchemaFilter(typeof(ItemSchemaFilter))] +public class Item { //... } ``` +snippet source | anchor + + -📝 `ProductSchemaFilter.cs` +📝 `ItemSchemaFilter.cs` -```csharp -public class ProductSchemaFilter : ISchemaFilter + + + +```cs +public class ItemSchemaFilter : ISchemaFilter { public void Apply(OpenApiSchema schema, SchemaFilterContext context) { schema.Example = new OpenApiObject { - [ "Id" ] = new OpenApiInteger(1), - [ "Description" ] = new OpenApiString("An awesome product") + ["Id"] = new OpenApiInteger(1), + ["Description"] = new OpenApiString("An awesome item") }; } } ``` +snippet source | anchor + + ## Add Tag Metadata -By default, the Swagger generator will tag all operations with the controller name for MVC applications. This tag is then -used to drive the operation groupings in swagger-ui. If you'd like to provide a description for each of these groups, you -can do so by adding metadata for each controller name tag using `[SwaggerTag]`: +By default, the OpenAPI generator will tag all operations with the controller name for MVC applications. This tag is then +used to drive the operation groupings in [swagger-ui](https://github.com/swagger-api/swagger-ui). If you'd like to provide +a description for each of these groups, you can do so by adding metadata for each controller name tag using `[SwaggerTag]`: -```csharp -[SwaggerTag("Create, read, update and delete Products")] -public class ProductsController + + + +```cs +[SwaggerTag("Create, read, update and delete Items")] +public class ItemsController { //... } ``` +snippet source | anchor + + > [!NOTE] -> This will add the above description specifically to the tag named `Products`. Therefore, you should avoid using this attribute +> This will add the above description specifically to the tag named `Items`. Therefore, you should avoid using this attribute > if you're tagging Operations with something other than the controller name - e.g. if you're customizing the tagging behavior with `TagActionsBy`. ## List Known Subtypes for Inheritance and Polymorphism @@ -166,22 +224,34 @@ which selects _all_ subtypes in the same assembly as the base type, and therefor 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); }); ``` +snippet source | anchor + + 📝 `Shape.cs` -```csharp + + + +```cs [JsonDerivedType(typeof(Rectangle))] [JsonDerivedType(typeof(Circle))] public abstract class Shape { } ``` +snippet source | anchor + + ## Enrich Polymorphic Base Classes with Discriminator Metadata @@ -191,20 +261,29 @@ then be incorporated into the generated schema definition: 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); }); ``` +snippet source | anchor + + -📝 `Shape.cs` +📝 `ShapeWithDiscriminator.cs` -```csharp + + + +```cs [JsonPolymorphic(TypeDiscriminatorPropertyName = "shapeType")] [JsonDerivedType(typeof(Rectangle), "rectangle")] [JsonDerivedType(typeof(Circle), "circle")] -public abstract class Shape +public abstract class ShapeWithDiscriminator { // Avoid using a JsonPolymorphicAttribute.TypeDiscriminatorPropertyName // that conflicts with a property in your type hierarchy. @@ -218,6 +297,9 @@ public enum ShapeType Rectangle } ``` +snippet source | anchor + + This indicates that the corresponding payload will have a `"shapeType"` property to discriminate between subtypes, and that property will have a value of `"rectangle"` if the payload represents a `Rectangle` type and a value of `"circle"` if it represents a `Circle` type. diff --git a/docs/configure-and-customize-cli.md b/docs/configure-and-customize-cli.md index a560b39ef9..06a8191b87 100644 --- a/docs/configure-and-customize-cli.md +++ b/docs/configure-and-customize-cli.md @@ -1,18 +1,18 @@ # Configuration and Customization of `Swashbuckle.AspNetCore.Cli` -## Retrieve Swagger Directly from a Startup Assembly +## Retrieve OpenAPI Directly from a Startup Assembly -Once your application has been set up with Swashbuckle (see [Getting Started](../README.md#getting-started)), you can -use the Swashbuckle CLI tool to retrieve Swagger/OpenAPI JSON directly from your application's startup assembly, and write -it to a file. This can be useful if you want to incorporate Swagger generation into a CI/CD process, or if you want to serve +Once your application has been set up with Swashbuckle.AspNetCore (see [Getting Started](../README.md#getting-started)), you can +use the Swashbuckle.OpenAPI CLI tool to retrieve OpenAPI JSON documents directly from your application's startup assembly, and write +it to a file. This can be useful if you want to incorporate OpenAPI generation into a CI/CD process, or if you want to serve it from static file at run-time. It's packaged as a [.NET Tool](https://learn.microsoft.com/dotnet/core/tools/global-tools) that can be installed and used via the .NET SDK. > [!WARNING] -> The tool needs to load your Startup DLL and its dependencies at runtime. Therefore, you should use a version of the `dotnet` SDK -> that is compatible with your application. For example, if your app targets `net8.0`, then you should use version 8.0.xxx of the SDK -> to run the CLI tool. +> The tool needs to load your Startup DLL and its dependencies at runtime. Therefore, you should use a version of the `dotnet` CLI +> that is compatible with your application. For example, if your app targets `net10.0`, then you should use version 10.0.xxx of the +> .NET SDK to run the CLI tool. ### Using the tool with the .NET SDK @@ -46,16 +46,16 @@ dotnet tool install -g Swashbuckle.AspNetCore.Cli swagger tofile --help ``` -2. Generate a Swagger/OpenAPI document from your application's startup assembly +2. Generate an OpenAPI document from your application's startup assembly ```terminal swagger tofile --output [output] [startupassembly] [swaggerdoc] ``` Placeholders and their meaning: - * `[output]`: the relative path where the Swagger JSON document will be output to; + * `[output]`: the relative path where the OpenAPI JSON document will be output to; * `[startupassembly]`: the relative path to your application's startup assembly; - * `[swaggerdoc]`: the name of the Swagger document you want to generate, as configured in your application. + * `[swaggerdoc]`: the name of the OpenAPI document you want to generate, as configured in your application. ## Use the CLI Tool with a Custom Host Configuration @@ -71,10 +71,16 @@ will be used to provide a host for the CLI tool to run in. For example, the following class could be used to leverage the same host configuration as your application: -```csharp + + + +```cs public class SwaggerHostFactory { public static IHost CreateHost() - => Program.CreateHostBuilder([]).Build(); + => MyApplication.CreateHostBuilder([]).Build(); } ``` +snippet source | anchor + + diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md index 61d5d332c2..df34563adb 100644 --- a/docs/configure-and-customize-redoc.md +++ b/docs/configure-and-customize-redoc.md @@ -4,30 +4,45 @@ By default, the Redoc UI will be exposed at `/api-docs`. If necessary, you can alter this when enabling the Redoc middleware: -```csharp + + + +```cs app.UseReDoc(options => { - options.RoutePrefix = "docs" + options.RoutePrefix = "docs"; }); ``` +snippet source | anchor + + ## Change Document Title By default, the Redoc UI will have a generic document title. You can alter this when enabling the Redoc middleware: -```csharp + + + +```cs app.UseReDoc(options => { options.DocumentTitle = "My API Docs"; }); ``` +snippet source | anchor + + ## Apply Redoc Parameters Redoc ships with its own set of configuration parameters, all described [in the Redoc documentation][redoc-options]. -In Swashbuckle, most of these are surfaced through the Redoc middleware options: +In Swashbuckle.AspNetCore, most of these are surfaced through the Redoc middleware options: -```csharp + + + +```cs app.UseReDoc(options => { options.SpecUrl("/v1/swagger.json"); @@ -46,6 +61,9 @@ app.UseReDoc(options => options.SortPropsAlphabetically(); }); ``` +snippet source | anchor + + > [!NOTE] > Using `options.SpecUrl("/v1/swagger.json")` multiple times within the same `UseReDoc(...)` will not add multiple URLs. @@ -55,17 +73,26 @@ app.UseReDoc(options => To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying the relative paths in the middleware options: -```csharp + + + +```cs app.UseReDoc(options => { options.InjectStylesheet("/redoc/custom.css"); }); ``` +snippet source | anchor + + It is also possible to modify the theme by using the `AdditionalItems` property. More information can be found [in the Redoc documentation][redoc-options]. -```csharp + + + +```cs app.UseReDoc(options => { options.ConfigObject.AdditionalItems = new Dictionary @@ -74,18 +101,35 @@ app.UseReDoc(options => }; }); ``` +snippet source | anchor + + ## Customize index.html To customize the UI beyond the basic options listed above, you can provide your own version of the Redoc `index.html` page: -```csharp + + + +```cs app.UseReDoc(options => { - options.IndexStream = () => GetType().Assembly + options.IndexStream = () => typeof(Program).Assembly .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource }); ``` +snippet source | anchor + + + +```xml + + + + + +``` > [!TIP] > To get started, you should base your custom `index.html` on the [default version](../src/Swashbuckle.AspNetCore.ReDoc/index.html). diff --git a/docs/configure-and-customize-swagger.md b/docs/configure-and-customize-swagger.md index be2536577b..ef390e0bdd 100644 --- a/docs/configure-and-customize-swagger.md +++ b/docs/configure-and-customize-swagger.md @@ -1,19 +1,25 @@ # Configuration and Customization of `Swashbuckle.AspNetCore.Swagger` -## Change the Path for Swagger JSON Endpoints +## Change the Path for OpenAPI JSON Endpoints -By default, Swagger JSON will be exposed at the following route - `/swagger/{documentName}/swagger.json`. +By default, OpenAPI (Swagger) JSON will be exposed at the following route - `/swagger/{documentName}/swagger.json`. If necessary, you can change this when enabling the Swagger middleware. > [!IMPORTANT] > Custom routes **must** include the `{documentName}` parameter. -```csharp + + + +```cs app.UseSwagger(options => { options.RouteTemplate = "api-docs/{documentName}/swagger.json"; }); ``` +snippet source | anchor + + > [!NOTE] > If you're using the SwaggerUI middleware, you'll also need to update its configuration to reflect the new endpoints: @@ -28,71 +34,92 @@ app.UseSwagger(options => > If you also need to update the relative path that the UI itself is available on, you'll need to follow the instructions > found in [Change Relative Path to the UI](configure-and-customize-swaggerui.md#change-relative-path-to-the-ui). -## Modify Swagger with Request Context +## Modify OpenAPI with Request Context -If you need to set some Swagger metadata based on the current request, you can configure a filter that's executed prior to serializing the document. +If you need to set some OpenAPI metadata based on the current request, you can configure a filter that's executed prior to serializing the document. -```csharp + + + +```cs app.UseSwagger(options => { - options.PreSerializeFilters.Add((swagger, httpReq) => + options.PreSerializeFilters.Add((document, request) => { - swagger.Servers = [new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}" }]; + document.Servers = [new OpenApiServer { Url = $"{request.Scheme}://{request.Host.Value}" }]; }); }); ``` +snippet source | anchor + + -The `OpenApiDocument` and the current `HttpRequest` are both passed to the filter. This provides a lot of flexibility. +The `OpenApiDocument` and the current `HttpRequest` are both passed to the filter, which provides a lot of flexibility. For example, you can add an explicit API server based on the `Host` header (as shown), or you could inspect session information or an `Authorization` header and remove operations from the document based on user permissions. ## Serialize Swagger in the 2.0 format -By default, Swashbuckle will generate and expose Swagger JSON in version 3.0 of the specification, officially called the +By default, Swashbuckle will generate and expose OpenAPI JSON in version 3.0 of the specification, officially called the OpenAPI Specification. However, to support backwards compatibility, you can opt to continue exposing it in the Swagger 2.0 format with the following option: -```csharp + + + +```cs app.UseSwagger(options => { - options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0; + options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0; }); ``` +snippet source | anchor + + ## Working with Virtual Directories and Reverse Proxies Virtual directories and reverse proxies can cause issues for applications that generate links and redirects, particularly if the app returns *absolute* URLs based on the `Host` header and other information from the current request. To avoid these -issues, Swashbuckle uses *relative* URLs where possible, and encourages their use when configuring the SwaggerUI and ReDoc middleware. +issues, Swashbuckle.AspNetCore uses *relative* URLs where possible, and encourages their use when configuring the SwaggerUI +and ReDoc middleware. -For example, to wire up the SwaggerUI middleware, you provide the URL to one or more OpenAPI/Swagger documents. This is the URL -that swagger-ui, a client-side application, will call to retrieve your API metadata. To ensure this works behind virtual directories -and reverse proxies, you should express this relative to the `RoutePrefix` of swagger-ui itself: +For example, to wire up the SwaggerUI middleware, you provide the URL to one or more OpenAPI documents. This is the URL +that [swagger-ui](https://github.com/swagger-api/swagger-ui), a client-side application, will call to retrieve your API metadata. To ensure this works behind +virtual directories and reverse proxies, you should express this relative to the `RoutePrefix` of swagger-ui itself: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.RoutePrefix = "swagger"; options.SwaggerEndpoint("v1/swagger.json", "My API V1"); }); ``` - -> [!NOTE] -> In previous versions of the documentation, you may have seen this expressed as a root-relative link (e.g. `/swagger/v1/swagger.json`). -> This won't work if your app is hosted on an IIS virtual directory or behind a proxy that trims the request path before forwarding. -> If you switch to the *page-relative* syntax shown above, it should work in all cases. +snippet source | anchor + + ## Customizing how the OpenAPI document is serialized -By default, Swashbuckle will serialize the OpenAPI document using the `Serialize*` methods on the OpenAPI document object. If a -customized serialization is desired, it is possible to create a custom document serializer that implements the `ISwaggerDocumentSerializer` interface. This can be set on the `SwaggerOptions` in the service collection using `ConfigureSwagger()`: +By default, Swashbuckle.AspNetCore will serialize the OpenAPI document using the `Serialize*` methods on the OpenAPI document object. If a +customized serialization is desired, it is possible to create a custom document serializer that implements the `ISwaggerDocumentSerializer` interface. +This can be set on the `SwaggerOptions` in the service collection using `ConfigureSwagger()`: -```csharp + + + +```cs services.ConfigureSwagger(options => { - option.SetCustomDocumentSerializer(); + options.SetCustomDocumentSerializer(); }); ``` +snippet source | anchor + + > [!NOTE] > If you plan on using the command line tool to generate OpenAPI specification files, this must be done on the service @@ -100,9 +127,15 @@ services.ConfigureSwagger(options => When the command line tool is not used, it can also be done on the application host: -```csharp + + + +```cs app.UseSwagger(options => { options.SetCustomDocumentSerializer(); }); ``` +snippet source | anchor + + diff --git a/docs/configure-and-customize-swaggergen.md b/docs/configure-and-customize-swaggergen.md index c54718ac75..20f01d5aa3 100644 --- a/docs/configure-and-customize-swaggergen.md +++ b/docs/configure-and-customize-swaggergen.md @@ -2,70 +2,97 @@ ## Assign Explicit OperationIds -In Swagger, operations may be assigned an `operationId`. This ID must be unique among all operations described in the API. +In OpenAPI, operations may be assigned an `operationId`. This ID must be unique among all operations described in the API. Tools and libraries (e.g. client generators) may use the `operationId` to uniquely identify an operation, therefore, it is recommended to follow common programming naming conventions. Auto-generating an ID that matches these requirements, while also providing a name that would be meaningful in client libraries, -is a non-trivial task and thus Swashbuckle omits the `operationId` by default. However, if necessary, you can assign `operationIds` -by decorating individual routes or by providing a custom strategy. +is a non-trivial task and thus Swashbuckle.AspNetCore omits the `operationId` by default. However, if necessary, you can assign +`operationIds` by decorating individual routes or by providing a custom strategy. ### Option 1: Decorate routes with a `Name` property -```csharp + + + +```cs // operationId = "GetProductById" [HttpGet("{id}", Name = "GetProductById")] public IActionResult Get(int id) { // ... + return Ok(); } ``` +snippet source | anchor + + ### Option 2: Provide a custom strategy 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { // Other configuration... - + // Use method name as operationId - options.CustomOperationIds(apiDesc => + options.CustomOperationIds(apiDescription => { - return apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null; + return apiDescription.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null; }); }); ``` +snippet source | anchor + + 📝 `ProductsController.cs` -```csharp + + + +```cs // operationId = "GetProductById" -[HttpGet("{id}")] +[HttpGet("/product/{id}")] public IActionResult GetProductById(int id) { // ... + return Ok(); } ``` +snippet source | anchor + + > [!NOTE] -> With either approach, API authors are responsible for ensuring the uniqueness of `operationIds` across all Operations. +> With either approach, API authors are responsible for ensuring the uniqueness of `operationIds` across all operations. ## List Operation Responses -By default, Swashbuckle will generate a `"200"` response for each operation. If the action returns a response object, then -this will be used to generate a schema for the response body. For example: +By default, Swashbuckle.AspNetCore will generate an HTTP `"200"` response for each operation. If the endpoint returns a +response object, then this will be used to generate a schema for the response body. For example: -```csharp + + + +```cs [HttpPost("{id}")] public Product GetById(int id) { // ... + return new Product(); } ``` +snippet source | anchor + + -Will produce the following response metadata: +This endpoint will produce the following response metadata: ```yaml responses: { @@ -87,18 +114,25 @@ responses: { If you need to specify a different status code and/or additional responses, or your MVC actions return `IActionResult` instead of a response object, you can explicitly describe responses with `[ProducesResponseType]` which is part of ASP.NET Core. For example: -```csharp -[HttpPost("{id}")] + + + +```cs +[HttpPost("product/{id}")] [ProducesResponseType(typeof(Product), 200)] [ProducesResponseType(typeof(IDictionary), 400)] [ProducesResponseType(500)] -public IActionResult GetById(int id) +public IActionResult GetProductInfoById(int id) { // ... + return Ok(); } ``` +snippet source | anchor + + -Will produce the following response metadata: +This endpoint will produce the following response metadata: ```yaml responses: { @@ -134,14 +168,17 @@ responses: { ## Flag Required Parameters and Schema Properties -In a Swagger document, you can flag parameters and schema properties that are required for a request. If a parameter -(top-level or property-based) is decorated with `[BindRequired]` or `[Required]`, then Swashbuckle will automatically -flag it as a `required` parameter in the generated Swagger document: +In an OpenAPI document, you can flag parameters and schema properties that are required for a request. If a parameter +(top-level or property-based) is decorated with `[BindRequired]` or `[Required]`, then Swashbuckle.AspNetCore will automatically +flag it as a `required` parameter in the generated OpenAPI document: 📝 `ProductsController.cs` -```csharp -public IActionResult Search([FromQuery, BindRequired] string keywords, [FromQuery] PagingParams pagingParams) + + + +```cs +public IActionResult Search([FromQuery, BindRequired] string keywords, [FromQuery] PagingOptions paging) { if (!ModelState.IsValid) { @@ -149,28 +186,41 @@ public IActionResult Search([FromQuery, BindRequired] string keywords, [FromQuer } // ... + return Ok(); } ``` +snippet source | anchor + + -📝 `SearchParams.cs` +📝 `PagingOptions.cs` -```csharp -public class PagingParams + + + +```cs +public class PagingOptions { [Required] - public int PageNo { get; set; } + public int PageNumber { get; set; } public int PageSize { get; set; } } ``` +snippet source | anchor + + -In addition to parameters, Swashbuckle will also honor `[Required]` when used in a model that's bound to the request body. +In addition to parameters, Swashbuckle.AspNetCore will also honor `[Required]` when used in a model that's bound to the request body. In this case, the decorated properties will be flagged as `required` properties in the body description: 📝 `ProductsController.cs` -```csharp -public IActionResult Create([FromBody] Product product) + + + +```cs +public IActionResult CreateNewProduct([FromBody] NewProduct product) { if (!ModelState.IsValid) { @@ -178,185 +228,243 @@ public IActionResult Create([FromBody] Product product) } // ... + return Created(); } ``` +snippet source | anchor + + -📝 `Product.cs` +📝 `NewProduct.cs` -```csharp -public class Product + + + +```cs +public class NewProduct { [Required] - public string Name { get; set; } + public string Name { get; set; } = string.Empty; - public string Description { get; set; } + public string Description { get; set; } = string.Empty; } ``` +snippet source | anchor + + ## Handle Forms and File Uploads This MVC controller will accept two form field values and one named file upload from the same form: -```csharp + + + +```cs [HttpPost] public void UploadFile([FromForm] string description, [FromForm] DateTime clientDate, IFormFile file) { // ... } ``` +snippet source | anchor + + > [!IMPORTANT] > As per the [ASP.NET Core documentation](https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads), you're not supposed to > decorate `IFormFile` parameters with the `[FromForm]` attribute as the binding source is automatically inferred from the type. In fact, -> the inferred value is `BindingSource.FormFile` and if you apply the attribute it will be set to `BindingSource.Form` instead, which breaks `ApiExplorer`, the metadata component that ships with ASP.NET Core and is heavily relied on by Swashbuckle. One particular issue here is +> the inferred value is `BindingSource.FormFile` and if you apply the attribute it will be set to `BindingSource.Form` instead, which breaks +> `ApiExplorer`, the metadata component that ships with ASP.NET Core and is heavily relied on by Swashbuckle.AspNetCore. One particular issue here is > that SwaggerUI will not treat the parameter as a file and so will not display a file upload button, if you do mistakenly include this attribute. ## Handle File Downloads > [!IMPORTANT] -> `ApiExplorer` (the ASP.NET Core metadata component that Swashbuckle is built on) **does not** surface the `FileResult` types by -> default and so you need to explicitly tell it to with `[ProducesResponseType]`: - -```csharp -[HttpGet("{fileName}")] +> `ApiExplorer` (the ASP.NET Core metadata component that Swashbuckle.AspNetCore is built on) **does not** surface the `FileResult` types by +> default and so you need to explicitly configure it to do so with `[ProducesResponseType]`: + + + + +```cs +[HttpGet("download/{fileName}")] [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK, "image/jpeg")] -public FileStreamResult GetFile(string fileName) +public FileStreamResult GetImage(string fileName) { // ... + return new FileStreamResult(Stream.Null, "image/jpeg"); } ``` +snippet source | anchor + + ## Include Descriptions from XML Comments -To enhance the generated docs with human-friendly descriptions, you can annotate controller actions and models with -[XML Comments](https://learn.microsoft.com/dotnet/csharp/language-reference/xmldoc/) and configure Swashbuckle to incorporate -those comments into the generated Swagger document. - -1. Open the Properties dialog for your project, click the "Build" tab and ensure that "XML documentation file" is checked, or add an - `true` element to the `` section of your `.csproj` file. This - will produce a file containing all XML comments at build-time. - - > At this point, any classes or methods that are **not** annotated with XML comments will trigger a build warning. To suppress this, - > enter the warning code `1591` into the _"Suppress warnings"_ field in the Properties dialog or add `1591` to a - > `` section of your `.csproj` project file. - -1. Configure Swashbuckle to incorporate the XML comments on file into the generated Swagger JSON: - - ```csharp - services.AddSwaggerGen(options => - { - options.SwaggerDoc( - "v1", - new OpenApiInfo - { - Title = "My API - V1", - Version = "v1" - } - ); - - options.IncludeXmlComments(Assembly.GetExecutingAssembly()); - // or options.IncludeXmlComments(typeof(MyController).Assembly)); - }); - ``` - -1. Annotate your actions with `summary`, `remarks`, `param` and/or `response` tags as desired: - - ```csharp - /// - /// Retrieves a specific product by unique id - /// - /// Awesomeness! - /// The product id - /// Product retrieved - /// Product not found - /// Oops! Can't lookup your product right now - [HttpGet("{id}")] - [ProducesResponseType(typeof(Product), 200)] - [ProducesResponseType(404)] - [ProducesResponseType(500)] - public Product GetById(int id) - { - // ... - } - ``` - -1. Annotate your types with `summary` and `example` tags, other tags (`remarks`, `para`, etc.) are not supported: - - ```csharp - public class Product - { - /// - /// The name of the product - /// - /// Men's basketball shoes - public string Name { get; set; } - - /// - /// Quantity left in stock - /// - /// 10 - public int AvailableStock { get; set; } - - /// - /// The sizes the product is available in - /// - /// ["Small", "Medium", "Large"] - public List Sizes { get; set; } - } - ``` +To enhance the generated docs with human-friendly descriptions, you can annotate endpoints and models with +[XML Comments](https://learn.microsoft.com/dotnet/csharp/language-reference/xmldoc/) and configure Swashbuckle.AspNetCore +to include those comments into the generated OpenAPI document. + +First open the Properties dialog for your project, click the "Build" tab and ensure that "XML documentation file" is checked, or add an +`true` element to a `` in your `.csproj` file. This +will produce a file containing all XML comments at build-time. + +> At this point, any classes or methods that are **not** annotated with XML comments will trigger a build warning. To suppress this, +> enter the warning code `1591` into the _"Suppress warnings"_ field in the Properties dialog or add `$(NoWarn);1591` to a +> `` of your `.csproj` project file. + +Next configure Swashbuckle.AspNetCore to incorporate the XML comments on file into the generated OpenAPI JSON: + + + + +```cs +services.AddSwaggerGen(options => +{ + options.SwaggerDoc( + "v1", + new OpenApiInfo + { + Title = "My API - V1", + Version = "v1" + } + ); + + options.IncludeXmlComments(Assembly.GetExecutingAssembly()); + // or options.IncludeXmlComments(typeof(MyController).Assembly)); +}); +``` +snippet source | anchor + + + +Next annotate your endpoints with `summary`, `remarks`, `param` and/or `response` tags as desired: + + + + +```cs +/// +/// Retrieves a specific product line by unique id +/// +/// Awesomeness! +/// The product line id +/// Product line retrieved +/// Product line not found +/// Oops! Can't lookup your product line right now +[HttpGet("product/{id}")] +[ProducesResponseType(typeof(ProductLine), 200)] +[ProducesResponseType(404)] +[ProducesResponseType(500)] +public ProductLine GetProductBySystemId(int id) +{ + // ... + return new ProductLine(); +} +``` +snippet source | anchor + + -1. Rebuild your project to update the XML Comments file and navigate to the Swagger JSON endpoint. Note how the descriptions are -mapped onto corresponding Swagger fields. +Then annotate your types with `summary` and `example` tags, other tags (`remarks`, `para`, etc.) are not supported: + + + + +```cs +public class ProductLine +{ + /// + /// The name of the product + /// + /// Men's basketball shoes + public string Name { get; set; } = string.Empty; + + /// + /// Quantity left in stock + /// + /// 10 + public int AvailableStock { get; set; } + + /// + /// The sizes the product is available in + /// + /// ["Small", "Medium", "Large"] + public List Sizes { get; set; } = []; +} +``` +snippet source | anchor + + + +Finally, rebuild your project to update the XML Comments file and navigate to the OpenAPI JSON endpoint. Note how the descriptions are +mapped onto corresponding OpenAPI properties. > [!NOTE] -> You can also provide Swagger Schema descriptions by annotating your API models and their properties with `` tags. If you +> You can also provide OpenAPI schema descriptions by annotating your API models and their properties with `` tags. If you > have multiple XML comments files (e.g. separate libraries for controllers and models), you can invoke the `IncludeXmlComments` method -> multiple times and they will all be merged into the generated Swagger document. +> multiple times and they will all be merged into the generated OpenAPI document. ## Provide Global API Metadata -In addition to `"PathItems"`, `"Operations"` and `"Responses"`, which Swashbuckle generates for you, Swagger also supports -[global metadata](https://swagger.io/specification/#oasObject). For example, you can provide a full description for your API, terms +In addition to `"PathItems"`, `"Operations"` and `"Responses"`, which Swashbuckle.AspNetCore generates for you, OpenAPI also supports +[global metadata](https://swagger.io/specification/#openapi-object). For example, you can provide a full description for your API, terms of service or even contact and licensing information: -```csharp -options.SwaggerDoc("v1", - new OpenApiInfo - { - Title = "My API - V1", - Version = "v1", - Description = "A sample API to demo Swashbuckle", - TermsOfService = new Uri("http://tempuri.org/terms"), - Contact = new OpenApiContact - { - Name = "Joe Developer", - Email = "joe.developer@tempuri.org" - }, - License = new OpenApiLicense + + + +```cs +services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", + new OpenApiInfo { - Name = "Apache 2.0", - Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0.html") + Title = "My API - V1", + Version = "v1", + Description = "A sample API to demo Swashbuckle", + TermsOfService = new Uri("http://tempuri.org/terms"), + Contact = new OpenApiContact + { + Name = "Joe Developer", + Email = "joe.developer@tempuri.org" + }, + License = new OpenApiLicense + { + Name = "Apache 2.0", + Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0.html") + } } - } -); + ); +}); ``` +snippet source | anchor + + > [!TIP] > Use IntelliSense to see what other members are available. -## Generate Multiple Swagger Documents +## Generate Multiple OpenAPI Documents With the setup described above, the generator will include all API operations in a single Swagger document. However, you can create multiple documents if necessary. For example, you may want a separate document for each version of your API. To do this, start by defining multiple Swagger documents in your application startup code: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API - V1", Version = "v1" }); options.SwaggerDoc("v2", new OpenApiInfo { Title = "My API - V2", Version = "v2" }); }); ``` +snippet source | anchor + + > [!NOTE] > Take note of the first argument to SwaggerDoc. It **must** be a URI-friendly name that uniquely identifies the document. @@ -372,14 +480,20 @@ ASP.NET Core, to make this distinction. You can set this by decorating individua To include an action in a specific Swagger document, decorate it with `[ApiExplorerSettings]` and set `GroupName` to the corresponding document name (case sensitive): -```csharp + + + +```cs [HttpPost] [ApiExplorerSettings(GroupName = "v2")] -public void Post([FromBody]Product product) +public void PostLine([FromBody] ProductLine product) { // ... } ``` +snippet source | anchor + + ### Assign Actions to Documents by Convention @@ -388,31 +502,38 @@ could wire up the following convention to assign actions to documents based on t 📝 `ApiExplorerGroupPerVersionConvention.cs` -```csharp + + + +```cs public class ApiExplorerGroupPerVersionConvention : IControllerModelConvention { public void Apply(ControllerModel controller) { var controllerNamespace = controller.ControllerType.Namespace; // e.g. "Controllers.V1" - var apiVersion = controllerNamespace.Split('.').Last().ToLower(); + var apiVersion = controllerNamespace?.Split('.').Last().ToLower(); controller.ApiExplorer.GroupName = apiVersion; } } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc(options => - options.Conventions.Add(new ApiExplorerGroupPerVersionConvention()) - ); - - //... -} + + + +```cs +services.AddMvc(options => + options.Conventions.Add(new ApiExplorerGroupPerVersionConvention()) +); ``` +snippet source | anchor + + ### Customize the Action Selection Process @@ -421,7 +542,10 @@ that's surfaced by the framework. The default implementation inspects `ApiDescri or equal to the requested document name. However, you can also provide a custom inclusion predicate. For example, if you're using an attribute-based approach to implement API versioning (e.g. `Microsoft.AspNetCore.Mvc.Versioning`), you could configure a custom predicate that leverages the versioning attributes instead: -```csharp + + + +```cs options.DocInclusionPredicate((docName, apiDesc) => { if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) @@ -429,59 +553,80 @@ options.DocInclusionPredicate((docName, apiDesc) => return false; } - var versions = methodInfo.DeclaringType + var versions = methodInfo.DeclaringType? .GetCustomAttributes(true) .OfType() - .SelectMany(attribute => attribute.Versions); + .SelectMany(attribute => attribute.Versions) ?? []; - return versions.Any(v => $"v{v.ToString()}" == docName); + return versions.Any(version => $"v{version}" == docName); }); ``` +snippet source | anchor + + ### Exposing Multiple Documents through the UI -If you're using the `SwaggerUI` middleware, you'll need to specify any additional Swagger endpoints you want to expose. -See [List Multiple Swagger Documents](configure-and-customize-swaggerui.md#list-multiple-swagger-documents) for more information. +If you're using the `SwaggerUI` middleware, you'll need to specify any additional OpenAPI endpoints you want to expose. +See [List Multiple OpenAPI Documents](configure-and-customize-swaggerui.md#list-multiple-openapi-documents) for more information. ## Omit Obsolete Operations and/or Schema Properties -The [Swagger spec][swagger-specification] includes a `deprecated` flag for indicating that an operation is deprecated -and should be refrained from being used. The Swagger generator will automatically set this flag if the corresponding action is -decorated with `[Obsolete]`. However, instead of setting a flag, you can configure the generator to ignore obsolete actions altogether: +The [OpenAPI specification][swagger-specification] includes a `deprecated` flag for indicating that an operation is deprecated +(obsolete) and should be refrained from being used. The OpenAPI generator will automatically set this flag if the corresponding action is +decorated with the `[Obsolete]` attribute. However, instead of setting a flag, you can configure the generator to ignore obsolete actions altogether: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.IgnoreObsoleteActions(); }); ``` +snippet source | anchor + + -A similar approach can also be used to omit obsolete properties from `Schemas` in the Swagger document. That is, you can decorate -model properties with `[Obsolete]` and configure Swashbuckle to omit those properties when generating JSON Schemas: +A similar approach can also be used to omit obsolete properties from `Schemas` in the OpenAPI document. That is, you can decorate +model properties with `[Obsolete]` and configure Swashbuckle.AspNetCore to omit those properties when generating JSON schemas: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.IgnoreObsoleteProperties(); }); ``` +snippet source | anchor + + ## Omit Arbitrary Operations -You can omit operations from the Swagger output by decorating individual actions or by applying an application-wide convention. +You can omit operations from the OpenAPI output by decorating individual actions or by applying an application-wide convention. ### Decorate Individual Actions -To omit a specific action, decorate it with `[ApiExplorerSettings]` and set the `IgnoreApi` flag: +To omit a specific action, decorate it with `[ApiExplorerSettings]` and set the `IgnoreApi` property to `true`: -```csharp -[HttpGet("{id}")] + + + +```cs +[HttpDelete("{id}")] [ApiExplorerSettings(IgnoreApi = true)] -public Product GetById(int id) +public void Delete(int id) { // ... } ``` +snippet source | anchor + + ### Omit Actions by Convention @@ -490,7 +635,10 @@ could wire up the following convention to only document `GET` operations: 📝 `ApiExplorerGetsOnlyConvention.cs` -```csharp + + + +```cs public class ApiExplorerGetsOnlyConvention : IActionModelConvention { public void Apply(ActionModel action) @@ -499,55 +647,71 @@ public class ApiExplorerGetsOnlyConvention : IActionModelConvention } } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp -public void ConfigureServices(IServiceCollection services) -{ - services.AddMvc(options => - options.Conventions.Add(new ApiExplorerGetsOnlyConvention()) - ); - - //... -} + + + +```cs +services.AddMvc(options => + options.Conventions.Add(new ApiExplorerGetsOnlyConvention()) +); ``` +snippet source | anchor + + ## Customize Operation Tags (e.g. for UI Grouping) -The [Swagger spec][swagger-specification] allows one or more "tags" to be assigned to an operation. The Swagger generator +The [OpenAPI specification][swagger-specification] allows one or more "tags" to be assigned to an operation. The OpenAPI generator will assign the controller name as the default tag. This is important to note if you're using the `SwaggerUI` middleware as it uses this value to group operations. You can override the default tag by providing a function that applies tags by convention. For example, the following configuration will tag, and therefore group operations in the UI, by HTTP method: -```csharp + + + +```cs services.AddSwaggerGen(options => { - options.TagActionsBy(api => api.HttpMethod); + options.TagActionsBy(api => [api.HttpMethod]); }); ``` +snippet source | anchor + + ## Change Operation Sort Order (e.g. for UI Sorting) By default, actions are ordered by assigned tag (see above) before they're grouped into the path-centric, nested structure of the -[Swagger specification][swagger-specification]. But, you can change the default ordering of actions with a custom sorting strategy: +[OpenAPI specification][swagger-specification]. However, you can change the default ordering of actions with a custom sorting strategy: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}"); }); ``` +snippet source | anchor + + > [!NOTE] -> This dictates the sort order **before** actions are grouped and transformed into the Swagger format. Therefore it affects the ordering -> of groups (i.e. Swagger "PathItems"), **and** the ordering of operations within a group, in the Swagger output. +> This dictates the sort order **before** actions are grouped and transformed into the OpenAPI format. Therefore it affects the ordering +> of groups (i.e. OpenAPI "PathItems"), **and** the ordering of operations within a group, in the OpenAPI document that is output. ## Customize Schema Ids -If the generator encounters complex parameter or response types, it will generate a corresponding JSON Schema, add it to the global +If the generator encounters complex parameter or response types, it will generate a corresponding JSON schema, add it to the global `components:schemas` dictionary, and reference it from the operation description by unique Id. For example, if you have an action that returns a `Product` type, then the generated schema will be referenced as follows: @@ -567,59 +731,77 @@ responses: { ``` However, if it encounters multiple types with the same name but different namespaces (e.g. `RequestModels.Product` and `ResponseModels.Product`), -then Swashbuckle will raise an exception due to _"Conflicting schemaIds"_. In this case, you'll need to provide a custom Id strategy that +then Swashbuckle.AspNetCore will raise an exception due to _"Conflicting schemaIds"_. In this case, you'll need to provide a custom Id strategy that further qualifies the name: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.CustomSchemaIds((type) => type.FullName); }); ``` +snippet source | anchor + + > [!NOTE] > See [this GitHub issue](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2703) for support for nested types. ## Override Schema for Specific Types -Out-of-the-box, Swashbuckle performs a best-effort generating JSON Schemas that accurately describe your request and response payloads. +Out-of-the-box, Swashbuckle.AspNetCore performs a best-effort generating JSON schemas that accurately describe your request and response payloads. However, if you're customizing serialization behavior for certain types in your API, you may need to help it out to get accurate output. For example, you might have a class with multiple properties that you want to represent in JSON as a comma-separated string. To do this you -would probably implement a custom `JsonConverter`. In this case, Swashbuckle doesn't know how the converter is implemented and so you would -need to provide it with a Schema that accurately describes the type: +would probably implement a custom `JsonConverter`. In this case, Swashbuckle.AspNetCore doesn't know how the converter is implemented and so you would +need to provide it with a schema that accurately describes the type: 📝 `PhoneNumber.cs` -```csharp + + + +```cs public class PhoneNumber { - public string CountryCode { get; set; } + public string CountryCode { get; set; } = string.Empty; - public string AreaCode { get; set; } + public string AreaCode { get; set; } = string.Empty; - public string SubscriberId { get; set; } + public string SubscriberId { get; set; } = string.Empty; } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.MapType(() => new OpenApiSchema { Type = "string" }); }); ``` +snippet source | anchor + + ## Extend Generator with Operation, Schema and Document Filters -Swashbuckle exposes a filter pipeline that hooks into the generation process. Once generated, individual metadata objects are passed +Swashbuckle.AspNetCore exposes a filter pipeline that hooks into the generation process. Once generated, individual metadata objects are passed into the pipeline where they can be modified further. You can wire up custom filters to enrich the generated `Operations`, `Schemas` and `Documents`. ### Operation Filters -Swashbuckle retrieves an `ApiDescription`, part of ASP.NET Core, for every action and uses it to generate a corresponding `OpenApiOperation`. +Swashbuckle.AspNetCore retrieves an `ApiDescription`, part of ASP.NET Core, for every action and uses it to generate a corresponding `OpenApiOperation`. Once generated, it passes the `OpenApiOperation` and the `ApiDescription` through the list of configured Operation Filters. In a typical filter implementation, you would inspect the `ApiDescription` for relevant information (e.g. route information, action attributes etc.) @@ -628,31 +810,45 @@ are decorated with `[Authorize]`: 📝 `AuthResponsesOperationFilter.cs` -```csharp + + + +```cs public class AuthResponsesOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { - var authAttributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true) + var hasAuthAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(true) .Union(context.MethodInfo.GetCustomAttributes(true)) - .OfType(); + .OfType() + .Any() ?? false; - if (authAttributes.Any()) + if (hasAuthAttributes) { + operation.Responses ??= []; operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); } } } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.OperationFilter(); }); ``` +snippet source | anchor + + > [!NOTE] > Filter pipelines are DI-aware. That is, you can create filters with constructor parameters and if the parameter types @@ -660,7 +856,7 @@ services.AddSwaggerGen(options => ### Schema Filters -Swashbuckle generates a Swagger-flavored [JSONSchema](https://swagger.io/specification/#schemaObject) for every parameter, response +Swashbuckle.AspnetCore generates an OpenAPI-flavored [JSONSchema](https://swagger.io/specification/#schema-object) for every parameter, response and property type that's exposed by your endpoints. Once generated, it passes the schema and type through the list of configured Schema Filters. @@ -669,7 +865,10 @@ to inform the AutoRest tool how enums should be modelled when it generates the A 📝 `AutoRestSchemaFilter.cs` -```csharp + + + +```cs public class AutoRestSchemaFilter : ISchemaFilter { public void Apply(OpenApiSchema schema, SchemaFilterContext context) @@ -689,53 +888,71 @@ public class AutoRestSchemaFilter : ISchemaFilter } } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.SchemaFilter(); }); ``` +snippet source | anchor + + The example below allows for automatic schema generation of generic `Dictionary` objects. -Note that this only generates the swagger; `System.Text.Json` is not able to parse dictionary enums by default, +Note that this only generates the OpenAPI document; `System.Text.Json` is not able to parse dictionary enums by default, so you will need [a special JsonConverter, as shown in the .NET documentation](https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/converters-how-to#sample-factory-pattern-converter). 📝 `DictionaryTKeyEnumTValueSchemaFilter.cs` -```csharp + + + +```cs public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - // Only run for fields that are a Dictionary - if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) + public void Apply(OpenApiSchema schema, SchemaFilterContext context) { - return; - } + // Only run for fields that are a Dictionary + if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) + { + return; + } - var genericArgs = context.Type.GetGenericArguments(); - var keyType = genericArgs[0]; - var valueType = genericArgs[1]; + var genericArgs = context.Type.GetGenericArguments(); + var keyType = genericArgs[0]; + var valueType = genericArgs[1]; - if (!keyType.IsEnum) - { - return; - } + if (!keyType.IsEnum) + { + return; + } - schema.Type = "object"; - schema.Properties = keyType.GetEnumNames().ToDictionary( - name => name, - name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository)); + schema.Type = "object"; + schema.Properties = keyType.GetEnumNames().ToDictionary( + name => name, + name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository)); } } ``` +snippet source | anchor + + 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { // These will be replaced by DictionaryTKeyEnumTValueSchemaFilter, but are needed to avoid @@ -744,16 +961,22 @@ services.AddSwaggerGen(options => options.SchemaFilter(); }); ``` +snippet source | anchor + + ### Document Filters Once an `OpenApiDocument` has been generated, it too can be passed through a set of pre-configured Document Filters. -This gives full control to modify the document however you see fit. To ensure you're still returning valid Swagger JSON, you +This gives full control to modify the document however you see fit. To ensure you're still returning valid OpenAPI JSON, you should have a read through the [specification][swagger-specification] before using this filter type. The example below provides a description for any tags that are assigned to operations in the document: -```csharp + + + +```cs public class TagDescriptionsDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) @@ -766,23 +989,29 @@ public class TagDescriptionsDocumentFilter : IDocumentFilter } } ``` +snippet source | anchor + + > [!NOTE] -> If you're using the `SwaggerUI` middleware, the `TagDescriptionsDocumentFilter` demonstrated above could be used to -> display additional descriptions beside each group of Operations. +> If you're using the `SwaggerUI` middleware, the `TagDescriptionsDocumentFilter` demonstrated above +> could be used to display additional descriptions beside each group of operations. ## Add Security Definitions and Requirements -In Swagger, you can describe how your API is secured by defining one or more security schemes (e.g. Basic, API key, OAuth2 etc.) -and declaring which of those schemes are applicable globally OR for specific operations. For more details, take a look at the -[Security Requirement Object in the Swagger spec](https://swagger.io/specification/#securityRequirementObject). +In OpenAPI, you can describe how your API is secured by defining one or more security schemes (e.g. Basic, API key, OAuth2 etc.) +and declaring which of those schemes are applicable globally or for specific operations. For more details, take a look at the +[Security Requirement Object in the OpenAPI specification](https://swagger.io/specification/#security-requirement-object). -In Swashbuckle, you can define schemes by invoking the `AddSecurityDefinition` method, providing a name and an instance of +In Swashbuckle.AspNetCore, you can define schemes by invoking the `AddSecurityDefinition` method, providing a name and an instance of `OpenApiSecurityScheme`. For example you can define an [OAuth 2.0 - implicit flow](https://oauth.net/2/) as follows: 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { // Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow) @@ -804,6 +1033,9 @@ services.AddSwaggerGen(options => }); }); ``` +snippet source | anchor + + > [!NOTE] > In addition to defining a scheme, you also need to indicate which operations that scheme is applicable to. You can apply schemes @@ -813,7 +1045,10 @@ services.AddSwaggerGen(options => 📝 `Startup.cs` -```csharp + + + +```cs services.AddSwaggerGen(options => { options.AddSecurityRequirement(new OpenApiSecurityRequirement @@ -828,13 +1063,19 @@ services.AddSwaggerGen(options => }); }); ``` +snippet source | anchor + + If you have schemes that are only applicable for certain operations, you can apply them through an Operation filter. For example, the following filter adds OAuth2 requirements based on the presence of the `AuthorizeAttribute`: 📝 `SecurityRequirementsOperationFilter.cs` -```csharp + + + +```cs public class SecurityRequirementsOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) @@ -843,11 +1084,13 @@ public class SecurityRequirementsOperationFilter : IOperationFilter var requiredScopes = context.MethodInfo .GetCustomAttributes(true) .OfType() - .Select(attribute => attribute.Policy) - .Distinct(); + .Select(attribute => attribute.Policy!) + .Distinct() + .ToList(); - if (requiredScopes.Any()) + if (requiredScopes.Count > 0) { + operation.Responses ??= []; operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); @@ -856,17 +1099,20 @@ public class SecurityRequirementsOperationFilter : IOperationFilter Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } }; - operation.Security = new List - { + operation.Security = + [ new OpenApiSecurityRequirement { - [scheme] = [.. requiredScopes] + [scheme] = requiredScopes } - }; + ]; } } } ``` +snippet source | anchor + + > [!NOTE] > If you're using the `SwaggerUI` middleware, you can enable interactive OAuth2.0 flows that are powered by the emitted @@ -874,10 +1120,13 @@ public class SecurityRequirementsOperationFilter : IOperationFilter ## Add Security Definitions and Requirements for Bearer authentication -```csharp + + + +```cs services.AddSwaggerGen(options => { - options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme + options.AddSecurityDefinition("bearer", new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "bearer", @@ -889,29 +1138,32 @@ services.AddSwaggerGen(options => { new OpenApiSecurityScheme { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" } + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } }, - new string[0] + [] } }); }); ``` +snippet source | anchor + + ## Inheritance and Polymorphism -Swagger / OpenAPI defines the `allOf` and `oneOf` keywords for describing -[inheritance and polymorphism](https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/) relationships +OpenAPI defines the `allOf` and `oneOf` keywords for describing +[inheritance and polymorphism](https://swagger.io/docs/specification/v3_0/data-models/inheritance-and-polymorphism/#polymorphism) relationships in schema definitions. For example, if you're using a base class for models that share common properties you can use the `allOf` keyword to describe the inheritance hierarchy. Or, if your serializer supports polymorphic serialization/deserialization, you can use the `oneOf` keyword to document all the "possible" schemas for requests/responses that vary by subtype. ### Enabling Inheritance -By default, Swashbuckle flattens inheritance hierarchies. That is, for derived models, the inherited properties are combined and listed -alongside the declared properties. This can cause a lot of duplication in the generated Swagger, particularly when there's multiple subtypes. +By default, Swashbuckle.AspNetCore flattens inheritance hierarchies. That is, for derived models, the inherited properties are combined and listed +alongside the declared properties. This can cause a lot of duplication in the generated OpenAPI document, particularly when there's multiple subtypes. It's also problematic if you're using a client generator (e.g. NSwag) and would like to maintain the inheritance hierarchy in the generated client models. To work around this, you can apply the `UseAllOfForInheritance` setting, and this will leverage the `allOf` keyword to -incorporate inherited properties by reference in the generated Swagger document: +incorporate inherited properties by reference in the generated OpenAPI document: ```yaml Circle: { @@ -945,19 +1197,19 @@ If your serializer supports polymorphic serialization/deserialization and you wo that accepts/returns abstract base types, you can apply the `UseOneOfForPolymorphism` setting. As a result, the generated request/response schemas will reference a collection of "possible" schemas instead of just the base class schema: -```yaml -requestBody: { - content: { - application/json: { - schema: { - oneOf: [ +```json +"requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ { - $ref: "#/components/schemas/Rectangle" + "$ref": "#/components/schemas/Rectangle" }, { - $ref: "#/components/schemas/Circle" - }, - ], + "$ref": "#/components/schemas/Circle" + } + ] } } } @@ -967,21 +1219,27 @@ requestBody: { ### Detecting Subtypes As inheritance and polymorphism relationships can often become quite complex, not just in your own models but also within the .NET class -library, Swashbuckle is selective about which hierarchies it does and doesn't expose in the generated Swagger document. By default, it will +library, Swashbuckle.AspNetCore is selective about which hierarchies it does and doesn't expose in the generated OpenAPI document. By default, it will pick up any subtypes that are defined in the same assembly as a given base type. If you'd like to override this behavior, you can provide a custom selector method: -```csharp + + + +```cs services.AddSwaggerGen(options => { options.UseAllOfForInheritance(); options.SelectSubTypesUsing(baseType => { - return typeof(Startup).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType)); - }) + return typeof(Program).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType)); + }); }); ``` +snippet source | anchor + + > [!NOTE] > If you're using the [Swashbuckle Annotations library](configure-and-customize-annotations.md#configuration--customization-of-swashbuckleaspnetcoreannotations), it @@ -991,13 +1249,13 @@ services.AddSwaggerGen(options => ### Describing Discriminators -In conjunction with the `oneOf` and/or `allOf` keywords, Swagger/OpenAPI supports a `discriminator` field on base schema definitions. +In conjunction with the `oneOf` and/or `allOf` keywords, OpenAPI supports a `discriminator` field on base schema definitions. This keyword points to the property that identifies the specific type being represented by a given payload. In addition to the property name, the discriminator description may also include a `mapping` which maps discriminator values to specific schema definitions. For example, the Newtonsoft serializer supports polymorphic serialization/deserialization by emitting/accepting a `"$type"` property on JSON instances. The value of this property will be the [assembly qualified type name](https://learn.microsoft.com/dotnet/api/system.type.assemblyqualifiedname) -of the type represented by a given JSON instance. So, to explicitly describe this behavior in Swagger, the corresponding request/response +of the type represented by a given JSON instance. So, to explicitly describe this behavior in OpenAPI, the corresponding request/response schema could be defined as follows: ```yaml @@ -1010,7 +1268,7 @@ components: { type: "object", properties: { $type: { - type": "string" + type: "string" }, discriminator: { propertyName: "$type", @@ -1049,15 +1307,21 @@ a discriminator property, then Swashbuckle will automatically generate the corre Alternatively, if you've customized your serializer to support polymorphic serialization/deserialization, you can provide some custom selector functions to determine the discriminator name and corresponding mapping: -```csharp + + + +```cs services.AddSwaggerGen(options => { - options.UseOneOfForInheritance(); + options.UseAllOfForInheritance(); options.SelectDiscriminatorNameUsing((baseType) => "TypeName"); options.SelectDiscriminatorValueUsing((subType) => subType.Name); }); ``` +snippet source | anchor + + > [!NOTE] > If you're using the [Swashbuckle Annotations library](configure-and-customize-annotations.md#configuration--customization-of-swashbuckleaspnetcoreannotations), it diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md index 608e26e23c..27af2bcac2 100644 --- a/docs/configure-and-customize-swaggerui.md +++ b/docs/configure-and-customize-swaggerui.md @@ -4,59 +4,87 @@ By default, the Swagger UI will be exposed at `/swagger`. If necessary, you can alter this when enabling the SwaggerUI middleware: -```csharp + + + +```cs app.UseSwaggerUI(options => { - options.RoutePrefix = "api-docs" + options.RoutePrefix = "api-docs"; }); ``` +snippet source | anchor + + ## Change Document Title -By default, the Swagger UI will have a generic document title. When you have multiple Swagger pages open, it can be difficult to +By default, the Swagger UI will have a generic document title. When you have multiple OpenAPI documents open, it can be difficult to tell them apart. You can alter this when enabling the SwaggerUI middleware: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.DocumentTitle = "My Swagger UI"; }); ``` +snippet source | anchor + + ## Change CSS or JS Paths -By default, the Swagger UI include default CSS and JavaScript, but if you wish to change the path or URL (for example to use a CDN): +By default, the Swagger UI includes default CSS and JavaScript, but if you wish to change the path or URL (for example to use a CDN) +you can override the defaults as shown below: -```csharp + + + +```cs app.UseSwaggerUI(options => { - options.StylesPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.21.0/swagger-ui.min.css"; - options.ScriptBundlePath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.21.0/swagger-ui-bundle.min.js"; - options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.21.0/swagger-ui-standalone-preset.min.js"; + options.StylesPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui.min.css"; + options.ScriptBundlePath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-bundle.min.js"; + options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js"; }); ``` +snippet source | anchor + + -## List Multiple Swagger Documents +## List Multiple OpenAPI Documents -When enabling the middleware, you're required to specify one or more Swagger endpoints (fully qualified or relative to the UI page) to +When enabling the middleware, you're required to specify one or more OpenAPI endpoints (fully qualified or relative to the UI page) to power the UI. If you provide multiple endpoints, they'll be listed in the top right corner of the page, allowing users to toggle between the different documents. For example, the following configuration could be used to document different versions of an API. -```csharp + + + +```cs app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs"); }); ``` +snippet source | anchor + + ## Apply swagger-ui Parameters -swagger-ui ships with its own set of configuration parameters, all described -[by the swagger-ui Configuration](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#display). -In Swashbuckle, most of these are surfaced through the SwaggerUI middleware options: +[swagger-ui][swagger-ui] ships with its own set of configuration parameters, all described +[by the swagger-ui Configuration](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/configuration.md#display). +In Swashbuckle.AspNetCore, most of these are surfaced through the SwaggerUI middleware options: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.DefaultModelExpandDepth(2); @@ -72,77 +100,96 @@ app.UseSwaggerUI(options => options.MaxDisplayedTags(5); options.ShowExtensions(); options.ShowCommonExtensions(); - options.Plugins = ["myCustomPlugin"]; options.EnableValidator(); options.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Head); options.UseRequestInterceptor("(request) => { return request; }"); options.UseResponseInterceptor("(response) => { return response; }"); }); ``` - - > [!NOTE] - > When adding custom plugins, make sure you add any custom `js` files that define any plugin function(s). +snippet source | anchor + + ## Inject Custom JavaScript To tweak the behavior, you can inject additional JavaScript files by adding them to your `wwwroot` folder and specifying the relative paths in the middleware options: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.InjectJavascript("/swagger-ui/custom.js"); }); ``` - -> [!NOTE] -> The `InjectOnCompleteJavaScript` and `InjectOnFailureJavaScript` options have been removed because the latest version of swagger-ui -> doesn't expose the necessary hooks. Instead, it provides a [flexible customization system](https://github.com/swagger-api/swagger-ui/blob/master/docs/customization/overview.md) -> based on concepts and patterns from React and Redux. To leverage this, you'll need to provide a custom version of `index.html` as -> described [below](#customize-indexhtml). -> -> The [custom index sample app](../test/WebSites/CustomUIIndex/Swagger/index.html) demonstrates this approach, using the swagger-ui plugin -> system provide a custom topbar and to hide the info component. +snippet source | anchor + + ## Inject Custom CSS To tweak the look and feel, you can inject additional CSS stylesheets by adding them to your `wwwroot` folder and specifying the relative paths in the middleware options: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.InjectStylesheet("/swagger-ui/custom.css"); }); ``` +snippet source | anchor + + ## Customize index.html To customize the UI beyond the basic options listed above, you can provide your own version of the swagger-ui `index.html` page: -```csharp + + + +```cs app.UseSwaggerUI(options => { - options.IndexStream = () => GetType().Assembly + options.IndexStream = () => typeof(Program).Assembly .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource }); ``` +snippet source | anchor + + + +```xml + + + + + +``` > [!TIP] -> To get started, you should base your custom `index.html` on the [default version](../src/Swashbuckle.AspNetCore.SwaggerUI/index.html) +> To get started, you should base your custom `index.html` on the [built-in version](../src/Swashbuckle.AspNetCore.SwaggerUI/index.html). ## Enable OAuth2.0 Flows -swagger-ui has built-in support to participate in OAuth2.0 authorization flows. It interacts with authorization and/or token -endpoints, as specified in the Swagger JSON, to obtain access tokens for subsequent API calls. See +[swagger-ui][swagger-ui] has built-in support to participate in OAuth2.0 authorization flows. It interacts with authorization and/or token +endpoints, as specified in the OpenAPI JSON, to obtain access tokens for subsequent API calls. See [Adding Security Definitions and Requirements](configure-and-customize-swaggergen.md#add-security-definitions-and-requirements) for an example of adding OAuth2.0 metadata to the generated Swagger. -If your Swagger endpoint includes the appropriate security metadata, the UI interaction should be automatically enabled. However, you +If your OpenAPI endpoint includes the appropriate security metadata, the UI interaction should be automatically enabled. However, you can further customize OAuth support in the UI with the following settings below. See the -[Swagger-UI documentation](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/oauth2.md) for more information. +[Swagger-UI documentation](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/oauth2.md) for more information. -```csharp + + + +```cs app.UseSwaggerUI(options => { options.OAuthClientId("test-id"); @@ -153,30 +200,47 @@ app.UseSwaggerUI(options => options.OAuth2RedirectUrl("url"); options.OAuthScopeSeparator(" "); options.OAuthScopes("scope1", "scope2"); - options.OAuthAdditionalQueryStringParams(new Dictionary { { "foo", "bar" }}); + options.OAuthAdditionalQueryStringParams(new Dictionary { ["foo"] = "bar" }); options.OAuthUseBasicAuthenticationWithAccessCodeGrant(); options.OAuthUsePkce(); }); ``` +snippet source | anchor + + ## Use client-side request and response interceptors To use custom interceptors on requests and responses going through swagger-ui you can define them as JavaScript functions in the configuration: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.UseRequestInterceptor("(req) => { req.headers['x-my-custom-header'] = 'MyCustomValue'; return req; }"); options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }"); }); ``` +snippet source | anchor + + This can be useful in a range of scenarios where you might want to append local XSRF tokens to all requests, for example: -```csharp + + + +```cs app.UseSwaggerUI(options => { options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }"); }); ``` +snippet source | anchor + + + +[swagger-ui]: https://github.com/swagger-api/swagger-ui diff --git a/mdsnippets.json b/mdsnippets.json new file mode 100644 index 0000000000..b8f535779e --- /dev/null +++ b/mdsnippets.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/refs/tags/27.0.0/schema.json", + "Convention": "InPlaceOverwrite", + "ExcludeDirectories": ["artifacts", ".github"] +} diff --git a/package-readme.md b/package-readme.md index 869ffa6fd7..dbd08929d9 100644 --- a/package-readme.md +++ b/package-readme.md @@ -1,8 +1,8 @@ # Swashbuckle.AspNetCore -[Swagger](https://swagger.io) (OpenAPI) tooling for APIs built with ASP.NET Core. +[OpenAPI](https://swagger.io) (Swagger) tooling for APIs built with ASP.NET Core. -Generate beautiful API documentation, including a UI to explore and test operations, directly from your routes, controllers and models. +Generate beautiful API documentation, including a UI to explore and test operations, directly from your endpoints, routes, controllers and models. [![Build status](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions/workflows/build.yml/badge.svg?branch=master&event=push)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/actions?query=workflow%3Abuild+branch%3Amaster+event%3Apush) [![Code coverage](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore/branch/master/graph/badge.svg)](https://codecov.io/gh/domaindrivendev/Swashbuckle.AspNetCore) diff --git a/test/WebSites/DocumentationSnippets/ApiExplorerGetsOnlyConvention.cs b/test/WebSites/DocumentationSnippets/ApiExplorerGetsOnlyConvention.cs new file mode 100644 index 0000000000..8adff74bbd --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ApiExplorerGetsOnlyConvention.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-IActionModelConvention +public class ApiExplorerGetsOnlyConvention : IActionModelConvention +{ + public void Apply(ActionModel action) + { + action.ApiExplorer.IsVisible = action.Attributes.OfType().Any(); + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ApiExplorerGroupPerVersionConvention.cs b/test/WebSites/DocumentationSnippets/ApiExplorerGroupPerVersionConvention.cs new file mode 100644 index 0000000000..cc213ea449 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ApiExplorerGroupPerVersionConvention.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc.ApplicationModels; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-ControllerModelConvention +public class ApiExplorerGroupPerVersionConvention : IControllerModelConvention +{ + public void Apply(ControllerModel controller) + { + var controllerNamespace = controller.ControllerType.Namespace; // e.g. "Controllers.V1" + var apiVersion = controllerNamespace?.Split('.').Last().ToLower(); + + controller.ApiExplorer.GroupName = apiVersion; + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs b/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs new file mode 100644 index 0000000000..166d338c93 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-AuthResponsesOperationFilter +public class AuthResponsesOperationFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + var hasAuthAttributes = context.MethodInfo.DeclaringType?.GetCustomAttributes(true) + .Union(context.MethodInfo.GetCustomAttributes(true)) + .OfType() + .Any() ?? false; + + if (hasAuthAttributes) + { + operation.Responses ??= []; + operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); + } + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs b/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs new file mode 100644 index 0000000000..4497d60d75 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs @@ -0,0 +1,26 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-AutoRestSchemaFilter +public class AutoRestSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + var type = context.Type; + if (type.IsEnum) + { + schema.Extensions.Add( + "x-ms-enum", + new OpenApiObject + { + ["name"] = new OpenApiString(type.Name), + ["modelAsString"] = new OpenApiBoolean(true) + } + ); + } + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/Circle.cs b/test/WebSites/DocumentationSnippets/Circle.cs new file mode 100644 index 0000000000..a20ab44888 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Circle.cs @@ -0,0 +1,3 @@ +namespace DocumentationSnippets; + +public class Circle; diff --git a/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs b/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs new file mode 100644 index 0000000000..3e85090f3c --- /dev/null +++ b/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs @@ -0,0 +1,14 @@ +using Microsoft.OpenApi; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Writers; +using Swashbuckle.AspNetCore.Swagger; + +namespace DocumentationSnippets; + +public class CustomDocumentSerializer : ISwaggerDocumentSerializer +{ + public void SerializeDocument(OpenApiDocument document, IOpenApiWriter writer, OpenApiSpecVersion specVersion) + { + throw new NotImplementedException(); + } +} diff --git a/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs b/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs new file mode 100644 index 0000000000..08345f37a2 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs @@ -0,0 +1,32 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-DictionaryTKeyEnumTValueSchemaFilter +public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + // Only run for fields that are a Dictionary + if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) + { + return; + } + + var genericArgs = context.Type.GetGenericArguments(); + var keyType = genericArgs[0]; + var valueType = genericArgs[1]; + + if (!keyType.IsEnum) + { + return; + } + + schema.Type = "object"; + schema.Properties = keyType.GetEnumNames().ToDictionary( + name => name, + name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository)); + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/DocumentationSnippets.csproj b/test/WebSites/DocumentationSnippets/DocumentationSnippets.csproj new file mode 100644 index 0000000000..67b7202c4d --- /dev/null +++ b/test/WebSites/DocumentationSnippets/DocumentationSnippets.csproj @@ -0,0 +1,19 @@ + + + + $(DefaultTargetFrameworks) + enable + enable + $(NoWarn);IDE0060 + + + + + + + + + + + + diff --git a/test/WebSites/DocumentationSnippets/ExampleController.cs b/test/WebSites/DocumentationSnippets/ExampleController.cs new file mode 100644 index 0000000000..87478dd831 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ExampleController.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc; + +namespace DocumentationSnippets; + +// begin-snippet: README-AttributeRouting +[Route("example")] +public class ExampleController : Controller +{ + [HttpGet("")] + public IActionResult DoStuff() + { + // Your implementation + return Empty; + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs b/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs new file mode 100644 index 0000000000..1cbb116eb9 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs @@ -0,0 +1,274 @@ +using System.Reflection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +public static class IServiceCollectionExtensions +{ + public static void Configure(this IServiceCollection services) + { + // begin-snippet: README-Newtonsoft.Json + services.AddMvc(); + + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); + }); + + services.AddSwaggerGenNewtonsoftSupport(); + // end-snippet + + // begin-snippet: README-MvcCore + services.AddMvcCore() + .AddApiExplorer(); + // end-snippet + + // begin-snippet: Annotations-Enable + services.AddSwaggerGen(options => + { + // Other setup, then... + options.EnableAnnotations(); + }); + // end-snippet + + // begin-snippet: Annotations-EnablePolymorphism + services.AddSwaggerGen(options => + { + options.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true); + }); + // end-snippet + + // begin-snippet: Swagger-CustomSerializerServices + services.ConfigureSwagger(options => + { + options.SetCustomDocumentSerializer(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-CustomNamingStrategyConfiguration + services.AddSwaggerGen(options => + { + // Other configuration... + + // Use method name as operationId + options.CustomOperationIds(apiDescription => + { + return apiDescription.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null; + }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-ConfigureXmlDocumentation + services.AddSwaggerGen(options => + { + options.SwaggerDoc( + "v1", + new OpenApiInfo + { + Title = "My API - V1", + Version = "v1" + } + ); + + options.IncludeXmlComments(Assembly.GetExecutingAssembly()); + // or options.IncludeXmlComments(typeof(MyController).Assembly)); + }); + // end-snippet + + // begin-snippet: SwaggerGen-GlobalMetadata + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", + new OpenApiInfo + { + Title = "My API - V1", + Version = "v1", + Description = "A sample API to demo Swashbuckle", + TermsOfService = new Uri("http://tempuri.org/terms"), + Contact = new OpenApiContact + { + Name = "Joe Developer", + Email = "joe.developer@tempuri.org" + }, + License = new OpenApiLicense + { + Name = "Apache 2.0", + Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0.html") + } + } + ); + }); + // end-snippet + + // begin-snippet: SwaggerGen-MultipleDocuments + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API - V1", Version = "v1" }); + options.SwaggerDoc("v2", new OpenApiInfo { Title = "My API - V2", Version = "v2" }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-ConfigureControllerModelConvention + services.AddMvc(options => + options.Conventions.Add(new ApiExplorerGroupPerVersionConvention()) + ); + // end-snippet + + // begin-snippet: SwaggerGen-IgnoreObsoleteActions + services.AddSwaggerGen(options => + { + options.IgnoreObsoleteActions(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-IgnoreObsoleteProperties + services.AddSwaggerGen(options => + { + options.IgnoreObsoleteProperties(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-HiddenByConventionConfiguration + services.AddMvc(options => + options.Conventions.Add(new ApiExplorerGetsOnlyConvention()) + ); + // end-snippet + + // begin-snippet: SwaggerGen-CustomTags + services.AddSwaggerGen(options => + { + options.TagActionsBy(api => [api.HttpMethod]); + }); + // end-snippet + + // begin-snippet: SwaggerGen-CustomSorting + services.AddSwaggerGen(options => + { + options.OrderActionsBy((apiDesc) => $"{apiDesc.ActionDescriptor.RouteValues["controller"]}_{apiDesc.HttpMethod}"); + }); + // end-snippet + + // begin-snippet: SwaggerGen-CustomSchemaIds + services.AddSwaggerGen(options => + { + options.CustomSchemaIds((type) => type.FullName); + }); + // end-snippet + + // begin-snippet: SwaggerGen-CustomSchemaMapping + services.AddSwaggerGen(options => + { + options.MapType(() => new OpenApiSchema { Type = "string" }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-ConfigureOperationFilter + services.AddSwaggerGen(options => + { + options.OperationFilter(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-ConfigureSchemaFilter + services.AddSwaggerGen(options => + { + options.SchemaFilter(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-ConfigureSchemaFilterForEnumDictionaryEnum + services.AddSwaggerGen(options => + { + // These will be replaced by DictionaryTKeyEnumTValueSchemaFilter, but are needed to avoid + // an error. You will need one for every kind of Dictionary<,> you have. + options.MapType>>(() => new OpenApiSchema()); + options.SchemaFilter(); + }); + // end-snippet + + // begin-snippet: SwaggerGen-AddSecurityDefinition + services.AddSwaggerGen(options => + { + // Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow) + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri("/auth-server/connect/authorize", UriKind.Relative), + Scopes = new Dictionary + { + ["readAccess"] = "Access read operations", + ["writeAccess"] = "Access write operations" + } + } + } + }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-AddSecurityRequirement + services.AddSwaggerGen(options => + { + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } + }, + ["readAccess", "writeAccess"] + } + }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-BearerAuthentication + services.AddSwaggerGen(options => + { + options.AddSecurityDefinition("bearer", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.Http, + Scheme = "bearer", + BearerFormat = "JWT", + Description = "JWT Authorization header using the Bearer scheme." + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } + }, + [] + } + }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-DetectSubtypes + services.AddSwaggerGen(options => + { + options.UseAllOfForInheritance(); + + options.SelectSubTypesUsing(baseType => + { + return typeof(Program).Assembly.GetTypes().Where(type => type.IsSubclassOf(baseType)); + }); + }); + // end-snippet + + // begin-snippet: SwaggerGen-UseAllOfForInheritance + services.AddSwaggerGen(options => + { + options.UseAllOfForInheritance(); + + options.SelectDiscriminatorNameUsing((baseType) => "TypeName"); + options.SelectDiscriminatorValueUsing((subType) => subType.Name); + }); + // end-snippet + } +} diff --git a/test/WebSites/DocumentationSnippets/Item.cs b/test/WebSites/DocumentationSnippets/Item.cs new file mode 100644 index 0000000000..c04ed740cc --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Item.cs @@ -0,0 +1,11 @@ +using Swashbuckle.AspNetCore.Annotations; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-SchemaModel +[SwaggerSchemaFilter(typeof(ItemSchemaFilter))] +public class Item +{ + //... +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs b/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs new file mode 100644 index 0000000000..316c5729ee --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs @@ -0,0 +1,19 @@ +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-SchemaFilter +public class ItemSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + schema.Example = new OpenApiObject + { + ["Id"] = new OpenApiInteger(1), + ["Description"] = new OpenApiString("An awesome item") + }; + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ItemsController.cs b/test/WebSites/DocumentationSnippets/ItemsController.cs new file mode 100644 index 0000000000..40fad9e9b5 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ItemsController.cs @@ -0,0 +1,11 @@ +using Swashbuckle.AspNetCore.Annotations; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-SwaggerTag +[SwaggerTag("Create, read, update and delete Items")] +public class ItemsController +{ + //... +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/MyEnum.cs b/test/WebSites/DocumentationSnippets/MyEnum.cs new file mode 100644 index 0000000000..1392699b47 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/MyEnum.cs @@ -0,0 +1,6 @@ +namespace DocumentationSnippets; + +public enum MyEnum +{ + None = 0, +} diff --git a/test/WebSites/DocumentationSnippets/NewProduct.cs b/test/WebSites/DocumentationSnippets/NewProduct.cs new file mode 100644 index 0000000000..9283e234e7 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/NewProduct.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-NewProduct +public class NewProduct +{ + [Required] + public string Name { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/PagingOptions.cs b/test/WebSites/DocumentationSnippets/PagingOptions.cs new file mode 100644 index 0000000000..39227cd64d --- /dev/null +++ b/test/WebSites/DocumentationSnippets/PagingOptions.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.DataAnnotations; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-RequiredParametersModel +public class PagingOptions +{ + [Required] + public int PageNumber { get; set; } + + public int PageSize { get; set; } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/PhoneNumber.cs b/test/WebSites/DocumentationSnippets/PhoneNumber.cs new file mode 100644 index 0000000000..e86f453adb --- /dev/null +++ b/test/WebSites/DocumentationSnippets/PhoneNumber.cs @@ -0,0 +1,12 @@ +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-PhoneNumber +public class PhoneNumber +{ + public string CountryCode { get; set; } = string.Empty; + + public string AreaCode { get; set; } = string.Empty; + + public string SubscriberId { get; set; } = string.Empty; +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/Product.cs b/test/WebSites/DocumentationSnippets/Product.cs new file mode 100644 index 0000000000..32849ad883 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Product.cs @@ -0,0 +1,18 @@ +using Swashbuckle.AspNetCore.Annotations; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-SwaggerSchema +[SwaggerSchema(Required = ["Description"])] +public class Product +{ + [SwaggerSchema("The product identifier", ReadOnly = true)] + public int Id { get; set; } + + [SwaggerSchema("The product description")] + public string Description { get; set; } = string.Empty; + + [SwaggerSchema("The date it was created", Format = "date")] + public DateTime DateCreated { get; set; } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ProductLine.cs b/test/WebSites/DocumentationSnippets/ProductLine.cs new file mode 100644 index 0000000000..1ff8f2c2bc --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ProductLine.cs @@ -0,0 +1,24 @@ +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-ClassWithXmlComments +public class ProductLine +{ + /// + /// The name of the product + /// + /// Men's basketball shoes + public string Name { get; set; } = string.Empty; + + /// + /// Quantity left in stock + /// + /// 10 + public int AvailableStock { get; set; } + + /// + /// The sizes the product is available in + /// + /// ["Small", "Medium", "Large"] + public List Sizes { get; set; } = []; +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ProductsController.cs b/test/WebSites/DocumentationSnippets/ProductsController.cs new file mode 100644 index 0000000000..f4f5a70277 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ProductsController.cs @@ -0,0 +1,192 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Swashbuckle.AspNetCore.Annotations; + +namespace DocumentationSnippets; + +public class ProductsController : Controller +{ + // begin-snippet: README-endpoints + [HttpPost] + public void CreateProduct([FromBody] Product product) + { + // Implementation goes here + } + + [HttpGet] + public IEnumerable SearchProducts([FromQuery] string keywords) + { + // Implementation goes here + return []; + } + // end-snippet + + // begin-snippet: Annotations-SwaggerOperation + [HttpPost] + [SwaggerOperation( + Summary = "Creates a new product", + Description = "Requires admin privileges", + OperationId = "CreateProduct", + Tags = ["Purchase", "Products"] + )] + public IActionResult Create([FromBody] Product product) + { + //... + return Ok(); + } + // end-snippet + + // begin-snippet: Annotations-SwaggerResponse + [HttpPost] + [SwaggerResponse(201, "The product was created", typeof(Product))] + [SwaggerResponse(400, "The product data is invalid")] + public IActionResult Post([FromBody] Product product) + { + //... + return Created(); + } + // end-snippet + + // begin-snippet: Annotations-SwaggerParameter + [HttpGet] + public IActionResult GetProducts( + [FromQuery, SwaggerParameter("Search keywords", Required = true)] string keywords) + { + //... + return Ok(); + } + // end-snippet + + // begin-snippet: Annotations-SwaggerRequestBody + [HttpPost] + public IActionResult SubmitProduct( + [FromBody, SwaggerRequestBody("The product payload", Required = true)] Product product) + { + //... + return Created(); + } + // end-snippet + + // begin-snippet: SwaggerGen-NamedRoute + // operationId = "GetProductById" + [HttpGet("{id}", Name = "GetProductById")] + public IActionResult Get(int id) + { + // ... + return Ok(); + } + // end-snippet + + // begin-snippet: SwaggerGen-CustomNamingStrategyEndpoint + // operationId = "GetProductById" + [HttpGet("/product/{id}")] + public IActionResult GetProductById(int id) + { + // ... + return Ok(); + } + // end-snippet + + // begin-snippet: SwaggerGen-ImplicitResponse + [HttpPost("{id}")] + public Product GetById(int id) + { + // ... + return new Product(); + } + // end-snippet + + // begin-snippet: SwaggerGen-ExplicitReponses + [HttpPost("product/{id}")] + [ProducesResponseType(typeof(Product), 200)] + [ProducesResponseType(typeof(IDictionary), 400)] + [ProducesResponseType(500)] + public IActionResult GetProductInfoById(int id) + { + // ... + return Ok(); + } + // end-snippet + + // begin-snippet: SwaggerGen-RequiredParametersEndpoint + public IActionResult Search([FromQuery, BindRequired] string keywords, [FromQuery] PagingOptions paging) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + // ... + return Ok(); + } + // end-snippet + + // begin-snippet: SwaggerGen-RequiredParametersFromBody + public IActionResult CreateNewProduct([FromBody] NewProduct product) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + // ... + return Created(); + } + // end-snippet + + // begin-snippet: SwaggerGen-UploadFile + [HttpPost] + public void UploadFile([FromForm] string description, [FromForm] DateTime clientDate, IFormFile file) + { + // ... + } + // end-snippet + + // begin-snippet: SwaggerGen-DownloadFile + [HttpGet("download/{fileName}")] + [ProducesResponseType(typeof(FileStreamResult), StatusCodes.Status200OK, "image/jpeg")] + public FileStreamResult GetImage(string fileName) + { + // ... + return new FileStreamResult(Stream.Null, "image/jpeg"); + } + // end-snippet + + // begin-snippet: SwaggerGen-EndpointWithXmlComments + /// + /// Retrieves a specific product line by unique id + /// + /// Awesomeness! + /// The product line id + /// Product line retrieved + /// Product line not found + /// Oops! Can't lookup your product line right now + [HttpGet("product/{id}")] + [ProducesResponseType(typeof(ProductLine), 200)] + [ProducesResponseType(404)] + [ProducesResponseType(500)] + public ProductLine GetProductBySystemId(int id) + { + // ... + return new ProductLine(); + } + // end-snippet + + // begin-snippet: SwaggerGen-EndpointGroupName + [HttpPost] + [ApiExplorerSettings(GroupName = "v2")] + public void PostLine([FromBody] ProductLine product) + { + // ... + } + // end-snippet + + // begin-snippet: SwaggerGen-HiddenByAttribute + [HttpDelete("{id}")] + [ApiExplorerSettings(IgnoreApi = true)] + public void Delete(int id) + { + // ... + } + // end-snippet +} diff --git a/test/WebSites/DocumentationSnippets/Program.cs b/test/WebSites/DocumentationSnippets/Program.cs new file mode 100644 index 0000000000..340d5ce89d --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Program.cs @@ -0,0 +1,18 @@ +// begin-snippet: README-configure +using Microsoft.OpenApi.Models; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddMvc(); + +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); +}); + +var app = builder.Build(); + +app.UseSwagger(); + +app.Run(); +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/Properties/launchSettings.json b/test/WebSites/DocumentationSnippets/Properties/launchSettings.json new file mode 100644 index 0000000000..a9b19be55e --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "DocumentationSnippets": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:62136;http://localhost:62137" + } + } +} diff --git a/test/WebSites/DocumentationSnippets/Rectangle.cs b/test/WebSites/DocumentationSnippets/Rectangle.cs new file mode 100644 index 0000000000..998b052977 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Rectangle.cs @@ -0,0 +1,3 @@ +namespace DocumentationSnippets; + +public class Rectangle; diff --git a/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs b/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs new file mode 100644 index 0000000000..6e5bc21460 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs @@ -0,0 +1,41 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-SecurityRequirementsOperationFilter +public class SecurityRequirementsOperationFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + // Policy names map to scopes + var requiredScopes = context.MethodInfo + .GetCustomAttributes(true) + .OfType() + .Select(attribute => attribute.Policy!) + .Distinct() + .ToList(); + + if (requiredScopes.Count > 0) + { + operation.Responses ??= []; + operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); + operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); + + var scheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } + }; + + operation.Security = + [ + new OpenApiSecurityRequirement + { + [scheme] = requiredScopes + } + ]; + } + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/Shape.cs b/test/WebSites/DocumentationSnippets/Shape.cs new file mode 100644 index 0000000000..4a27d50e10 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/Shape.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-Shape +[JsonDerivedType(typeof(Rectangle))] +[JsonDerivedType(typeof(Circle))] +public abstract class Shape +{ +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/ShapeWithDiscriminator.cs b/test/WebSites/DocumentationSnippets/ShapeWithDiscriminator.cs new file mode 100644 index 0000000000..9faa4db49c --- /dev/null +++ b/test/WebSites/DocumentationSnippets/ShapeWithDiscriminator.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace DocumentationSnippets; + +// begin-snippet: Annotations-ShapeWithDiscriminator +[JsonPolymorphic(TypeDiscriminatorPropertyName = "shapeType")] +[JsonDerivedType(typeof(Rectangle), "rectangle")] +[JsonDerivedType(typeof(Circle), "circle")] +public abstract class ShapeWithDiscriminator +{ + // Avoid using a JsonPolymorphicAttribute.TypeDiscriminatorPropertyName + // that conflicts with a property in your type hierarchy. + // Related issue: https://github.com/dotnet/runtime/issues/72170 +} + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum ShapeType +{ + Circle, + Rectangle +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/SwaggerGenOptionsExtensions.cs b/test/WebSites/DocumentationSnippets/SwaggerGenOptionsExtensions.cs new file mode 100644 index 0000000000..f3f3c681df --- /dev/null +++ b/test/WebSites/DocumentationSnippets/SwaggerGenOptionsExtensions.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +public static class SwaggerGenOptionsExtensions +{ + public static void Configure(SwaggerGenOptions options) + { + // begin-snippet: SwaggerGen-DocInclusionPredicate + options.DocInclusionPredicate((docName, apiDesc) => + { + if (!apiDesc.TryGetMethodInfo(out MethodInfo methodInfo)) + { + return false; + } + + var versions = methodInfo.DeclaringType? + .GetCustomAttributes(true) + .OfType() + .SelectMany(attribute => attribute.Versions) ?? []; + + return versions.Any(version => $"v{version}" == docName); + }); + // end-snippet + } +} + +[AttributeUsage(AttributeTargets.Method)] +public class ApiVersionAttribute : Attribute +{ + public List Versions { get;set; } = []; +} diff --git a/test/WebSites/DocumentationSnippets/SwaggerHostFactory.cs b/test/WebSites/DocumentationSnippets/SwaggerHostFactory.cs new file mode 100644 index 0000000000..3fa5f2efb0 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/SwaggerHostFactory.cs @@ -0,0 +1,17 @@ +namespace DocumentationSnippets; + +// begin-snippet: SwaggerHostFactory +public class SwaggerHostFactory +{ + public static IHost CreateHost() + => MyApplication.CreateHostBuilder([]).Build(); +} +// end-snippet + +public static class MyApplication +{ + public static IHostBuilder CreateHostBuilder(string[] args) + { + return WebApplication.CreateBuilder(args).Host; + } +} diff --git a/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs b/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs new file mode 100644 index 0000000000..c9d9806733 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs @@ -0,0 +1,18 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace DocumentationSnippets; + +// begin-snippet: SwaggerGen-TagDescriptionsDocumentFilter +public class TagDescriptionsDocumentFilter : IDocumentFilter +{ + public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) + { + swaggerDoc.Tags = + [ + new OpenApiTag { Name = "Products", Description = "Browse/manage the product catalog" }, + new OpenApiTag { Name = "Orders", Description = "Submit orders" } + ]; + } +} +// end-snippet diff --git a/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs b/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs new file mode 100644 index 0000000000..3181fe7b13 --- /dev/null +++ b/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs @@ -0,0 +1,241 @@ +using Microsoft.OpenApi; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace DocumentationSnippets; + +public static class WebApplicationExtensions +{ + public static void Configure(WebApplication app) + { + // begin-snippet: README-MapSwagger + // Your own endpoints go here, and then... + app.MapSwagger(); + // end-snippet + + // begin-snippet: README-UseSwagger + app.UseSwagger(); + // end-snippet + + // begin-snippet: README-UseSwaggerUI + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("v1/swagger.json", "My API V1"); + }); + // end-snippet + + // begin-snippet: README-MvcConventionalRouting + app.UseMvc(routes => + { + // SwaggerGen won't find controllers that are routed via this technique. + routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-CustomPath + app.UseSwaggerUI(options => + { + options.RoutePrefix = "api-docs"; + }); + // end-snippet + + // begin-snippet: SwaggerUI-CustomDocumentTitle + app.UseSwaggerUI(options => + { + options.DocumentTitle = "My Swagger UI"; + }); + // end-snippet + + // begin-snippet: SwaggerUI-CustomAssets + app.UseSwaggerUI(options => + { + options.StylesPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui.min.css"; + options.ScriptBundlePath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-bundle.min.js"; + options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js"; + }); + // end-snippet + + // begin-snippet: SwaggerUI-MultipleDocuments + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "V1 Docs"); + options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-CustomOptions + app.UseSwaggerUI(options => + { + options.DefaultModelExpandDepth(2); + options.DefaultModelRendering(ModelRendering.Model); + options.DefaultModelsExpandDepth(-1); + options.DisplayOperationId(); + options.DisplayRequestDuration(); + options.DocExpansion(DocExpansion.None); + options.EnableDeepLinking(); + options.EnableFilter(); + options.EnablePersistAuthorization(); + options.EnableTryItOutByDefault(); + options.MaxDisplayedTags(5); + options.ShowExtensions(); + options.ShowCommonExtensions(); + options.EnableValidator(); + options.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Head); + options.UseRequestInterceptor("(request) => { return request; }"); + options.UseResponseInterceptor("(response) => { return response; }"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-JavaScript + app.UseSwaggerUI(options => + { + options.InjectJavascript("/swagger-ui/custom.js"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-CSS + app.UseSwaggerUI(options => + { + options.InjectStylesheet("/swagger-ui/custom.css"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-CustomIndexHtml + app.UseSwaggerUI(options => + { + options.IndexStream = () => typeof(Program).Assembly + .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource + }); + // end-snippet + + // begin-snippet: SwaggerUI-OAuth2 + app.UseSwaggerUI(options => + { + options.OAuthClientId("test-id"); + options.OAuthClientSecret("test-secret"); + options.OAuthUsername("test-user"); + options.OAuthRealm("test-realm"); + options.OAuthAppName("test-app"); + options.OAuth2RedirectUrl("url"); + options.OAuthScopeSeparator(" "); + options.OAuthScopes("scope1", "scope2"); + options.OAuthAdditionalQueryStringParams(new Dictionary { ["foo"] = "bar" }); + options.OAuthUseBasicAuthenticationWithAccessCodeGrant(); + options.OAuthUsePkce(); + }); + // end-snippet + + // begin-snippet: SwaggerUI-Interceptors + app.UseSwaggerUI(options => + { + options.UseRequestInterceptor("(req) => { req.headers['x-my-custom-header'] = 'MyCustomValue'; return req; }"); + options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }"); + }); + // end-snippet + + // begin-snippet: SwaggerUI-Interceptor-XSRF + app.UseSwaggerUI(options => + { + options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }"); + }); + // end-snippet + + // begin-snippet: Redoc-RoutePrefix + app.UseReDoc(options => + { + options.RoutePrefix = "docs"; + }); + // end-snippet + + // begin-snippet: Redoc-DocumentTitle + app.UseReDoc(options => + { + options.DocumentTitle = "My API Docs"; + }); + // end-snippet + + // begin-snippet: Redoc-CustomOptions + app.UseReDoc(options => + { + options.SpecUrl("/v1/swagger.json"); + options.EnableUntrustedSpec(); + options.ScrollYOffset(10); + options.HideHostname(); + options.HideDownloadButton(); + options.ExpandResponses("200,201"); + options.RequiredPropsFirst(); + options.NoAutoAuth(); + options.PathInMiddlePanel(); + options.HideLoading(); + options.NativeScrollbars(); + options.DisableSearch(); + options.OnlyRequiredInSamples(); + options.SortPropsAlphabetically(); + }); + // end-snippet + + // begin-snippet: Redoc-CustomCSS + app.UseReDoc(options => + { + options.InjectStylesheet("/redoc/custom.css"); + }); + // end-snippet + + // begin-snippet: Redoc-ModifyTheme + app.UseReDoc(options => + { + options.ConfigObject.AdditionalItems = new Dictionary + { + // Configured additional options + }; + }); + // end-snippet + + // begin-snippet: Redoc-CustomIndexHtml + app.UseReDoc(options => + { + options.IndexStream = () => typeof(Program).Assembly + .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource + }); + // end-snippet + + // begin-snippet: Swagger-RouteTemplate + app.UseSwagger(options => + { + options.RouteTemplate = "api-docs/{documentName}/swagger.json"; + }); + // end-snippet + + // begin-snippet: Swagger-ModifyWithHttpRequest + app.UseSwagger(options => + { + options.PreSerializeFilters.Add((document, request) => + { + document.Servers = [new OpenApiServer { Url = $"{request.Scheme}://{request.Host.Value}" }]; + }); + }); + // end-snippet + + // begin-snippet: Swagger-Swagger2.0 + app.UseSwagger(options => + { + options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0; + }); + // end-snippet + + // begin-snippet: Swagger-ReverseProxy + app.UseSwaggerUI(options => + { + options.RoutePrefix = "swagger"; + options.SwaggerEndpoint("v1/swagger.json", "My API V1"); + }); + // end-snippet + + // begin-snippet: Swagger-CustomSerializerMiddleware + app.UseSwagger(options => + { + options.SetCustomDocumentSerializer(); + }); + // end-snippet + } +} From 73980b7eaef21c7c5ae5a9f4510b1dedd1285ceb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:17:02 +0000 Subject: [PATCH 562/680] Update the code-snippets in the documentation (#3593) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/configure-and-customize-swaggergen.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configure-and-customize-swaggergen.md b/docs/configure-and-customize-swaggergen.md index 20f01d5aa3..6310c3ec27 100644 --- a/docs/configure-and-customize-swaggergen.md +++ b/docs/configure-and-customize-swaggergen.md @@ -647,7 +647,7 @@ public class ApiExplorerGetsOnlyConvention : IActionModelConvention } } ``` -snippet source | anchor +snippet source | anchor From e949756c4a3b0c9271bf696268beee0d9f57dbbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:17:28 +0000 Subject: [PATCH 563/680] Bump github/codeql-action from 3.30.3 to 3.30.5 (#3589) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.3 to 3.30.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/192325c86100d080feab897ff886c34abd4c83a3...3599b3baa15b485a2e49ef411a7a4bb2452e7f93) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 89a15dd0bd..6fc4f1f838 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 0438c85fd0..68abdd1973 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 with: sarif_file: results.sarif From 6ab751ec2e970a78cb44971039f64ab3bdfb8bfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:18:25 +0000 Subject: [PATCH 564/680] Bump the xunit group with 4 updates (#3591) Bumps Verify.XunitV3 from 30.18.0 to 30.19.1 Bumps xunit.runner.visualstudio from 3.1.4 to 3.1.5 Bumps xunit.v3 from 3.0.1 to 3.1.0 Bumps xunit.v3.extensibility.core from 3.0.1 to 3.1.0 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 30.19.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit - dependency-name: xunit.runner.visualstudio dependency-version: 3.1.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: xunit - dependency-name: xunit.v3 dependency-version: 3.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit - dependency-name: xunit.v3.extensibility.core dependency-version: 3.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 6bbca4604c..ac3e6974c3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,10 +22,10 @@ - + - - - + + + From 1fd2619b96414db858cd795a17e9b1a009b5790f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:19:29 +0000 Subject: [PATCH 565/680] Bump actions/dependency-review-action from 4.7.3 to 4.8.0 (#3588) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.7.3 to 4.8.0. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/595b5aeba73380359d98a5e087f648dbb0edce1b...56339e523c0409420f6c2c9a2f4292bbb3c07dd3) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 4247a9afbe..4e3ef20425 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -23,6 +23,6 @@ jobs: show-progress: false - name: Review dependencies - uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3 + uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 with: allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' From 1070368afe936b06bd4579053ea228f0fd7bce33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:06:31 +0100 Subject: [PATCH 566/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3595) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.29.1 to 5.29.2. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.29.1...v5.29.2) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.29.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 11a2a7219d..3b24a22ba9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.1" + "swagger-ui-dist": "5.29.2" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.1.tgz", - "integrity": "sha512-qyjpz0qgcomRr41a5Aye42o69TKwCeHM9F8htLGVeUMKekNS6qAqz9oS7CtSvgGJSppSNAYAIh7vrfrSdHj9zw==", + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.2.tgz", + "integrity": "sha512-MbnWUJT5Qm3eFOZUKNEXwY987n99U121dN0VGezL/yuxYr13ytu1yMWX1Cg2iqJyWwZfWtWyr0pC2Sp+KYYzTw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 3dbf46501f..36687ee4a6 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.1" + "swagger-ui-dist": "5.29.2" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From a955418126f19a8f8dc0b4f0afc9e713a943cee6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:27:04 +0000 Subject: [PATCH 567/680] Bump version (#3596) * Bump version Bump version to 9.0.7 for the next release. Signed-off-by: github-actions[bot] * Update version table Add details for 9.0.6. --------- Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: Martin Costello --- Directory.Build.props | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 632c05e9b3..9bd7c66fa0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,7 @@ package-readme.md See $(PackageProjectUrl)/releases for details. false - 9.0.5 + 9.0.6 true git $(PackageProjectUrl).git @@ -37,7 +37,7 @@ snupkg true true - 9.0.6 + 9.0.7 false diff --git a/README.md b/README.md index 34d7b93581..0228e78483 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ for more details. | Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | |----------------------------------------------------------------------------------|------------------------|-----------------------|-----------------------|------------|-------| | [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/HEAD/) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [9.0.5](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.5) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.5%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | +| [9.0.6](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.6) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | | [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | From 988e08ea0c6e4452a08de2cd9981e09123093c6b Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Thu, 2 Oct 2025 12:09:07 +0100 Subject: [PATCH 568/680] Update README badges (#3597) Refactor the badges to try and make it so there's less to manually maintain when there's a new release. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0228e78483..7515b101b6 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,12 @@ for more details. ## Compatibility -| Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | -|----------------------------------------------------------------------------------|------------------------|-----------------------|-----------------------|------------|-------| -| [CI](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/commits/HEAD/) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [9.0.6](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v9.0.6) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | -| [8.1.4](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | -| [7.3.2](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/tree/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | +| Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | +|---------------------|------------------------|-----------------------|-----------------------|------------|-------| +| [![CI Swashbuckle.AspNetCore version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Build.props&query=(%2F%2FProject%2FPropertyGroup%2FVersionPrefix)%5B1%5D&prefix=v&suffix=-*&logo=github&label=CI)](https://www.myget.org/gallery/domaindrivendev) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | +| [![Latest v9 Swashbuckle.AspNetCore version](https://img.shields.io/github/v/release/domaindrivendev/Swashbuckle.AspNetCore?display_name=tag&logo=github&label=v9)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/latest) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | +| [![Last v8 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v8-v8.1.4-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | +| [![Last v7 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v7-v7.3.2-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | ## Getting Started From d2e8b0f5649fd8150fd3649d0011ecf57a62aece Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:13:38 +0100 Subject: [PATCH 569/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3598) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.29.2 to 5.29.3. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.29.2...v5.29.3) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.29.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 3b24a22ba9..3dc7acd079 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.2" + "swagger-ui-dist": "5.29.3" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.2.tgz", - "integrity": "sha512-MbnWUJT5Qm3eFOZUKNEXwY987n99U121dN0VGezL/yuxYr13ytu1yMWX1Cg2iqJyWwZfWtWyr0pC2Sp+KYYzTw==", + "version": "5.29.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.3.tgz", + "integrity": "sha512-U99f/2YocRA2Mxqx3eUBRhQonWVtE5dIvMs0Zlsn4a4ip8awMq0JxXhU+Sidtna2WlZrHbK2Rro3RZvYUymRbA==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 36687ee4a6..13af417318 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.2" + "swagger-ui-dist": "5.29.3" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From a944e7b7685da7640a99eb80ec650e69365fb10f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:14:10 +0100 Subject: [PATCH 570/680] Bump github/codeql-action from 3.30.5 to 4.30.7 (#3599) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.5 to 4.30.7. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/3599b3baa15b485a2e49ef411a7a4bb2452e7f93...e296a935590eb16afc0c0108289f68c87e2a89a5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.30.7 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6fc4f1f838..51aabb723b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 + uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 + uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 68abdd1973..c98e8bcc38 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5 + uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: sarif_file: results.sarif From 342043b754a65e47ffeadfa5ab3ff9bac27af772 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:14:33 +0100 Subject: [PATCH 571/680] Bump actions/stale from 10.0.0 to 10.1.0 (#3600) Bumps [actions/stale](https://github.com/actions/stale) from 10.0.0 to 10.1.0. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/3a9db7e6a41a89f618792c92c0e97cc736e1b13f...5f858e3efba33a5ca4407a664cc011ad407f2008) --- updated-dependencies: - dependency-name: actions/stale dependency-version: 10.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index d7a0e87494..3af09c8818 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -17,7 +17,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 + - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 with: days-before-close: 14 days-before-stale: 60 From b00b49364da400ffa288e1911d192ffbcc6a8385 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Oct 2025 09:22:17 +0000 Subject: [PATCH 572/680] Bump the xunit group with 1 update (#3601) Bumps Verify.XunitV3 from 30.19.1 to 30.20.1 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 30.20.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ac3e6974c3..5bb5d9e214 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From ebf3d31694d480d205b406863cedc5d4d126774f Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 10 Oct 2025 10:28:38 +0100 Subject: [PATCH 573/680] Extend NuGet package validation - Use Meziantou.Framework.NuGetPackageValidation.Tool to extend NuGet package validation. - Use consistent PowerShell environment variable syntax. --- .config/dotnet-tools.json | 13 +++++++++++-- .github/bump-version.ps1 | 2 +- .github/workflows/build.yml | 23 +++++++++++++++++++++-- .github/workflows/bump-version.yml | 6 +++--- .github/workflows/release.yml | 2 +- build.ps1 | 2 +- 6 files changed, 38 insertions(+), 10 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1945a747eb..2b11c94661 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -6,13 +6,22 @@ "version": "0.0.1-preview.537", "commands": [ "dotnet-validate" - ] + ], + "rollForward": false }, "markdownsnippets.tool": { "version": "27.0.2", "commands": [ "mdsnippets" - ] + ], + "rollForward": false + }, + "meziantou.framework.nugetpackagevalidation.tool": { + "version": "1.0.31", + "commands": [ + "meziantou.validate-nuget-package" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.github/bump-version.ps1 b/.github/bump-version.ps1 index ec51eece82..8f08c7afa6 100644 --- a/.github/bump-version.ps1 +++ b/.github/bump-version.ps1 @@ -48,7 +48,7 @@ try { $writer = $null } -$githubOutput = $env:GITHUB_OUTPUT +$githubOutput = ${env:GITHUB_OUTPUT} if (($null -ne $githubOutput) -and (Test-Path $githubOutput)) { "version=${updatedVersion}" >> $githubOutput diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc91e876e7..835c743b26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: outputs: dotnet-sdk-version: ${{ steps.setup-dotnet.outputs.dotnet-version }} dotnet-validate-version: ${{ steps.get-dotnet-tools-versions.outputs.dotnet-validate-version }} + nuget-package-validation-version: ${{ steps.get-dotnet-tools-versions.outputs.nuget-package-validation-version }} package-names: ${{ steps.build.outputs.package-names }} package-version: ${{ steps.build.outputs.package-version }} @@ -148,7 +149,9 @@ jobs: run: | $manifest = (Get-Content "./.config/dotnet-tools.json" | Out-String | ConvertFrom-Json) $dotnetValidateVersion = $manifest.tools.'dotnet-validate'.version - "dotnet-validate-version=${dotnetValidateVersion}" >> $env:GITHUB_OUTPUT + $nugetPackageValidationVersion = $manifest.tools.'meziantou.framework.nugetpackagevalidation.tool'.version + "dotnet-validate-version=${dotnetValidateVersion}" >> ${env:GITHUB_OUTPUT} + "nuget-package-validation-version=${nugetPackageValidationVersion}" >> ${env:GITHUB_OUTPUT} validate-packages: needs: build @@ -165,17 +168,33 @@ jobs: with: dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} - - name: Validate NuGet packages + - name: Install NuGet package validation tools shell: pwsh env: DOTNET_VALIDATE_VERSION: ${{ needs.build.outputs.dotnet-validate-version }} + NUGET_PACKAGE_VALIDATION_VERSION: ${{ needs.build.outputs.nuget-package-validation-version }} run: | dotnet tool install --global dotnet-validate --version ${env:DOTNET_VALIDATE_VERSION} --allow-roll-forward + dotnet tool install --global Meziantou.Framework.NuGetPackageValidation.Tool --version ${env:NUGET_PACKAGE_VALIDATION_VERSION} --allow-roll-forward + + - name: Validate NuGet packages + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | $packages = Get-ChildItem -Filter "*.nupkg" | ForEach-Object { $_.FullName } $invalidPackages = 0 foreach ($package in $packages) { + $isValid = $true dotnet validate package local $package if ($LASTEXITCODE -ne 0) { + $isValid = $false + } + meziantou.validate-nuget-package $package --github-token ${env:GH_TOKEN} --excluded-rules IconMustBeSet + if ($LASTEXITCODE -ne 0) { + $isValid = $false + } + if (-Not $isValid) { $invalidPackages++ } } diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index b95b38df37..79424c58e4 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -79,9 +79,9 @@ jobs: git commit -m "Bump version`n`nBump version to ${env:NEXT_VERSION} for the next release." -s git push -u origin $branchName - "branch-name=${branchName}" >> $env:GITHUB_OUTPUT - "updated-version=true" >> $env:GITHUB_OUTPUT - "version=${env:NEXT_VERSION}" >> $env:GITHUB_OUTPUT + "branch-name=${branchName}" >> ${env:GITHUB_OUTPUT} + "updated-version=true" >> ${env:GITHUB_OUTPUT} + "version=${env:NEXT_VERSION}" >> ${env:GITHUB_OUTPUT} - name: Create pull request if: steps.push-changes.outputs.updated-version == 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4d279a8924..bcd12c2bfe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: $properties = Join-Path "." "Directory.Build.props" $xml = [xml](Get-Content $properties) $version = $xml.SelectSingleNode('Project/PropertyGroup/VersionPrefix').InnerText - "version=${version}" >> $env:GITHUB_OUTPUT + "version=${version}" >> ${env:GITHUB_OUTPUT} - name: Create release uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 diff --git a/build.ps1 b/build.ps1 index e9f016451b..d39ee8714d 100755 --- a/build.ps1 +++ b/build.ps1 @@ -18,7 +18,7 @@ if ($LASTEXITCODE -ne 0) { $additionalArgs = @() -if (![string]::IsNullOrEmpty($env:GITHUB_SHA)) { +if (![string]::IsNullOrEmpty(${env:GITHUB_SHA})) { $additionalArgs += "--logger:GitHubActions;report-warnings=false" $additionalArgs += "--logger:junit;LogFilePath=junit.xml" } From 82a69331677a5002f819f88b4f6d5784dd6e7dfd Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 10 Oct 2025 10:40:59 +0100 Subject: [PATCH 574/680] Update package tags - Add `openapi`. - Add missing tags for Swashbuckle.AspNetCore.Cli. - Add tags for swagger-ui. --- .../Swashbuckle.AspNetCore.Annotations.csproj | 2 +- .../Swashbuckle.AspNetCore.Cli.csproj | 1 + .../Swashbuckle.AspNetCore.Newtonsoft.csproj | 2 +- .../Swashbuckle.AspNetCore.ReDoc.csproj | 2 +- .../Swashbuckle.AspNetCore.Swagger.csproj | 2 +- .../Swashbuckle.AspNetCore.SwaggerGen.csproj | 2 +- .../Swashbuckle.AspNetCore.SwaggerUI.csproj | 2 +- src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj index 41512118f4..80865dbfc1 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj +++ b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj @@ -6,7 +6,7 @@ true true $(NoWarn);1591 - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;annotations + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;annotations true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj b/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj index cae9f0c1e9..e7980af792 100644 --- a/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj +++ b/src/Swashbuckle.AspNetCore.Cli/Swashbuckle.AspNetCore.Cli.csproj @@ -7,6 +7,7 @@ Exe true Swashbuckle.AspNetCore.Cli + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;cli;commandline $(DefaultTargetFrameworks) swagger diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj index d1e4860dd0..740a9a03a4 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj @@ -4,7 +4,7 @@ Swashbuckle.AspNetCore.Newtonsoft.snk Swagger Generator opt-in component to support Newtonsoft.Json serializer behaviors true - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;newtonsoft + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;newtonsoft true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj index d6d38f06b0..52c1c86fe5 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj +++ b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj @@ -8,7 +8,7 @@ true true $(NoWarn);1591 - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore;redoc + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;redoc true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj index e391e5959d..6346a9732e 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj +++ b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj @@ -7,7 +7,7 @@ true true $(NoWarn);1591 - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj b/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj index 4b492544d4..b046ad097f 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/Swashbuckle.AspNetCore.SwaggerGen.csproj @@ -6,7 +6,7 @@ true true $(NoWarn);1591 - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj index 33d938a35a..e22b217a77 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj @@ -8,7 +8,7 @@ true true $(NoWarn);1591 - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;swaggerui;swagger-ui true $(DefaultTargetFrameworks) diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj index 306a4e3459..019000834e 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj @@ -12,7 +12,7 @@ $(NoWarn);NU5128 $(MSBuildProjectName).nuspec Swashbuckle.AspNetCore - swagger;documentation;discovery;help;webapi;aspnet;aspnetcore + swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore $(DefaultTargetFrameworks) From e527f1528badd502c54b71ec9d296ab0e64fc531 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 10 Oct 2025 10:46:18 +0100 Subject: [PATCH 575/680] Add XML documentation Add XML documentation for the Swashbuckle.AspNetCore.Newtonsoft package. --- .../NewtonsoftServiceCollectionExtensions.cs | 10 ++++++++++ .../SchemaGenerator/JsonPropertyExtensions.cs | 18 ++++++++++++++++++ .../NewtonsoftDataContractResolver.cs | 5 +++++ .../Swashbuckle.AspNetCore.Newtonsoft.csproj | 1 + 4 files changed, 34 insertions(+) diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/DependencyInjection/NewtonsoftServiceCollectionExtensions.cs b/src/Swashbuckle.AspNetCore.Newtonsoft/DependencyInjection/NewtonsoftServiceCollectionExtensions.cs index 2007887728..4d082c6df8 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/DependencyInjection/NewtonsoftServiceCollectionExtensions.cs +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/DependencyInjection/NewtonsoftServiceCollectionExtensions.cs @@ -6,8 +6,18 @@ namespace Microsoft.Extensions.DependencyInjection; +/// +/// A class containing extension methods for setting up Newtonsoft.Json support. This class cannot be inherited. +/// public static class NewtonsoftServiceCollectionExtensions { + /// + /// Add support for using Newtonsoft.Json for serializing OpenAPI documents. + /// + /// The to configure. + /// + /// The so that additional calls can be chained. + /// public static IServiceCollection AddSwaggerGenNewtonsoftSupport(this IServiceCollection services) { return services.Replace( diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/JsonPropertyExtensions.cs b/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/JsonPropertyExtensions.cs index 9b7a895b59..d6b7fad95f 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/JsonPropertyExtensions.cs +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/JsonPropertyExtensions.cs @@ -4,8 +4,19 @@ namespace Swashbuckle.AspNetCore.Newtonsoft; +/// +/// A class containing extension methods for the class. This class cannot be inherited. +/// public static class JsonPropertyExtensions { + /// + /// Tries to get the for the given . + /// + /// The to get the for. + /// When the method returns, contains the for the property, if found. + /// + /// if the was found; otherwise, . + /// public static bool TryGetMemberInfo(this JsonProperty jsonProperty, out MemberInfo memberInfo) { memberInfo = jsonProperty.DeclaringType? @@ -15,6 +26,13 @@ public static bool TryGetMemberInfo(this JsonProperty jsonProperty, out MemberIn return memberInfo != null; } + /// + /// Returns whether the specified is specified whether it is required. + /// + /// The to test. + /// + /// if the specifies whether it is required; otherwise, . + /// public static bool IsRequiredSpecified(this JsonProperty jsonProperty) => jsonProperty.Required != Required.Default; } diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/NewtonsoftDataContractResolver.cs b/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/NewtonsoftDataContractResolver.cs index 6132126b1c..e2d517a8db 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/NewtonsoftDataContractResolver.cs +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/SchemaGenerator/NewtonsoftDataContractResolver.cs @@ -7,11 +7,16 @@ namespace Swashbuckle.AspNetCore.Newtonsoft; +/// +/// A class representing an implementation that uses Newtonsoft.Json. +/// +/// The to use. public class NewtonsoftDataContractResolver(JsonSerializerSettings serializerSettings) : ISerializerDataContractResolver { private readonly JsonSerializerSettings _serializerSettings = serializerSettings; private readonly IContractResolver _contractResolver = serializerSettings.ContractResolver ?? new DefaultContractResolver(); + /// public DataContract GetDataContractForType(Type type) { var effectiveType = Nullable.GetUnderlyingType(type) ?? type; diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj index 740a9a03a4..b44f8652fb 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj @@ -3,6 +3,7 @@ Swashbuckle.AspNetCore.Newtonsoft.snk Swagger Generator opt-in component to support Newtonsoft.Json serializer behaviors + true true swagger;openapi;documentation;discovery;help;webapi;aspnet;aspnetcore;newtonsoft true From 58c5095bb17aab1a158dceb263a138ff087524f2 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 10 Oct 2025 10:56:53 +0100 Subject: [PATCH 576/680] Fix missing Git commit Add the Git commit SHA to the `.nuspec` for Swashbuckle.AspNetCore. --- src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj | 1 + src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj index 019000834e..c4186cdc23 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.csproj @@ -25,6 +25,7 @@ id=$(PackageId); licenseExpression=$(PackageLicenseExpression); projectUrl=$(PackageProjectUrl); + repositoryCommit=$(GITHUB_SHA); repositoryType=$(RepositoryType); repositoryUrl=$(RepositoryUrl); tags=$(PackageTags.Replace(';', ' ')); diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec index 7f2314fa8c..98553901cc 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec @@ -11,7 +11,7 @@ docs\package-readme.md $description$ $tags$ - + From 08f7b4a8c83fceeecd2c06e0931dd85acfadb379 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:13:52 +0200 Subject: [PATCH 577/680] Bump github/codeql-action from 4.30.7 to 4.30.8 (#3610) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.7 to 4.30.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/e296a935590eb16afc0c0108289f68c87e2a89a5...f443b600d91635bebf5b0d9ebc620189c0d6fba5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.30.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 51aabb723b..f4a1ede6db 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index c98e8bcc38..2f03fab681 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 with: sarif_file: results.sarif From e495a5e3418cc0c2106714b5fec8aaba550d7b29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:18:47 +0200 Subject: [PATCH 578/680] Bump martincostello/update-dotnet-sdk from 4.0.0 to 5.0.0 (#3607) Bumps [martincostello/update-dotnet-sdk](https://github.com/martincostello/update-dotnet-sdk) from 4.0.0 to 5.0.0. - [Release notes](https://github.com/martincostello/update-dotnet-sdk/releases) - [Commits](https://github.com/martincostello/update-dotnet-sdk/compare/b4a65696fa41bbe5a4ddf75b7da0e204d7343dc7...a832b148de803dd5bcba5dbedb4ca75f20c3a0c4) --- updated-dependencies: - dependency-name: martincostello/update-dotnet-sdk dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-dotnet-sdk.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-dotnet-sdk.yml b/.github/workflows/update-dotnet-sdk.yml index 760d39fe6f..0f9e4393d4 100644 --- a/.github/workflows/update-dotnet-sdk.yml +++ b/.github/workflows/update-dotnet-sdk.yml @@ -11,7 +11,7 @@ permissions: jobs: update-dotnet-sdk: - uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@b4a65696fa41bbe5a4ddf75b7da0e204d7343dc7 # v4.0.0 + uses: martincostello/update-dotnet-sdk/.github/workflows/update-dotnet-sdk.yml@a832b148de803dd5bcba5dbedb4ca75f20c3a0c4 # v5.0.0 permissions: contents: write pull-requests: write From 66e4b62bd54cc1ab0d0b77292fae854e13cb86a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:19:18 +0200 Subject: [PATCH 579/680] Bump redoc from 2.5.1 to 2.5.2 in /src/Swashbuckle.AspNetCore.ReDoc (#3605) Bumps [redoc](https://github.com/Redocly/redoc) from 2.5.1 to 2.5.2. - [Release notes](https://github.com/Redocly/redoc/releases) - [Changelog](https://github.com/Redocly/redoc/blob/main/CHANGELOG.md) - [Commits](https://github.com/Redocly/redoc/commits) --- updated-dependencies: - dependency-name: redoc dependency-version: 2.5.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 47 +++++++++++++++---- src/Swashbuckle.AspNetCore.ReDoc/package.json | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json index 10d8cf399b..1e30389428 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json @@ -8,7 +8,7 @@ "name": "swashbuckle", "version": "1.0.0", "dependencies": { - "redoc": "2.5.1" + "redoc": "2.5.2" } }, "node_modules/@babel/runtime": { @@ -355,6 +355,24 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", + "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.1.1" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/foreach": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.6.tgz", @@ -701,12 +719,13 @@ } }, "node_modules/openapi-sampler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.5.1.tgz", - "integrity": "sha512-tIWIrZUKNAsbqf3bd9U1oH6JEXo8LNYuDlXw26By67EygpjT+ArFnsxxyTMjFWRfbqo5ozkvgSQDK69Gd8CddA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/openapi-sampler/-/openapi-sampler-1.6.2.tgz", + "integrity": "sha512-NyKGiFKfSWAZr4srD/5WDhInOWDhfml32h/FKUqLpEwKJt0kG0LGUU0MdyNkKrVGuJnw6DuPWq/sHCwAMpiRxg==", "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.7", + "fast-xml-parser": "^4.5.0", "json-pointer": "0.6.2" } }, @@ -853,9 +872,9 @@ } }, "node_modules/redoc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.1.tgz", - "integrity": "sha512-LmqA+4A3CmhTllGG197F0arUpmChukAj9klfSdxNRemT9Hr07xXr7OGKu4PHzBs359sgrJ+4JwmOlM7nxLPGMg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/redoc/-/redoc-2.5.2.tgz", + "integrity": "sha512-sTJfItvRkcDTojB6wdLN4M+Ua6mlZwElV21Tf8Mn7IbQF/1Os6GvgQpZyLWPGZZHbhy7GC1Or1hTMHfz1vKh5A==", "license": "MIT", "dependencies": { "@redocly/openapi-core": "^1.4.0", @@ -868,7 +887,7 @@ "mark.js": "^8.11.1", "marked": "^4.3.0", "mobx-react": "9.2.0", - "openapi-sampler": "^1.5.0", + "openapi-sampler": "^1.6.2", "path-browserify": "^1.0.1", "perfect-scrollbar": "^1.5.5", "polished": "^4.2.2", @@ -1046,6 +1065,18 @@ "node": ">=8" } }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/styled-components": { "version": "6.1.13", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.13.tgz", diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package.json b/src/Swashbuckle.AspNetCore.ReDoc/package.json index 5636bd4774..671b8d5abf 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package.json @@ -3,6 +3,6 @@ "version": "1.0.0", "private": true, "dependencies": { - "redoc": "2.5.1" + "redoc": "2.5.2" } } From cb22246d93fc496cd25132ff33ca6ccabd7b3475 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:20:51 +0000 Subject: [PATCH 580/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3606) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.29.3 to 5.29.4. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.29.3...v5.29.4) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.29.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 3dc7acd079..314149b9f8 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.3" + "swagger-ui-dist": "5.29.4" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.3.tgz", - "integrity": "sha512-U99f/2YocRA2Mxqx3eUBRhQonWVtE5dIvMs0Zlsn4a4ip8awMq0JxXhU+Sidtna2WlZrHbK2Rro3RZvYUymRbA==", + "version": "5.29.4", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz", + "integrity": "sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 13af417318..4d573da1a3 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.3" + "swagger-ui-dist": "5.29.4" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From 6ea5e65c9a3aab326acf9bac7eb0b73867f68fea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:21:40 +0000 Subject: [PATCH 581/680] Bump actions/setup-node from 5.0.0 to 6.0.0 (#3608) * Bump actions/setup-node from 5.0.0 to 6.0.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/a0853c24544627f65ddf259abe73b1d18a591444...2028fbc5c25fe9cf00d9f06a71cc4710d4507903) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Disable package cache Resolve Zizmor error. --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Costello --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 835c743b26..0b555175e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,9 +79,10 @@ jobs: 9.0.x - name: Setup Node - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: node-version: '24' + package-manager-cache: false - name: Setup .NET SDK uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 @@ -115,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 + uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 if: runner.os == 'Windows' with: artifact-name: Swashbuckle.AspNetCore.spdx.json From af130fe7df3a1af7195ac435a6d6f054769fda0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:21:49 +0200 Subject: [PATCH 582/680] Bump actions/dependency-review-action from 4.8.0 to 4.8.1 (#3609) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.8.0 to 4.8.1. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/56339e523c0409420f6c2c9a2f4292bbb3c07dd3...40c09b7dc99638e5ddb0bfd91c1673effc064d8a) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 4e3ef20425..0a51eb63f5 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -23,6 +23,6 @@ jobs: show-progress: false - name: Review dependencies - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 + uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 with: allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' From 4860f8aeb13934973a0697880efce290a6c2505d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 09:24:01 +0000 Subject: [PATCH 583/680] Bump the xunit group with 1 update (#3611) Bumps Verify.XunitV3 from 30.20.1 to 31.0.1 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.0.1 dependency-type: direct:production update-type: version-update:semver-major dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5bb5d9e214..73f54305a1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 4cd387452ee9efc52ef159789b37430b703e4b4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:58:56 +0000 Subject: [PATCH 584/680] Update .NET SDK (#3612) Update .NET SDK to version 9.0.306. --- updated-dependencies: - dependency-name: Microsoft.NET.Sdk dependency-version: 9.0.306 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 5cc6c623c4..909a1fb9ce 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.305", + "version": "9.0.306", "allowPrerelease": false, "rollForward": "latestMajor", "paths": [ ".dotnet", "$host$" ] From 7f32e743094e3fc6efb846887fbe426375f4881d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:08:00 +0000 Subject: [PATCH 585/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3615) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.29.4 to 5.29.5. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.29.4...v5.29.5) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.29.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 314149b9f8..30562c1742 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.4" + "swagger-ui-dist": "5.29.5" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.4", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.4.tgz", - "integrity": "sha512-gJFDz/gyLOCQtWwAgqs6Rk78z9ONnqTnlW11gimG9nLap8drKa3AJBKpzIQMIjl5PD2Ix+Tn+mc/tfoT2tgsng==", + "version": "5.29.5", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.5.tgz", + "integrity": "sha512-2zFnjONgLXlz8gLToRKvXHKJdqXF6UGgCmv65i8T6i/UrjDNyV1fIQ7FauZA40SaivlGKEvW2tw9XDyDhfcXqQ==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 4d573da1a3..cea02400f6 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.4" + "swagger-ui-dist": "5.29.5" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From b7bb2b491eb140c2796025f8a3b08dbf124ec6a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:09:04 +0000 Subject: [PATCH 586/680] Bump anchore/sbom-action from 0.20.6 to 0.20.8 (#3617) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.20.6 to 0.20.8. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/f8bdd1d8ac5e901a77a92f111440fdb1b593736b...aa0e114b2e19480f157109b9922bda359bd98b90) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-version: 0.20.8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b555175e3..cc7bcf9f57 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 + uses: anchore/sbom-action@aa0e114b2e19480f157109b9922bda359bd98b90 # v0.20.8 if: runner.os == 'Windows' with: artifact-name: Swashbuckle.AspNetCore.spdx.json From 78b0e1977c6c112c5255db3a41da93a87cba0e06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:09:26 +0000 Subject: [PATCH 587/680] Bump github/codeql-action from 4.30.8 to 4.30.9 (#3616) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.8 to 4.30.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f443b600d91635bebf5b0d9ebc620189c0d6fba5...16140ae1a102900babc80a33c44059580f687047) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.30.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f4a1ede6db..f11dea6def 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 + uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 + uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 2f03fab681..8793ec5928 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@f443b600d91635bebf5b0d9ebc620189c0d6fba5 # v4.30.8 + uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 with: sarif_file: results.sarif From 9d818bf8fa327de11d1f9e01fa40556757378d20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:12:36 +0000 Subject: [PATCH 588/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3618) --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 30562c1742..613b657353 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.29.5" + "swagger-ui-dist": "5.30.0" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.29.5", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.29.5.tgz", - "integrity": "sha512-2zFnjONgLXlz8gLToRKvXHKJdqXF6UGgCmv65i8T6i/UrjDNyV1fIQ7FauZA40SaivlGKEvW2tw9XDyDhfcXqQ==", + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.0.tgz", + "integrity": "sha512-BoiDSeT9PCtHfDYMgX5UpB/qTQy44UoSFRmzHqvhGfgxzEVPHxIW78a+HMLUIHnSFu3z63wjtbq6L6+Rto20Rw==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index cea02400f6..6b2e2a7024 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.29.5" + "swagger-ui-dist": "5.30.0" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From 35996653f6395ad1497acdd01aee24dcf20de544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:13:10 +0000 Subject: [PATCH 589/680] Bump actions/download-artifact from 5.0.0 to 6.0.0 (#3619) --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cc7bcf9f57..03e1d7b857 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -160,7 +160,7 @@ jobs: steps: - name: Download packages - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: packages-Windows @@ -220,7 +220,7 @@ jobs: steps: - name: Download packages - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: packages-Windows @@ -250,7 +250,7 @@ jobs: steps: - name: Download packages - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: packages-Windows From 94991975a829f76371afbec989b79ff2179cadd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:13:33 +0000 Subject: [PATCH 590/680] Bump actions/upload-artifact from 4.6.2 to 5.0.0 (#3620) --- .github/workflows/build.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03e1d7b857..f69134640a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,7 +96,7 @@ jobs: - name: Upload Coverage Reports if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: coverage-${{ runner.os }} path: ./artifacts/coverage @@ -137,7 +137,7 @@ jobs: ./artifacts/package/release/* - name: Publish NuGet packages - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: ${{ !cancelled() }} with: name: packages-${{ runner.os }} diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 8793ec5928..793a70c46f 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -34,7 +34,7 @@ jobs: results_format: sarif - name: Upload artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: SARIF path: results.sarif From 7b5d2636767a6069de5ac6c796d59205df03fc2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:14:02 +0000 Subject: [PATCH 591/680] Bump github/codeql-action from 4.30.9 to 4.31.0 (#3621) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f11dea6def..6852fbb721 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 793a70c46f..40fcaf52ce 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 with: sarif_file: results.sarif From e661cc8f34e9c957f6f10d3928030f1d03c128dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:16:21 +0000 Subject: [PATCH 592/680] Bump BenchmarkDotNet from 0.15.3 to 0.15.4 (#3624) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 73f54305a1..ea75084ed4 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@  - + From 0765b1665a15a0467a1fcfd9fb98fec4083b9054 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:17:01 +0000 Subject: [PATCH 593/680] Bump meziantou.framework.nugetpackagevalidation.tool from 1.0.31 to 1.0.34 (#3626) --- .config/dotnet-tools.json | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 2b11c94661..9ccd1202f5 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,27 +1,27 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "dotnet-validate": { - "version": "0.0.1-preview.537", - "commands": [ - "dotnet-validate" - ], - "rollForward": false - }, - "markdownsnippets.tool": { - "version": "27.0.2", - "commands": [ - "mdsnippets" - ], - "rollForward": false - }, - "meziantou.framework.nugetpackagevalidation.tool": { - "version": "1.0.31", - "commands": [ - "meziantou.validate-nuget-package" - ], - "rollForward": false - } - } +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-validate": { + "version": "0.0.1-preview.537", + "commands": [ + "dotnet-validate" + ], + "rollForward": false + }, + "markdownsnippets.tool": { + "version": "27.0.2", + "commands": [ + "mdsnippets" + ], + "rollForward": false + }, + "meziantou.framework.nugetpackagevalidation.tool": { + "version": "1.0.34", + "commands": [ + "meziantou.validate-nuget-package" + ], + "rollForward": false + } + } } \ No newline at end of file From 8117ba603f67bb3f77f94f96d523765221b69b68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:17:26 +0000 Subject: [PATCH 594/680] Bump Microsoft.NET.Test.Sdk from 17.14.1 to 18.0.0 (#3627) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ea75084ed4..cfda9694c3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,7 +17,7 @@ - + From d1ab79afa4314d7065f9f00679a1806b232405d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:19:43 +0000 Subject: [PATCH 595/680] Bump anchore/sbom-action from 0.20.8 to 0.20.9 (#3622) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f69134640a..132b29028c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@aa0e114b2e19480f157109b9922bda359bd98b90 # v0.20.8 + uses: anchore/sbom-action@8e94d75ddd33f69f691467e42275782e4bfefe84 # v0.20.9 if: runner.os == 'Windows' with: artifact-name: Swashbuckle.AspNetCore.spdx.json From 0576b4e9fa2dd848d52738266b7469e5fd2b9da9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:05:08 +0000 Subject: [PATCH 596/680] Bump the xunit group with 1 update (#3623) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cfda9694c3..485460656b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 4daf855d0e570b460f0661871a7dbb3125b860c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:09:07 +0000 Subject: [PATCH 597/680] Bump JunitXml.TestLogger from 6.1.0 to 7.0.2 (#3625) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 485460656b..7503e22676 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + From e2c8e05d938d4bf09ba5aa1f03e8dad1382d1b31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:50:22 +0000 Subject: [PATCH 598/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3630) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.30.0 to 5.30.1. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.30.0...v5.30.1) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.30.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 613b657353..c4b33b66a0 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.30.0" + "swagger-ui-dist": "5.30.1" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.0.tgz", - "integrity": "sha512-BoiDSeT9PCtHfDYMgX5UpB/qTQy44UoSFRmzHqvhGfgxzEVPHxIW78a+HMLUIHnSFu3z63wjtbq6L6+Rto20Rw==", + "version": "5.30.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.1.tgz", + "integrity": "sha512-4mNAUM31sr52K3JcK9qiGbfsFKNh/dm3PkEe+F9FAM31YY/NoRYUgsR/L6d7LLFn6PgZXtBG2ygp8+7UnpUIPg==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 6b2e2a7024..af8919434b 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.30.0" + "swagger-ui-dist": "5.30.1" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From 547095255ed998beee5113c00d3d040ea6bce967 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:50:29 +0000 Subject: [PATCH 599/680] Bump github/codeql-action from 4.31.0 to 4.31.2 (#3629) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.0 to 4.31.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/4e94bd11f71e507f7f87df81788dff88d1dacbfb...0499de31b99561a6d14a36a5f662c2a54f91beee) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6852fbb721..521a5833d3 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 40fcaf52ce..48aa0f83d3 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0 + uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 with: sarif_file: results.sarif From f433e477f50e70edd626caed0528ad3923e7d5c1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:05:13 +0000 Subject: [PATCH 600/680] Bump the xunit group with 1 update (#3631) Bumps Verify.XunitV3 from 31.2.0 to 31.3.0 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7503e22676..7f5b14a8a6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 6b5023fd501b77c05e0aa96473cda84d43024a6a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:10:04 +0000 Subject: [PATCH 601/680] Bump BenchmarkDotNet from 0.15.4 to 0.15.5 (#3632) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-version: 0.15.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7f5b14a8a6..884f22f51c 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@  - + From a23e0c269e8de5e2e7edc9843963b13fefde5451 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:11:43 +0000 Subject: [PATCH 602/680] Bump NSwag.MSBuild from 14.6.0 to 14.6.1 (#3633) --- updated-dependencies: - dependency-name: NSwag.MSBuild dependency-version: 14.6.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 884f22f51c..147226aa42 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -20,7 +20,7 @@ - + From 8ecdcbc5b283de5e0d6e4a070c5f4c8df8053551 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:40:26 +0000 Subject: [PATCH 603/680] Bump ReportGenerator from 5.4.13 to 5.4.18 (#3634) --- updated-dependencies: - dependency-name: ReportGenerator dependency-version: 5.4.18 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 147226aa42..472a260e35 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,7 +21,7 @@ - + From d532e185ac869a4f3913745e05113ce564803364 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:05:46 +0000 Subject: [PATCH 604/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3636) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.30.1 to 5.30.2. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.30.1...v5.30.2) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.30.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index c4b33b66a0..21e15498a4 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.30.1" + "swagger-ui-dist": "5.30.2" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.30.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.1.tgz", - "integrity": "sha512-4mNAUM31sr52K3JcK9qiGbfsFKNh/dm3PkEe+F9FAM31YY/NoRYUgsR/L6d7LLFn6PgZXtBG2ygp8+7UnpUIPg==", + "version": "5.30.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz", + "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index af8919434b..7c200754c9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.30.1" + "swagger-ui-dist": "5.30.2" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From 5836657f9e01c021738b455a6d10fd87f18d067e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:52:50 +0000 Subject: [PATCH 605/680] Bump meziantou.framework.nugetpackagevalidation.tool from 1.0.34 to 1.0.35 (#3639) --- updated-dependencies: - dependency-name: meziantou.framework.nugetpackagevalidation.tool dependency-version: 1.0.35 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .config/dotnet-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 9ccd1202f5..70b7a4fff0 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -17,7 +17,7 @@ "rollForward": false }, "meziantou.framework.nugetpackagevalidation.tool": { - "version": "1.0.34", + "version": "1.0.35", "commands": [ "meziantou.validate-nuget-package" ], From cca21606a0b8a3f0a1635b0b15933461fb3cc481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:53:12 +0000 Subject: [PATCH 606/680] Bump BenchmarkDotNet from 0.15.5 to 0.15.6 (#3638) --- updated-dependencies: - dependency-name: BenchmarkDotNet dependency-version: 0.15.6 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 472a260e35..c157fed450 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@  - + From 2b7afecb2400dc658b649e363a1f722afcaf20a9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:56:54 +0000 Subject: [PATCH 607/680] Bump the xunit group with 3 updates (#3637) Bumps Verify.XunitV3 from 31.3.0 to 31.4.3 Bumps xunit.v3 from 3.1.0 to 3.2.0 Bumps xunit.v3.extensibility.core from 3.1.0 to 3.2.0 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.4.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit - dependency-name: xunit.v3 dependency-version: 3.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit - dependency-name: xunit.v3.extensibility.core dependency-version: 3.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index c157fed450..2b69a177ab 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,10 +22,10 @@ - + - - + + From 57092cdba09e205f9c21de8ad03080725c6d1c8d Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Tue, 11 Nov 2025 13:13:58 +0000 Subject: [PATCH 608/680] Support ASP.NET Core 10 (#3283) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Support .NET 10 - Add support for ASP.NET Core 10 and Microsoft.OpenApi v2. - Add opt-in support for OpenAPI 3.1 when targeting `net10.0`. * Fix test Fix broken test. * Remove obsolete method Remove obsolete method. * Update to ASP.NET Core 10 preview 2 Update to preview 2 of ASP.NET Core 10, which includes Microsoft.OpenApi 2.0.0-preview7. * Add TODO Add TODO to use `SerializeAs()`. * Partially fix security requirement issue Use `AddComponent()` to ensure references work correctly. * Fix test Fix test by passing the current `OpenApiDocument` to the filter contexts so it can be used to resolve the references. * Fix test Fix broken test. * Fix type tests Account for flags enum. * Fix missing descriptions Fix missing descriptions in .NET 10. * Refactor fix Restore the sample projects to how they were, and instead adjust the annotation filters to consider parameters and properties for records. * Fix package reference Fix missed rename in merge. * Fix xUnit1051 warnings Fix xUnit1051 warnings * Update to ASP.NET Core 10 preview 3 (#3361) Update to preview 3 of ASP.NET Core 10. * Update NuGet packages Update NuGet packages for .NET 10 preview 3. * Link to issue Update Skip with link to GitHub issue. * Fix typo "respository" ➡️ "repository". * Fix tag serialization Add tags to the document before adding them to the operations as a workaround for https://github.com/microsoft/OpenAPI.NET/issues/2319. * Fix merge Fix bad merge with default branch. * Add issue reference Add comment with link to issue. * Remove TODO Based on tests passing, I think it is correct. * Fix formatting Adjust formatting to match what it would be without the pragma. * Prepare for .NET 10 preview 4 (#3388) Cherry-pick changes from #3386 to prepare for ASP.NET Core 10 preview 4 and Microsoft.OpenApi 2.0.0-preview.17. * Fix nullability for null schema Type Fix bitmasks for adding `JsonSchemaType.Null` if the `Type` is null. Resolves #3387. * Re-enable MyGet Re-enable MyGet on the off-chance that it was a transient issue and has self-resolved. * Fix rebase Fix changes after merge from main. * Skip failing tets Skip failing tests for now - re-visit once ASP.NET Core 10 preview.4 lets us update to Microsoft.OpenApi 2.0.0-preview.17. * Update MyGet publish URL See if the issue is specific to the old V2 push endpoint. * Re-disable MyGet publishing Using the v3 feed didn't fix it. * Update to Microsoft.OpenApi 2.0.0 preview 17 Update Microsoft.OpenApi to version 2.0.0-preview.17 using the latest daily build of ASP.NET Core 10 preview 4. * Fix API baselines Update API baselines for change from `OperationType` to `HttpMethod `. * Skip failing tests .NET 8 and 9 TestFirst integration tests are failing, so skip for now. * Use SerializeAs Use the new `SerializeAs()` method to resolve TODOs. * Fix build Use the right writer. * Apply suggestions from code review * Fix build Fix build for official build of .NET 10 preview 4. * Disable failing tests Disable tests failing due to yet-to-be-logged issue with the OpenAPI document being truncated/not flushed. * Fix MergeWith Fix incorrect file name. * Publish packages Publish NuGet packages to the workflow even if tests fail. * Fix tests Fix tests due to error in test setup fixture caused by changes in https://github.com/microsoft/OpenAPI.NET/pull/2325. * Remove workaround Remove workaround needed for previous preview versions. * Re-enable tests Re-enable three skipped tests that are no longer failing. * Re-enable MyGet publishing Re-enable publishing to MyGet. * Apply review suggestions Apply some of the code-review suggestions. More to follow. * Workaround VS issue Workaround issue building the solution in Visual Studio. * Fix culture-insensitive handling of numbers with [Range] (#3426) Fix incorrect handling of culture-sensitivity for number ranges. * Refactor method Rename constant and move culture to variable. * Fix range parsing - Use the correct culture to parse `[Range]`. - Update snapshots. * Sort AnyOf and OneOf Address code review comment. * Update issue number Point to latest issue number. * Refactor [Range] handling Refactor how `[Range]` is handled to match how ASP.NET Core 10 does. * Add test cases for `[Range]` Add various test cases for the behaviour of `[Range]` applying changes to `OpenApiSchema` instances. * Refactor method to avoid round-trip Refactor method to avoid redundant round-trip parse/format when the `Maximum` and `Minimum` properties are not strings. * Tweak comment Fix grammar. * Fix comment Add another missing word. * Fix conversion Convert to `decimal` not `double`. * Update snapshot Update snapshot after merge. * Update .NET SDK to 10.0.100-preview.5.25277.114 (#3451) * Update .NET SDK Update .NET SDK to version 10.0.100-preview.5.25277.114. --- updated-dependencies: - dependency-name: Microsoft.NET.Sdk dependency-version: 10.0.100-preview.5.25277.114 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: github-actions[bot] * Bump NuGet packages Update NuGet packages for .NET 10 preview 5. --------- Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: Martin Costello * Bump Microsoft.OpenApi Bump Microsoft.OpenApi for .NET 10 preview 5. * Fix build Update `OpenApiAny` to `JsonNodeExtension`. * Fix line endings Fix-up line endings. * Validate TypedResults is supported for ASP.NET Core 10 (#3455) Co-authored-by: Javier García de la Noceda Argüelles * Support [ProducesResponseType], [Produces], and [ProducesDefaultResponseType] (#3456) Add support for populating the description for an OpenAPI operation from `[ProducesResponseType]`, `[Produces]`, and/or `[ProducesDefaultResponseType]`. Resolves #3099. * Remove workarounds Remove workarounds from earlier previews. * Apply suggestions from code review * Fix build Fix build after merge. * Fix package version Update from preview 1 to preview 5. * Update PublicAPI baselines Move renames from Shipped to Unshipped files. * Update _SdkTasksTFM Update the value of `_SdkTasksTFM` for .NET 10. * Update to ASP.NET Core 10 preview.6 (#3479) Update to ASP.NET Core 10 preview 6 and Microsoft.OpenApi 2.0.0-preview.29. * Update Microsoft.OpenApi to 2.0.0 Update to the stable release of Microsoft.OpenApi 2.0.0. * Remove WithOpenApi usage `WithOpenApi()` will be deprecated in .NET 10 preview 7. See https://github.com/aspnet/announcements/issues/519. * Remove redundant references Remove redundant references to `Microsoft.AspNetCore.OpenApi`. * Add UI tests for SwaggerUI and Redoc Add end-to-end UI tests for SwaggerUI and Redoc to help validate upstream changes. Resolves #3514. * Update allowed licenses Update licenses for Microsoft.OpenApi 2.0.0. * Update to ASP.NET Core 10 preview 7 Update to preview 7 of ASP.NET Core 10. * Remove workaround Remove workaround for resolved issue. * Bump Microsoft.OpenApi to 2.1.0 - Bump Microsoft.OpenApi to 2.1.0 in test projects. - Pin to 2.0.0 in shipping packages. * Verify OpenAPI documents Verify there are no errors or warnings. * Update snapshots Update snapshot baselines. * Apply suggestions from code review Bump version to match other TFMs. * Update to .NET 10 RC1 (#3556) - Update to release candidate 1 of .NET 10. - Fix or suppress `ASPDEPR004` and `ASPDEPR008` warnings. * Fix deprecation warnings Fix `ASPDEPR004` and `ASPDEPR008` warnings. * Fix whitespace Fix-up formatting and package versions. * Resolve TODO NSwag now supports .NET 10. * Bump Duende.IdentityServer Update Duende.IdentityServer for .NET 10. * Simplify reference Remove duplication by defining the reference once. * Update documentation for v10 and OpenAPI 3.1 (#3585) - Update the READMEs for changes in v10. - Update most references to Swagger to OpenAPI. - Add migration guide for v9 to v10. - Add the version of Microsoft.OpenApi included to the version table. - Support for generating code snippets in the documentation from code that is checked to compile. * Fix build Fix bad merge. * Update Microsoft.OpenApi Bump to v2.3.4. * Bump Microsoft.OpenApi Bump Microsoft.OpenApi to 2.3.5. * Update to .NET 10 RC2 Update to Release Candidate 2 of .NET 10. * Bump Microsoft.OpenApi Bump Microsoft.OpenApi to 2.3.6. * Update Microsoft.OpenApi Update to 2.3.7. * Bump Microsoft.OpenApi Update to 2.3.8. * Initialized Security property (#3635) Avoid `NullReferenceException` in synchronous code path. * Bump Microsoft.OpenApi Bump Microsoft.OpenApi to 2.3.9. * Bump Microsoft.OpenApi Update Microsoft.OpenApi package version to 2.3.0. * Bump Microsoft.OpenApi Update Microsoft.OpenApi package version to 2.3.0. * Update migration guide - Call out some additional required changes. - Add specific package versions. - Link to the Microsoft.AspNetCore.OpenApi NuGet package. * Add upgrade tip Added a recommendation to upgrade to Swashbuckle.AspNetCore v9.0.6 before migrating to v10. * Update to .NET 10 GA Update to stable release of .NET 10. --------- Signed-off-by: github-actions[bot] Co-authored-by: Bart Koelman <10324372+bkoelman@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: Javier García de la Noceda Argüelles Co-authored-by: Javier García de la Noceda Argüelles Co-authored-by: Cesar Roux dit Buisson <129835705+crudbee@users.noreply.github.com> --- .github/dependabot.yml | 2 +- .github/workflows/dependency-review.yml | 54 +- Directory.Build.props | 4 +- Directory.Packages.props | 62 +- README.md | 22 +- docs/configure-and-customize-annotations.md | 15 +- docs/configure-and-customize-redoc.md | 12 +- docs/configure-and-customize-swagger.md | 29 +- docs/configure-and-customize-swaggergen.md | 81 +- docs/configure-and-customize-swaggerui.md | 22 +- docs/migrating-to-v10.md | 92 + global.json | 2 +- .../Swashbuckle.AspNetCore.Benchmarks.csproj | 2 +- .../XmlCommentsBenchmark.cs | 23 +- src/Shared/JsonExtensions.cs | 26 + src/Shared/JsonSchemaTypes.cs | 16 +- .../AnnotationsDocumentFilter.cs | 28 +- .../AnnotationsOperationFilter.cs | 12 +- .../AnnotationsParameterFilter.cs | 19 +- .../AnnotationsRequestBodyFilter.cs | 12 +- .../AnnotationsSchemaFilter.cs | 38 +- .../PublicAPI/PublicAPI.Shipped.txt | 6 - .../PublicAPI/PublicAPI.Unshipped.txt | 6 + .../ApiTestFixture.cs | 4 +- .../PublicAPI/PublicAPI.Shipped.txt | 1 - .../PublicAPI/PublicAPI.Unshipped.txt | 1 + .../ApiTestRunnerBase.cs | 5 +- .../ApiTestRunnerOptions.cs | 2 +- .../ApiTestRunnerOptionsExtensions.cs | 11 +- .../IContentValidator.cs | 2 +- .../JsonContentValidator.cs | 4 +- .../JsonValidation/IJsonValidator.cs | 6 +- .../JsonValidation/JsonAllOfValidator.cs | 8 +- .../JsonValidation/JsonAnyOfValidator.cs | 8 +- .../JsonValidation/JsonArrayValidator.cs | 8 +- .../JsonValidation/JsonBooleanValidator.cs | 8 +- .../JsonValidation/JsonNullValidator.cs | 8 +- .../JsonValidation/JsonNumberValidator.cs | 52 +- .../JsonValidation/JsonObjectValidator.cs | 8 +- .../JsonValidation/JsonOneOfValidator.cs | 8 +- .../JsonValidation/JsonStringValidator.cs | 8 +- .../JsonValidation/JsonValidator.cs | 15 +- .../OpenApiDocumentExtensions.cs | 30 +- .../OpenApiSchemaExtensions.cs | 31 +- .../PublicAPI/PublicAPI.Shipped.txt | 32 +- .../PublicAPI/PublicAPI.Unshipped.txt | 30 + .../RequestValidator.cs | 41 +- .../ResponseValidator.cs | 30 +- .../Swashbuckle.AspNetCore.ApiTesting.csproj | 7 +- src/Swashbuckle.AspNetCore.Cli/Program.cs | 14 +- .../Swashbuckle.AspNetCore.Newtonsoft.csproj | 4 + .../Swashbuckle.AspNetCore.ReDoc.csproj | 2 +- .../IAsyncSwaggerProvider.cs | 2 +- .../ISwaggerDocumentSerializer.cs | 2 - .../ISwaggerProvider.cs | 2 +- .../PublicAPI/PublicAPI.Shipped.txt | 5 - .../PublicAPI/PublicAPI.Unshipped.txt | 6 +- .../SwaggerEndpointOptions.cs | 1 - .../SwaggerMiddleware.cs | 15 +- .../SwaggerOptions.cs | 1 - .../Swashbuckle.AspNetCore.Swagger.csproj | 2 +- .../ConfigureSchemaGeneratorOptions.cs | 4 +- .../ConfigureSwaggerGeneratorOptions.cs | 4 +- .../DependencyInjection/DocumentProvider.cs | 14 +- .../SwaggerGenOptionsExtensions.cs | 12 +- .../JsonModelFactory.cs | 7 +- .../PublicAPI/PublicAPI.Shipped.txt | 45 - .../PublicAPI/PublicAPI.Unshipped.txt | 47 + .../PublicAPI/net8.0/PublicAPI.Shipped.txt | 4 - .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 1 - .../PublicAPI/net9.0/PublicAPI.Shipped.txt | 4 - .../PublicAPI/net9.0/PublicAPI.Unshipped.txt | 1 - .../SchemaGenerator/ISchemaFilter.cs | 4 +- .../OpenApiSchemaExtensions.cs | 90 +- .../SchemaGenerator/SchemaGenerator.cs | 107 +- .../SchemaGenerator/SchemaGeneratorOptions.cs | 6 +- .../SwaggerGenerator/IDocumentAsyncFilter.cs | 2 +- .../SwaggerGenerator/IDocumentFilter.cs | 2 +- .../SwaggerGenerator/IOperationAsyncFilter.cs | 2 +- .../SwaggerGenerator/IOperationFilter.cs | 2 +- .../SwaggerGenerator/IParameterAsyncFilter.cs | 4 +- .../SwaggerGenerator/IParameterFilter.cs | 4 +- .../IRequestBodyAsyncFilter.cs | 4 +- .../SwaggerGenerator/IRequestBodyFilter.cs | 4 +- .../SwaggerGenerator/ISchemaGenerator.cs | 4 +- .../SwaggerGenerator/OpenApiAnyFactory.cs | 105 - .../OperationFilterContext.cs | 4 + .../ParameterFilterContext.cs | 4 + .../RequestBodyFilterContext.cs | 6 +- .../SwaggerGenerator/SchemaRepository.cs | 18 +- .../SwaggerGenerator/SwaggerGenerator.cs | 246 ++- .../SwaggerGeneratorOptions.cs | 10 +- .../XmlComments/XmlCommentsDocumentFilter.cs | 39 +- .../XmlComments/XmlCommentsExampleHelper.cs | 34 +- .../XmlComments/XmlCommentsOperationFilter.cs | 21 +- .../XmlComments/XmlCommentsParameterFilter.cs | 45 +- .../XmlCommentsRequestBodyFilter.cs | 32 +- .../XmlComments/XmlCommentsSchemaFilter.cs | 45 +- .../Swashbuckle.AspNetCore.SwaggerUI.csproj | 2 +- .../Swashbuckle.AspNetCore.nuspec | 6 + .../AnnotationsDocumentFilterTests.cs | 2 +- .../AnnotationsOperationFilterTests.cs | 12 +- .../AnnotationsParameterFilterTests.cs | 5 +- .../AnnotationsRequestBodyFilterTests.cs | 10 +- .../AnnotationsSchemaFilterTests.cs | 10 +- .../VendorExtensionsOperationFilter.cs | 6 +- .../Fixtures/VendorExtensionsSchemaFilter.cs | 11 +- .../ApiTestRunnerBaseTests.cs | 10 +- .../JsonValidatorTests.cs | 26 +- .../RequestValidatorTests.cs | 60 +- .../ResponseValidatorTests.cs | 40 +- .../ToolTests.cs | 42 + .../CustomDocumentSerializerTests.cs | 45 + .../HttpServerFixture.cs | 20 + .../OpenApiDocumentLoader.cs | 24 +- .../PlaywrightFixture.cs | 65 + .../SwaggerIntegrationTests.cs | 5 +- ...hbuckle.AspNetCore.IntegrationTests.csproj | 16 +- .../UITests.cs | 48 + ...gerJson_ForAutofaq.DotNet10_0.verified.txt | 59 + ...ggerJson_ForAutofaq.DotNet8_0.verified.txt | 7 +- ...ggerJson_ForAutofaq.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 1718 +++++++++++++++++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 36 + ...waggerRequestUri=v1.DotNet9_0.verified.txt | 36 + ...aggerRequestUri=v1.DotNet10_0.verified.txt | 59 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 73 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 3 + ...aggerRequestUri=v1.DotNet10_0.verified.txt | 54 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 54 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 591 ++++++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 10 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 10 +- ...ggerRequestUri=1.0.DotNet10_0.verified.txt | 80 + ...aggerRequestUri=1.0.DotNet8_0.verified.txt | 7 +- ...aggerRequestUri=1.0.DotNet9_0.verified.txt | 7 +- ...ggerRequestUri=2.0.DotNet10_0.verified.txt | 220 +++ ...aggerRequestUri=2.0.DotNet8_0.verified.txt | 7 +- ...aggerRequestUri=2.0.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 474 +++++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 16 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 16 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 252 +++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 5 + ...waggerRequestUri=v1.DotNet9_0.verified.txt | 5 + ...aggerRequestUri=v1.DotNet10_0.verified.txt | 194 ++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 18 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 18 +- ...stUri=v1-generated.DotNet10_0.verified.txt | 52 + ...aggerRequestUri=v1.DotNet10_0.verified.txt | 79 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 87 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 10 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 10 +- ...swaggerRequestUri=.DotNet10_0.verified.txt | 9 + ...aggerRequestUri=v1.DotNet10_0.verified.txt | 87 + ...waggerRequestUri=v1.DotNet8_0.verified.txt | 7 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 7 +- ...aggerRequestUri=v1.DotNet10_0.verified.txt | 1220 ++++++++++++ ...waggerRequestUri=v1.DotNet8_0.verified.txt | 35 +- ...waggerRequestUri=v1.DotNet9_0.verified.txt | 35 +- ...eRenderedCorrectly.DotNet10_0.verified.txt | 1220 ++++++++++++ ...reRenderedCorrectly.DotNet8_0.verified.txt | 35 +- ...reRenderedCorrectly.DotNet9_0.verified.txt | 35 +- .../Fixtures/RecursiveCallSchemaFilter.cs | 4 +- .../Fixtures/VendorExtensionsSchemaFilter.cs | 11 +- .../NewtonsoftSchemaGeneratorTests.cs | 258 ++- ...shbuckle.AspNetCore.Newtonsoft.Test.csproj | 1 + .../Fixtures/RecursiveCallSchemaFilter.cs | 4 +- .../Fixtures/TestDocumentFilter.cs | 8 +- .../Fixtures/TestEnumSchemaFilter.cs | 4 +- .../Fixtures/TestOperationFilter.cs | 8 +- .../Fixtures/TestParameterFilter.cs | 15 +- .../Fixtures/TestRequestBodyFilter.cs | 15 +- .../Fixtures/TestSchemaFilter.cs | 13 +- .../JsonSerializerSchemaGeneratorTests.cs | 350 ++-- .../OpenApiAnyFactoryTests.cs | 141 -- .../OpenApiSchemaExtensionsTests.cs | 23 +- .../SwaggerGenerator/SwaggerGeneratorTests.cs | 228 +-- ...shbuckle.AspNetCore.SwaggerGen.Test.csproj | 4 + .../VerifyTests.cs | 3 +- .../XmlCommentsDocumentFilterTests.cs | 2 +- .../XmlCommentsExampleHelperTests.cs | 40 +- .../XmlCommentsOperationFilterTests.cs | 14 +- .../XmlCommentsParameterFilterTests.cs | 24 +- .../XmlCommentsRequestBodyFilterTests.cs | 25 +- .../XmlCommentsSchemaFilterTests.cs | 19 +- ...ndingSourceId=Body.DotNet10_0.verified.txt | 36 + ...indingSourceId=Body.DotNet8_0.verified.txt | 7 +- ...indingSourceId=Body.DotNet9_0.verified.txt | 7 +- ...ndingSourceId=Form.DotNet10_0.verified.txt | 46 + ...indingSourceId=Form.DotNet8_0.verified.txt | 7 +- ...indingSourceId=Form.DotNet9_0.verified.txt | 7 +- ...ctionHasFileResult.DotNet10_0.verified.txt | 35 + ...ActionHasFileResult.DotNet8_0.verified.txt | 7 +- ...ActionHasFileResult.DotNet9_0.verified.txt | 7 +- ...sObsoleteAttribute.DotNet10_0.verified.txt | 28 + ...asObsoleteAttribute.DotNet8_0.verified.txt | 7 +- ...asObsoleteAttribute.DotNet9_0.verified.txt | 7 +- ...sProducesAttribute.DotNet10_0.verified.txt | 35 + ...asProducesAttribute.DotNet8_0.verified.txt | 7 +- ...asProducesAttribute.DotNet9_0.verified.txt | 7 +- ...utNotWithIFormFile.DotNet10_0.verified.txt | 44 + ...ButNotWithIFormFile.DotNet8_0.verified.txt | 7 +- ...ButNotWithIFormFile.DotNet9_0.verified.txt | 7 +- ...eWithSwaggerIgnore.DotNet10_0.verified.txt | 46 + ...teWithSwaggerIgnore.DotNet8_0.verified.txt | 7 +- ...teWithSwaggerIgnore.DotNet9_0.verified.txt | 7 +- ...BindNeverAttribute.DotNet10_0.verified.txt | 27 + ...sBindNeverAttribute.DotNet8_0.verified.txt | 7 +- ...sBindNeverAttribute.DotNet9_0.verified.txt | 7 +- ...gerIgnoreAttribute.DotNet10_0.verified.txt | 27 + ...ggerIgnoreAttribute.DotNet8_0.verified.txt | 7 +- ...ggerIgnoreAttribute.DotNet9_0.verified.txt | 7 +- ...edOpenApiOperation.DotNet10_0.verified.txt | 38 + ...dpointNameMetadata.DotNet10_0.verified.txt | 28 + ...ndpointNameMetadata.DotNet8_0.verified.txt | 7 +- ...ndpointNameMetadata.DotNet9_0.verified.txt | 7 +- ...edOpenApiOperation.DotNet10_0.verified.txt | 24 + ...edOpenApiOperation.DotNet10_0.verified.txt | 40 + ...dedOpenApiMetadata.DotNet10_0.verified.txt | 21 + ...dRequiredAttribute.DotNet10_0.verified.txt | 37 + ...ndRequiredAttribute.DotNet8_0.verified.txt | 7 +- ...ndRequiredAttribute.DotNet9_0.verified.txt | 7 +- ...hRequiredAttribute.DotNet10_0.verified.txt | 37 + ...thRequiredAttribute.DotNet8_0.verified.txt | 7 +- ...thRequiredAttribute.DotNet9_0.verified.txt | 7 +- ...WithRequiredMember.DotNet10_0.verified.txt | 37 + ...nWithRequiredMember.DotNet8_0.verified.txt | 7 +- ...nWithRequiredMember.DotNet9_0.verified.txt | 7 +- ...dRequiredAttribute.DotNet10_0.verified.txt | 37 + ...ndRequiredAttribute.DotNet8_0.verified.txt | 7 +- ...ndRequiredAttribute.DotNet9_0.verified.txt | 7 +- ...hRequiredAttribute.DotNet10_0.verified.txt | 37 + ...thRequiredAttribute.DotNet8_0.verified.txt | 7 +- ...thRequiredAttribute.DotNet9_0.verified.txt | 7 +- ...ortedResponseTypes.DotNet10_0.verified.txt | 44 + ...portedResponseTypes.DotNet8_0.verified.txt | 7 +- ...portedResponseTypes.DotNet9_0.verified.txt | 7 +- ...hRouteNameMetadata.DotNet10_0.verified.txt | 28 + ...thRouteNameMetadata.DotNet8_0.verified.txt | 7 +- ...thRouteNameMetadata.DotNet9_0.verified.txt | 7 +- ...romHeaderParameter.DotNet10_0.verified.txt | 36 + ...FromHeaderParameter.DotNet8_0.verified.txt | 7 +- ...FromHeaderParameter.DotNet9_0.verified.txt | 7 +- ...romHeaderParameter.DotNet10_0.verified.txt | 36 + ...FromHeaderParameter.DotNet8_0.verified.txt | 7 +- ...FromHeaderParameter.DotNet9_0.verified.txt | 7 +- ...romHeaderParameter.DotNet10_0.verified.txt | 36 + ...FromHeaderParameter.DotNet8_0.verified.txt | 7 +- ...FromHeaderParameter.DotNet9_0.verified.txt | 7 +- ...hMatchingGroupName.DotNet10_0.verified.txt | 37 + ...thMatchingGroupName.DotNet8_0.verified.txt | 7 +- ...thMatchingGroupName.DotNet9_0.verified.txt | 7 +- ...nForBodyIsRequired.DotNet10_0.verified.txt | 31 + ...onForBodyIsRequired.DotNet8_0.verified.txt | 7 +- ...onForBodyIsRequired.DotNet9_0.verified.txt | 7 +- ...ingActionParameter.DotNet10_0.verified.txt | 37 + ...dingActionParameter.DotNet8_0.verified.txt | 7 +- ...dingActionParameter.DotNet9_0.verified.txt | 7 +- ...meterIsBoundToPath.DotNet10_0.verified.txt | 37 + ...ameterIsBoundToPath.DotNet8_0.verified.txt | 7 +- ...ameterIsBoundToPath.DotNet9_0.verified.txt | 7 +- ...ThatAreBoundToForm.DotNet10_0.verified.txt | 53 + ...sThatAreBoundToForm.DotNet8_0.verified.txt | 7 +- ...sThatAreBoundToForm.DotNet9_0.verified.txt | 7 +- ...ingSourceId=Header.DotNet10_0.verified.txt | 36 + ...dingSourceId=Header.DotNet8_0.verified.txt | 7 +- ...dingSourceId=Header.DotNet9_0.verified.txt | 7 +- ...ndingSourceId=Path.DotNet10_0.verified.txt | 37 + ...indingSourceId=Path.DotNet8_0.verified.txt | 7 +- ...indingSourceId=Path.DotNet9_0.verified.txt | 7 +- ...dingSourceId=Query.DotNet10_0.verified.txt | 36 + ...ndingSourceId=Query.DotNet8_0.verified.txt | 7 +- ...ndingSourceId=Query.DotNet9_0.verified.txt | 7 +- ...ndingSourceId=null.DotNet10_0.verified.txt | 36 + ...indingSourceId=null.DotNet8_0.verified.txt | 7 +- ...indingSourceId=null.DotNet9_0.verified.txt | 7 +- ...esolverIsSpecified.DotNet10_0.verified.txt | 27 + ...ResolverIsSpecified.DotNet8_0.verified.txt | 7 +- ...ResolverIsSpecified.DotNet9_0.verified.txt | 7 +- ...scriptionAttribute.DotNet10_0.verified.txt | 28 + ...escriptionAttribute.DotNet8_0.verified.txt | 7 +- ...escriptionAttribute.DotNet9_0.verified.txt | 7 +- ...asSummaryAttribute.DotNet10_0.verified.txt | 28 + ...HasSummaryAttribute.DotNet8_0.verified.txt | 7 +- ...HasSummaryAttribute.DotNet9_0.verified.txt | 7 +- ...intMetadataHasTags.DotNet10_0.verified.txt | 35 + ...ointMetadataHasTags.DotNet8_0.verified.txt | 13 +- ...ointMetadataHasTags.DotNet9_0.verified.txt | 13 +- ...om_GeneratedSchema.DotNet10_0.verified.txt | 65 + ...rom_GeneratedSchema.DotNet8_0.verified.txt | 7 +- ...rom_GeneratedSchema.DotNet9_0.verified.txt | 7 +- ...ationWithIFormFile.DotNet10_0.verified.txt | 36 + ...FormFileCollection.DotNet10_0.verified.txt | 39 + ...thSeveralFromForms.DotNet10_0.verified.txt | 73 + ...WithStringFromForm.DotNet10_0.verified.txt | 35 + ...hen_FromFormObject.DotNet10_0.verified.txt | 49 + ...When_FromFormObject.DotNet8_0.verified.txt | 7 +- ...When_FromFormObject.DotNet9_0.verified.txt | 7 +- ...rmObject_AndString.DotNet10_0.verified.txt | 66 + ...ormObject_AndString.DotNet8_0.verified.txt | 7 +- ...ormObject_AndString.DotNet9_0.verified.txt | 7 +- ...tadataTypeIsString.DotNet10_0.verified.txt | 48 + ...etadataTypeIsString.DotNet8_0.verified.txt | 7 +- ...etadataTypeIsString.DotNet9_0.verified.txt | 7 +- ...romHeaderParameter.DotNet10_0.verified.txt | 27 + ...romHeaderParameter.DotNet10_0.verified.txt | 27 + ...romHeaderParameter.DotNet10_0.verified.txt | 27 + ...gerIgnoreAttribute.DotNet10_0.verified.txt | 9 + ...electorIsSpecified.DotNet10_0.verified.txt | 51 + ...SelectorIsSpecified.DotNet8_0.verified.txt | 7 +- ...SelectorIsSpecified.DotNet9_0.verified.txt | 7 +- ...electorIsSpecified.DotNet10_0.verified.txt | 27 + ...SelectorIsSpecified.DotNet8_0.verified.txt | 7 +- ...SelectorIsSpecified.DotNet9_0.verified.txt | 7 +- ...ions.cs => JsonNodeExtensionExtensions.cs} | 8 +- .../Controllers/CrudActionsController.cs | 8 + test/WebSites/Basic/Startup.cs | 8 +- .../Swagger/AddCartsByIdGetExternalDocs.cs | 2 +- .../AssignOperationVendorExtensions.cs | 6 +- .../AssignRequestBodyVendorExtensions.cs | 11 +- .../Basic/Swagger/ExamplesSchemaFilter.cs | 21 +- .../wwwroot/swagger/v1/swagger_net10.0.json | 59 + .../wwwroot/swagger/v1/swagger_net8.0.json | 7 +- .../wwwroot/swagger/v1/swagger_net9.0.json | 7 +- .../wwwroot/swagger/v1/swagger_net10.0.json | 59 + .../wwwroot/swagger/v1/swagger_net8.0.json | 7 +- .../wwwroot/swagger/v1/swagger_net9.0.json | 7 +- .../DocumentSerializerTest.cs | 3 +- .../CustomDocumentSerializer/Startup.cs | 1 + .../AuthResponsesOperationFilter.cs | 2 +- .../AutoRestSchemaFilter.cs | 23 +- .../CustomDocumentSerializer.cs | 2 - .../DictionaryTKeyEnumTValueSchemaFilter.cs | 13 +- .../IServiceCollectionExtensions.cs | 24 +- .../DocumentationSnippets/ItemSchemaFilter.cs | 17 +- .../WebSites/DocumentationSnippets/Program.cs | 2 +- .../SecurityRequirementsOperationFilter.cs | 7 +- .../TagDescriptionsDocumentFilter.cs | 12 +- .../WebApplicationExtensions.cs | 8 +- test/WebSites/GenericControllers/Startup.cs | 2 +- .../Swagger/ApplySummariesOperationFilter.cs | 2 +- .../ConfigureSwaggerGenOptions.cs | 2 +- .../MvcWithNullable/MvcWithNullable.csproj | 8 - .../NswagClientExample.csproj | 1 + .../NswagClientExample/swagger_net10.0.json | 479 +++++ .../NswagClientExample/swagger_net8.0.json | 16 +- .../NswagClientExample/swagger_net9.0.json | 16 +- .../OAuth2Integration.csproj | 14 +- .../SecurityRequirementsOperationFilter.cs | 9 +- test/WebSites/OAuth2Integration/Startup.cs | 9 +- .../ApiTestsSetup.cs | 2 +- .../CreateUserTests.cs | 10 +- test/WebSites/WebApi.Aot/Program.cs | 2 +- .../WebApi/EndPoints/OpenApiEndpoints.cs | 26 +- .../EndPoints/SwaggerAnnotationsEndpoints.cs | 3 +- .../WebApi/EndPoints/XmlCommentsEndpoints.cs | 35 +- test/WebSites/WebApi/WebApi.csproj | 8 - 368 files changed, 12114 insertions(+), 2005 deletions(-) create mode 100644 docs/migrating-to-v10.md create mode 100644 src/Shared/JsonExtensions.cs delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Shipped.txt delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Unshipped.txt delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Shipped.txt delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Unshipped.txt delete mode 100644 src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OpenApiAnyFactory.cs create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/HttpServerFixture.cs create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/PlaywrightFixture.cs create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/UITests.cs create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomDocumentSerializer.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=TestFirst.Startup_swaggerRequestUri=v1-generated.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=TopLevelSwaggerDoc.Program_swaggerRequestUri=.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet10_0.verified.txt delete mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiAnyFactoryTests.cs create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithConsumesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithParameterAndProvidedOpenApiOperation.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProducesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProvidedOpenApiMetadata.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFile.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFileCollection.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithStringFromForm.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.OperationHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet10_0.verified.txt create mode 100644 test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet10_0.verified.txt rename test/Swashbuckle.AspNetCore.TestSupport/Extensions/{IOpenApiAnyExtensions.cs => JsonNodeExtensionExtensions.cs} (61%) create mode 100644 test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net10.0.json create mode 100644 test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net10.0.json create mode 100644 test/WebSites/NswagClientExample/swagger_net10.0.json diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cce780733f..18b5a96719 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -43,5 +43,5 @@ updates: - dependency-name: "Microsoft.AspNetCore.Mvc.NewtonsoftJson" - dependency-name: "Microsoft.AspNetCore.Mvc.Testing" - dependency-name: "Microsoft.OpenApi" - - dependency-name: "Microsoft.OpenApi.Readers" + - dependency-name: "Microsoft.OpenApi.YamlReader" - dependency-name: "xunit" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 0a51eb63f5..b5847a4f55 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,28 +1,28 @@ -name: dependency-review - -on: - pull_request: - branches: [ master, dotnet-vnext ] - -permissions: {} - -jobs: - dependency-review: - runs-on: ubuntu-latest - - permissions: - contents: read - - steps: - - - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - filter: 'tree:0' - persist-credentials: false - show-progress: false - - - name: Review dependencies +name: dependency-review + +on: + pull_request: + branches: [ master, dotnet-vnext ] + +permissions: {} + +jobs: + dependency-review: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + + - name: Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + filter: 'tree:0' + persist-credentials: false + show-progress: false + + - name: Review dependencies uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 - with: - allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' + with: + allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' diff --git a/Directory.Build.props b/Directory.Build.props index 9bd7c66fa0..0ba6096f29 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,7 +8,7 @@ https://github.com/domaindrivendev/Swashbuckle.AspNetCore true Copyright (c) 2016-$([System.DateTime]::Now.ToString(yyyy)) Richard Morris - net9.0;net8.0 + net10.0;net9.0;net8.0 true true $(IsPackable) @@ -37,7 +37,7 @@ snupkg true true - 9.0.7 + 10.0.0 false diff --git a/Directory.Packages.props b/Directory.Packages.props index 2b69a177ab..bbb67b60ea 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 7515b101b6..367b800163 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,17 @@ Once you have an API that can describe itself with a OpenAPI document, you've op tools including a client generator that can be targeted to a wide range of popular platforms. See [swagger-codegen][swagger-codegen] for more details. +> [!IMPORTANT] +> Version 10.0 of Swashbuckle.AspNetCore introduces breaking changes due to upgrading our dependency on [Microsoft.OpenApi][microsoft-openapi] +> to version 2.x.x to add support for generating OpenAPI 3.1 documents. Please see _[Migrating to Swashbuckle.AspNetCore v10][v10-migration]_ for more details. + ## Compatibility | Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | |---------------------|------------------------|-----------------------|-----------------------|------------|-------| -| [![CI Swashbuckle.AspNetCore version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Build.props&query=(%2F%2FProject%2FPropertyGroup%2FVersionPrefix)%5B1%5D&prefix=v&suffix=-*&logo=github&label=CI)](https://www.myget.org/gallery/domaindrivendev) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [![Latest v9 Swashbuckle.AspNetCore version](https://img.shields.io/github/v/release/domaindrivendev/Swashbuckle.AspNetCore?display_name=tag&logo=github&label=v9)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/latest) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | +| [![CI Swashbuckle.AspNetCore version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Build.props&query=(%2F%2FProject%2FPropertyGroup%2FVersionPrefix)%5B1%5D&prefix=v&suffix=-*&logo=github&label=CI)](https://www.myget.org/gallery/domaindrivendev) | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | +| [![Latest v10 Swashbuckle.AspNetCore version](https://img.shields.io/github/v/release/domaindrivendev/Swashbuckle.AspNetCore?display_name=tag&logo=github&label=v10)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/latest) | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | +| [![Last v9 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v9-v9.0.6-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | | [![Last v8 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v8-v8.1.4-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | | [![Last v7 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v7-v7.3.2-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | @@ -47,7 +52,7 @@ Next, register the OpenAPI (Swagger) generator in your application's startup pat ```cs -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; var builder = WebApplication.CreateBuilder(args); @@ -106,7 +111,7 @@ Then, expose the OpenAPI JSON document endpoint(s) using one of following method // Your own endpoints go here, and then... app.MapSwagger(); ``` -snippet source | anchor +snippet source | anchor @@ -118,7 +123,7 @@ app.MapSwagger(); ```cs app.UseSwagger(); ``` -snippet source | anchor +snippet source | anchor @@ -135,7 +140,7 @@ app.UseSwaggerUI(options => options.SwaggerEndpoint("v1/swagger.json", "My API V1"); }); ``` -snippet source | anchor +snippet source | anchor @@ -208,7 +213,7 @@ app.UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"); }); ``` -snippet source | anchor +snippet source | anchor @@ -282,6 +287,7 @@ Check out the table below for the full list of possible configuration options. | ------------- | ----------------------------------- | | **Swashbuckle.AspNetCore.Swagger** | [Change the Path for OpenAPI JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-openapi-json-endpoints) | | | [Modify OpenAPI with Request Context](docs/configure-and-customize-swagger.md#modify-openapi-with-request-context) | +| | [Serialize OpenAPI JSON in the 3.1 format](docs/configure-and-customize-swagger.md#serialize-openapi-in-the-31-format) | | | [Serialize Swagger JSON in the 2.0 format](docs/configure-and-customize-swagger.md#serialize-swagger-in-the-20-format) | | | [Working with Virtual Directories and Reverse Proxies](docs/configure-and-customize-swagger.md#working-with-virtual-directories-and-reverse-proxies) | | | [Customizing how the OpenAPI document is serialized](docs/configure-and-customize-swagger.md#customizing-how-the-openapi-document-is-serialized) | @@ -341,6 +347,7 @@ Check out the table below for the full list of possible configuration options. [help-wanted-badge]: https://img.shields.io/github/issues/domaindrivendev/Swashbuckle.AspNetCore/help-wanted?style=flat&color=%24EC820&label=Help%20wanted [help-wanted-issues]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/labels/help-wanted "Issues with help wanted for this project" [microelements-swashbuckle-fluentvalidation]: https://github.com/micro-elements/MicroElements.Swashbuckle.FluentValidation "MicroElements.Swashbuckle.FluentValidation on GitHub" +[microsoft-openapi]: https://www.nuget.org/packages/Microsoft.OpenApi "The Microsoft.OpenApi NuGet package" [mmlib-swaggerforocelot]: MMLib.SwaggerForOcelot "MMLib.SwaggerForOcelot on GitHub" [mvc-routing]: https://learn.microsoft.com/aspnet/core/mvc/controllers/routing "Routing to controller actions in ASP.NET Core" [newtonsoft-json]: https://www.nuget.org/packages/Newtonsoft.Json/ "Newtonsoft.Json NuGet package" @@ -365,3 +372,4 @@ Check out the table below for the full list of possible configuration options. [swagger-ui]: https://github.com/swagger-api/swagger-ui "The swagger-ui project in GitHub" [system-text-json]: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview "JSON serialization and deserialization in .NET - overview" [unchase-swashbuckle-aspnetcore-extensions]: https://github.com/unchase/Unchase.Swashbuckle.AspNetCore.Extensions "Unchase.Swashbuckle.AspNetCore.Extensions on GitHub" +[v10-migration]: docs/migrating-to-v10.md "Migrating to Swashbuckle.AspNetCore v10" diff --git a/docs/configure-and-customize-annotations.md b/docs/configure-and-customize-annotations.md index 1ec8f84c95..86c041deea 100644 --- a/docs/configure-and-customize-annotations.md +++ b/docs/configure-and-customize-annotations.md @@ -178,17 +178,20 @@ public class Item ```cs public class ItemSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Example = new OpenApiObject + if (schema is OpenApiSchema concrete) { - ["Id"] = new OpenApiInteger(1), - ["Description"] = new OpenApiString("An awesome item") - }; + concrete.Example = new JsonObject + { + ["Id"] = 1, + ["Description"] = "An awesome item" + }; + } } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/configure-and-customize-redoc.md b/docs/configure-and-customize-redoc.md index df34563adb..82f24a8166 100644 --- a/docs/configure-and-customize-redoc.md +++ b/docs/configure-and-customize-redoc.md @@ -13,7 +13,7 @@ app.UseReDoc(options => options.RoutePrefix = "docs"; }); ``` -snippet source | anchor +snippet source | anchor @@ -30,7 +30,7 @@ app.UseReDoc(options => options.DocumentTitle = "My API Docs"; }); ``` -snippet source | anchor +snippet source | anchor @@ -61,7 +61,7 @@ app.UseReDoc(options => options.SortPropsAlphabetically(); }); ``` -snippet source | anchor +snippet source | anchor @@ -82,7 +82,7 @@ app.UseReDoc(options => options.InjectStylesheet("/redoc/custom.css"); }); ``` -snippet source | anchor +snippet source | anchor @@ -101,7 +101,7 @@ app.UseReDoc(options => }; }); ``` -snippet source | anchor +snippet source | anchor @@ -119,7 +119,7 @@ app.UseReDoc(options => .GetManifestResourceStream("CustomIndex.ReDoc.index.html"); // Requires file to be added as an embedded resource }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/configure-and-customize-swagger.md b/docs/configure-and-customize-swagger.md index ef390e0bdd..3e80e350c9 100644 --- a/docs/configure-and-customize-swagger.md +++ b/docs/configure-and-customize-swagger.md @@ -17,7 +17,7 @@ app.UseSwagger(options => options.RouteTemplate = "api-docs/{documentName}/swagger.json"; }); ``` -snippet source | anchor +snippet source | anchor @@ -50,7 +50,7 @@ app.UseSwagger(options => }); }); ``` -snippet source | anchor +snippet source | anchor @@ -58,6 +58,25 @@ The `OpenApiDocument` and the current `HttpRequest` are both passed to the filte For example, you can add an explicit API server based on the `Host` header (as shown), or you could inspect session information or an `Authorization` header and remove operations from the document based on user permissions. +## Serialize OpenAPI in the 3.1 format + +By default, Swashbuckle.AspNetCore will generate and expose OpenAPI JSON in version 3.0 of the specification. +However, if you wish to use the latest version of the OpenAPI specification, you can opt into version 3.1 +format with the following option: + + + + +```cs +app.UseSwagger(options => +{ + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; +}); +``` +snippet source | anchor + + + ## Serialize Swagger in the 2.0 format By default, Swashbuckle will generate and expose OpenAPI JSON in version 3.0 of the specification, officially called the @@ -73,7 +92,7 @@ app.UseSwagger(options => options.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0; }); ``` -snippet source | anchor +snippet source | anchor @@ -98,7 +117,7 @@ app.UseSwaggerUI(options => options.SwaggerEndpoint("v1/swagger.json", "My API V1"); }); ``` -snippet source | anchor +snippet source | anchor @@ -136,6 +155,6 @@ app.UseSwagger(options => options.SetCustomDocumentSerializer(); }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/configure-and-customize-swaggergen.md b/docs/configure-and-customize-swaggergen.md index 6310c3ec27..53e187efd2 100644 --- a/docs/configure-and-customize-swaggergen.md +++ b/docs/configure-and-customize-swaggergen.md @@ -786,7 +786,7 @@ public class PhoneNumber ```cs services.AddSwaggerGen(options => { - options.MapType(() => new OpenApiSchema { Type = "string" }); + options.MapType(() => new OpenApiSchema { Type = JsonSchemaType.String }); }); ``` snippet source | anchor @@ -871,24 +871,27 @@ to inform the AutoRest tool how enums should be modelled when it generates the A ```cs public class AutoRestSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { var type = context.Type; - if (type.IsEnum) + if (type.IsEnum && schema is OpenApiSchema concrete) { - schema.Extensions.Add( + concrete.Extensions ??= new Dictionary(); + concrete.Extensions.Add( "x-ms-enum", - new OpenApiObject - { - ["name"] = new OpenApiString(type.Name), - ["modelAsString"] = new OpenApiBoolean(true) - } + new JsonNodeExtension( + new JsonObject + { + ["name"] = type.Name, + ["modelAsString"] = true + } + ) ); } } } ``` -snippet source | anchor +snippet source | anchor @@ -919,8 +922,13 @@ so you will need [a special JsonConverter, as shown in the .NET documentation](h ```cs public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { + if (schema is not OpenApiSchema concrete) + { + return; + } + // Only run for fields that are a Dictionary if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) { @@ -936,14 +944,14 @@ public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter return; } - schema.Type = "object"; - schema.Properties = keyType.GetEnumNames().ToDictionary( + concrete.Type = JsonSchemaType.Object; + concrete.Properties = keyType.GetEnumNames().ToDictionary( name => name, name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository)); } } ``` -snippet source | anchor +snippet source | anchor @@ -981,11 +989,11 @@ public class TagDescriptionsDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags = - [ - new OpenApiTag { Name = "Products", Description = "Browse/manage the product catalog" }, - new OpenApiTag { Name = "Orders", Description = "Submit orders" } - ]; + swaggerDoc.Tags = new HashSet() + { + new() { Name = "Products", Description = "Browse/manage the product catalog" }, + new() { Name = "Orders", Description = "Submit orders" } + }; } } ``` @@ -1051,19 +1059,13 @@ services.AddSwaggerGen(options => ```cs services.AddSwaggerGen(options => { - options.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement((document) => new OpenApiSecurityRequirement() { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }, - ["readAccess", "writeAccess"] - } + [new OpenApiSecuritySchemeReference("oauth2", document)] = ["readAccess", "writeAccess"] }); }); ``` -snippet source | anchor +snippet source | anchor @@ -1094,10 +1096,7 @@ public class SecurityRequirementsOperationFilter : IOperationFilter operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); - var scheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var scheme = new OpenApiSecuritySchemeReference("oauth2", context.Document); operation.Security = [ @@ -1110,7 +1109,7 @@ public class SecurityRequirementsOperationFilter : IOperationFilter } } ``` -snippet source | anchor +snippet source | anchor @@ -1133,19 +1132,13 @@ services.AddSwaggerGen(options => BearerFormat = "JWT", Description = "JWT Authorization header using the Bearer scheme." }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement(document => new OpenApiSecurityRequirement { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } - }, - [] - } + [new OpenApiSecuritySchemeReference("bearer", document)] = [] }); }); ``` -snippet source | anchor +snippet source | anchor @@ -1237,7 +1230,7 @@ services.AddSwaggerGen(options => }); }); ``` -snippet source | anchor +snippet source | anchor @@ -1319,7 +1312,7 @@ services.AddSwaggerGen(options => options.SelectDiscriminatorValueUsing((subType) => subType.Name); }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/configure-and-customize-swaggerui.md b/docs/configure-and-customize-swaggerui.md index 27af2bcac2..8dfcf90efa 100644 --- a/docs/configure-and-customize-swaggerui.md +++ b/docs/configure-and-customize-swaggerui.md @@ -13,7 +13,7 @@ app.UseSwaggerUI(options => options.RoutePrefix = "api-docs"; }); ``` -snippet source | anchor +snippet source | anchor @@ -31,7 +31,7 @@ app.UseSwaggerUI(options => options.DocumentTitle = "My Swagger UI"; }); ``` -snippet source | anchor +snippet source | anchor @@ -51,7 +51,7 @@ app.UseSwaggerUI(options => options.ScriptPresetsPath = "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.29.1/swagger-ui-standalone-preset.min.js"; }); ``` -snippet source | anchor +snippet source | anchor @@ -71,7 +71,7 @@ app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v2/swagger.json", "V2 Docs"); }); ``` -snippet source | anchor +snippet source | anchor @@ -106,7 +106,7 @@ app.UseSwaggerUI(options => options.UseResponseInterceptor("(response) => { return response; }"); }); ``` -snippet source | anchor +snippet source | anchor @@ -124,7 +124,7 @@ app.UseSwaggerUI(options => options.InjectJavascript("/swagger-ui/custom.js"); }); ``` -snippet source | anchor +snippet source | anchor @@ -142,7 +142,7 @@ app.UseSwaggerUI(options => options.InjectStylesheet("/swagger-ui/custom.css"); }); ``` -snippet source | anchor +snippet source | anchor @@ -160,7 +160,7 @@ app.UseSwaggerUI(options => .GetManifestResourceStream("CustomUIIndex.Swagger.index.html"); // Requires file to be added as an embedded resource }); ``` -snippet source | anchor +snippet source | anchor @@ -205,7 +205,7 @@ app.UseSwaggerUI(options => options.OAuthUsePkce(); }); ``` -snippet source | anchor +snippet source | anchor @@ -224,7 +224,7 @@ app.UseSwaggerUI(options => options.UseResponseInterceptor("(res) => { console.log('Custom interceptor intercepted response from:', res.url); return res; }"); }); ``` -snippet source | anchor +snippet source | anchor @@ -239,7 +239,7 @@ app.UseSwaggerUI(options => options.UseRequestInterceptor("(req) => { req.headers['X-XSRF-Token'] = localStorage.getItem('xsrf-token'); return req; }"); }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/migrating-to-v10.md b/docs/migrating-to-v10.md new file mode 100644 index 0000000000..76c859f172 --- /dev/null +++ b/docs/migrating-to-v10.md @@ -0,0 +1,92 @@ +# Migrating to Swashbuckle.AspNetCore v10 + +> [!IMPORTANT] +> This document describes the breaking changes introduced in Swashbuckle.AspNetCore v10, and how to migrate from v9.x to v10+. + +## Why breaking changes? + +While the [OpenAPI 3.1 specification][openapi-specification] is a minor release compared to OpenAPI 3.0, the OpenAPI specification does +not use Semantic Versioning (SemVer). The changes introduced between the two versions are quite breaking in a practical sense, so major +changes were required to be made to [Microsoft.OpenApi][microsoft-openapi-package], the package which Swashbuckle.AspNetCore builds upon, +in order to allow applications to produce OpenAPI 3.1 documents. + +These changes were introduced in [Microsoft.OpenApi v2.0.0][microsoft-openapi-v2-migration-guide], which _does_ follow SemVer. As a result, +Swashbuckle.AspNetCore v10+ now depends on Microsoft.OpenApi v2+ to allow users to produce OpenAPI 3.1 documents, fulfilling a +long-standing feature request: [Plans on official support for OpenApi 3.1.0 #2349][feature-request]. + +These changes are unfortunately required, even if you still wish to target Swagger 2.0 or OpenAPI 3.0 documents, as the same library is used +to produce all three document format versions. + +For the same breaking changes, ASP.NET Core v10+ also depends on Microsoft.OpenApi v2+, so these changes were also required to allow applications +using ASP.NET Core 10 to use Swashbuckle.AspNetCore effectively with minimal friction. This also helps support users who may wish to migrate an +application from Swashbuckle.AspNetCore to Microsoft.AspNetCore.OpenApi (for example if they need native AoT support). More information about the +breaking changes in ASP.NET Core 10 can be found in this document: _[What's new in ASP.NET Core in .NET 10][breaking-changes-aspnetcore]_. + +The refactoring required to support OpenAPI 3.1 in Swashbuckle.AspNetCore was significant. If you're interested in what exactly what was changed, +you can check out the PR to implement it that was worked on over the course of .NET 10's development (it's quite large): _[Support .NET 10 #3283][swashbuckle-aspnetcore-10]_. + +## How do I enable OpenAPI 3.1 support? + +By default, to minimise breaking _behavioural_ changes, Swashbuckle.AspNetCore v10+ will continue to produce OpenAPI 3.0 documents by default. + +To upgrade your OpenAPI documents to output using version 3.1 of the OpenAPI specification, you can override the version as shown in the code snippet below. + + + + +```cs +app.UseSwagger(options => +{ + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; +}); +``` +snippet source | anchor + + + +## How do I migrate to Swashbuckle.AspNetCore v10+? + +> [!TIP] +> It is strongly recommended that you upgrade to [Swashbuckle.AspNetCore v9.0.6][swashbuckle-aspnetcore-906] **before** upgrading to v10. + +The majority of the breaking changes you may encounter when migrating to Swashbuckle.AspNetCore v10+ are due to the underlying +changes in Microsoft.OpenApi v2.0.0+. The [v2 migration guide for Microsoft.OpenApi][microsoft-openapi-v2-migration-guide] provides a +detailed overview of the breaking changes and how to migrate your code from Microsoft.OpenApi v1.x to v2.x. The Microsoft.OpenApi +breaking changes document should be your primary reference when migrating to Swashbuckle.AspNetCore v10+. + +The chances of encountering breaking changes when migrating to Swashbuckle.AspNetCore v10+ depend on how extensively you use the +Swashbuckle.AspNetCore extensibility points, such as custom filters, to generate the OpenAPI document. If you do not use any custom logic +and do not depend on any other third-party libraries that depend on Microsoft.OpenApi v1.x, you may not encounter any breaking changes at all. + +In cases where Swashbuckle.AspNetCore has a breaking change, this is because the types from the underlying Microsoft.OpenApi library +are exposed in the public API of Swashbuckle.AspNetCore, so the breaking change bubbles up through the public API surface to cause compilation +issues in your code. For example the signature for `IRequestBodyAsyncFilter.ApplyAsync()` has changed as shown below: + +```diff +- Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task ++ Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +``` + +## Migration Overview + +Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the following areas: + +- Update any NuGet package references for Swashbuckle.AspNetCore and Microsoft.OpenApi to v10.0.0+ and v2.3.0+ respectively. +- Update any `using` directives that reference types from the `Microsoft.OpenApi.Models` namespace to use the new namespace `Microsoft.OpenApi`. +- Update model references (e.g. `OpenApiSchema`) to use the new interfaces (e.g. `IOpenApiSchema`) and the relevant concrete types to mutate them (e.g. `OpenApiSchema`). +- Update any use of `.Reference` properties (e.g. `OpenApiSchema.ReferenceV3`) to use the new `*Reference` class instead (e.g. `OpenApiSchemaReference`). +- Replace usage of the `OpenApiSchema.Type` property using a string (e.g. `"string"` or `"boolean"`) with the `JsonSchemaType` flags enumeration. +- Replace usage of the `OpenApiSchema.Nullable` property by OR-ing the `JsonSchemaType.Null` value to `OpenApiSchema.Type` (e.g. `schema.Type |= JsonSchemaType.Null;`). +- Remove any use of the [now-deprecated `WithOpenApi()` extension method][withopenapi-deprecation] in [Microsoft.AspNetCore.OpenApi][microsoft-aspnetcore-openapi-package]. +- Updating any use of `AddSecurityRequirement()` to use a `Func`. +- Updating some collections to use specific concrete types instead of interfaces if collection expressions were used, for example using `HashSet` with `OpenApiDocument.Tags` which is now an `ISet`. + +[breaking-changes-aspnetcore]: https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-10.0?#openapi-31-breaking-changes "OpenAPI 3.1 breaking changes" +[feature-request]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2349 "Plans on official support for OpenApi 3.1.0" +[microsoft-aspnetcore-openapi-package]: https://www.nuget.org/packages/Microsoft.AspNetCore.OpenApi/ "Microsoft.AspNetCore.OpenApi NuGet package" +[microsoft-openapi-package]: https://www.nuget.org/packages/Microsoft.OpenApi/ "Microsoft.OpenApi NuGet package" +[microsoft-openapi-v2-migration-guide]: https://github.com/microsoft/OpenAPI.NET/blob/main/docs/upgrade-guide-2.md "Microsoft OpenAPI.NET v2 migration guide" +[openapi-specification]: https://swagger.io/specification/ "OpenAPI Specification" +[swashbuckle-aspnetcore-10]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3283 "Support .NET 10" +[withopenapi-deprecation]: https://github.com/aspnet/Announcements/issues/519 "[Breaking change]: Deprecation of WithOpenApi extension method" +[swashbuckle-aspnetcore-906]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6 "Swashbuckle.AspNetCore v9.0.6" diff --git a/global.json b/global.json index 909a1fb9ce..136c4f6ad1 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.306", + "version": "10.0.100", "allowPrerelease": false, "rollForward": "latestMajor", "paths": [ ".dotnet", "$host$" ] diff --git a/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj b/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj index 718d1dfc14..b876a81442 100644 --- a/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj +++ b/perf/Swashbuckle.AspNetCore.Benchmarks/Swashbuckle.AspNetCore.Benchmarks.csproj @@ -2,7 +2,7 @@ false Exe - net9.0 + net10.0 True $(MSBuildThisFileDirectory)..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.snk diff --git a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs index f50fe1f485..ccdb2cbb76 100644 --- a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs +++ b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs @@ -4,7 +4,7 @@ using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen.Test; using Swashbuckle.AspNetCore.TestSupport; @@ -57,7 +57,9 @@ public void Setup() using var xmlStream = new MemoryStream(); xmlDocument.Save(xmlStream); xmlStream.Seek(0, SeekOrigin.Begin); + var xPathDocument = new XPathDocument(xmlStream); + var members = XmlCommentsDocumentHelper.CreateMemberDictionary(xPathDocument); // Document _document = new OpenApiDocument(); @@ -83,7 +85,7 @@ public void Setup() null, null); - _documentFilter = new XmlCommentsDocumentFilter(xPathDocument); + _documentFilter = new XmlCommentsDocumentFilter(members, new()); // Operation _operation = new OpenApiOperation(); @@ -91,13 +93,13 @@ public void Setup() .GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithSummaryAndResponseTags)); var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource"); - _operationFilterContext = new OperationFilterContext(apiDescription, null, null, methodInfo); - _operationFilter = new XmlCommentsOperationFilter(xPathDocument); + _operationFilterContext = new OperationFilterContext(apiDescription, null, null, null, methodInfo); + _operationFilter = new XmlCommentsOperationFilter(members, new()); // Parameter _parameter = new() { - Schema = new() + Schema = new OpenApiSchema() { Type = JsonSchemaTypes.String, Description = "schema-level description", @@ -106,8 +108,9 @@ public void Setup() var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty)); var apiParameterDescription = new ApiParameterDescription(); - _parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo); - _parameterFilter = new XmlCommentsParameterFilter(xPathDocument); + var xmlDocMembers = XmlCommentsDocumentHelper.CreateMemberDictionary(xPathDocument); + _parameterFilterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, propertyInfo: propertyInfo); + _parameterFilter = new XmlCommentsParameterFilter(xmlDocMembers, new()); // Request Body _requestBody = new OpenApiRequestBody @@ -116,7 +119,7 @@ public void Setup() { ["application/json"] = new() { - Schema = new() + Schema = new OpenApiSchema() { Type = JsonSchemaTypes.String, }, @@ -131,8 +134,8 @@ public void Setup() { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - _requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); - _requestBodyFilter = new XmlCommentsRequestBodyFilter(xPathDocument); + _requestBodyFilterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); + _requestBodyFilter = new XmlCommentsRequestBodyFilter(xmlDocMembers, new()); } [Benchmark] diff --git a/src/Shared/JsonExtensions.cs b/src/Shared/JsonExtensions.cs new file mode 100644 index 0000000000..cd314a1f5e --- /dev/null +++ b/src/Shared/JsonExtensions.cs @@ -0,0 +1,26 @@ +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Swashbuckle.AspNetCore; + +internal static class JsonExtensions +{ + private static readonly JsonSerializerOptions Options = new() + { +#if NET9_0_OR_GREATER + NewLine = "\n", +#endif + WriteIndented = true, + }; + + public static string ToJson(this JsonNode value) + { + var json = value.ToJsonString(Options); + +#if !NET9_0_OR_GREATER + json = json.Replace("\r\n", "\n"); +#endif + + return json; + } +} diff --git a/src/Shared/JsonSchemaTypes.cs b/src/Shared/JsonSchemaTypes.cs index 7285f9e9e1..693640ceca 100644 --- a/src/Shared/JsonSchemaTypes.cs +++ b/src/Shared/JsonSchemaTypes.cs @@ -1,12 +1,14 @@ +using Microsoft.OpenApi; + namespace Swashbuckle.AspNetCore; internal static class JsonSchemaTypes { - public const string Array = "array"; - public const string Boolean = "boolean"; - public const string Integer = "integer"; - public const string Number = "number"; - public const string Null = "null"; - public const string Object = "object"; - public const string String = "string"; + public static readonly JsonSchemaType Array = JsonSchemaType.Array; + public static readonly JsonSchemaType Boolean = JsonSchemaType.Boolean; + public static readonly JsonSchemaType Integer = JsonSchemaType.Integer; + public static readonly JsonSchemaType Number = JsonSchemaType.Number; + public static readonly JsonSchemaType Null = JsonSchemaType.Null; + public static readonly JsonSchemaType Object = JsonSchemaType.Object; + public static readonly JsonSchemaType String = JsonSchemaType.String; } diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs index a54ed0e838..412780804c 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations; @@ -8,7 +8,7 @@ public class AnnotationsDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags ??= []; + swaggerDoc.Tags ??= new SortedSet(); // Collect (unique) controller names and custom attributes in a dictionary var controllerNamesAndAttributes = context.ApiDescriptions @@ -24,7 +24,7 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) } private static void ApplySwaggerTagAttribute( - OpenApiDocument swaggerDoc, + OpenApiDocument document, string controllerName, IEnumerable customAttributes) { @@ -32,18 +32,24 @@ private static void ApplySwaggerTagAttribute( .OfType() .FirstOrDefault(); - if (swaggerTagAttribute == null) + if (swaggerTagAttribute is null) { return; } - swaggerDoc.Tags.Add(new OpenApiTag + var tag = document.Tags.FirstOrDefault((p) => p?.Name == controllerName); + + if (tag is null) + { + tag = new() { Name = controllerName }; + document.Tags.Add(tag); + } + + tag.Description ??= swaggerTagAttribute.Description; + + if (swaggerTagAttribute.ExternalDocsUrl is { } url) { - Name = controllerName, - Description = swaggerTagAttribute.Description, - ExternalDocs = (swaggerTagAttribute.ExternalDocsUrl != null) - ? new OpenApiExternalDocs { Url = new Uri(swaggerTagAttribute.ExternalDocsUrl) } - : null - }); + tag.ExternalDocs ??= new OpenApiExternalDocs { Url = new(url) }; + } } } diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs index e19c077520..25c960fccc 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations; @@ -69,7 +69,7 @@ private static void ApplySwaggerOperationAttribute( if (swaggerOperationAttribute.Tags is { } tags) { - operation.Tags = [.. tags.Select(tagName => new OpenApiTag { Name = tagName })]; + operation.Tags = new SortedSet(tags.Select(tagName => new OpenApiTagReference(tagName))); } } @@ -113,9 +113,11 @@ private static void ApplySwaggerResponseAttributes( operation.Responses[statusCode] = response; - if (swaggerResponseAttribute.ContentTypes is { } contentTypes) + if (response is OpenApiResponse concrete && + swaggerResponseAttribute.ContentTypes is { } contentTypes) { - response.Content.Clear(); + concrete.Content?.Clear(); + concrete.Content ??= new Dictionary(); foreach (var contentType in contentTypes) { @@ -123,7 +125,7 @@ private static void ApplySwaggerResponseAttributes( ? context.SchemaGenerator.GenerateSchema(swaggerResponseAttribute.Type, context.SchemaRepository) : null; - response.Content.Add(contentType, new OpenApiMediaType { Schema = schema }); + concrete.Content.Add(contentType, new OpenApiMediaType { Schema = schema }); } } } diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsParameterFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsParameterFilter.cs index 446dd1d90b..cdb1712a3c 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsParameterFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsParameterFilter.cs @@ -1,24 +1,25 @@ using System.Reflection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations; public class AnnotationsParameterFilter : IParameterFilter { - public void Apply(OpenApiParameter parameter, ParameterFilterContext context) + public void Apply(IOpenApiParameter parameter, ParameterFilterContext context) { - if (context.PropertyInfo != null) + if (context.PropertyInfo is { } propertyInfo) { - ApplyPropertyAnnotations(parameter, context.PropertyInfo); + ApplyPropertyAnnotations(parameter, propertyInfo); } - else if (context.ParameterInfo != null) + + if (context.ParameterInfo is { } parameterInfo) { - ApplyParamAnnotations(parameter, context.ParameterInfo); + ApplyParamAnnotations(parameter, parameterInfo); } } - private static void ApplyPropertyAnnotations(OpenApiParameter parameter, PropertyInfo propertyInfo) + private static void ApplyPropertyAnnotations(IOpenApiParameter parameter, PropertyInfo propertyInfo) { var swaggerParameterAttribute = propertyInfo.GetCustomAttributes() .FirstOrDefault(); @@ -29,7 +30,7 @@ private static void ApplyPropertyAnnotations(OpenApiParameter parameter, Propert } } - private static void ApplyParamAnnotations(OpenApiParameter parameter, ParameterInfo parameterInfo) + private static void ApplyParamAnnotations(IOpenApiParameter parameter, ParameterInfo parameterInfo) { var swaggerParameterAttribute = parameterInfo.GetCustomAttribute(); @@ -39,7 +40,7 @@ private static void ApplyParamAnnotations(OpenApiParameter parameter, ParameterI } } - private static void ApplySwaggerParameterAttribute(OpenApiParameter parameter, SwaggerParameterAttribute swaggerParameterAttribute) + private static void ApplySwaggerParameterAttribute(IOpenApiParameter parameter, SwaggerParameterAttribute swaggerParameterAttribute) { if (swaggerParameterAttribute.Description is { } description) { diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsRequestBodyFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsRequestBodyFilter.cs index 38c846ccd0..7cc7a61352 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsRequestBodyFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsRequestBodyFilter.cs @@ -1,12 +1,12 @@ using System.Reflection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations; public class AnnotationsRequestBodyFilter : IRequestBodyFilter { - public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context) + public void Apply(IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { var bodyParameterDescription = context.BodyParameterDescription; @@ -19,18 +19,16 @@ public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext conte if (propertyInfo != null) { ApplyPropertyAnnotations(requestBody, propertyInfo); - return; } var parameterInfo = bodyParameterDescription.ParameterInfo(); if (parameterInfo != null) { ApplyParamAnnotations(requestBody, parameterInfo); - return; } } - private static void ApplyPropertyAnnotations(OpenApiRequestBody parameter, PropertyInfo propertyInfo) + private static void ApplyPropertyAnnotations(IOpenApiRequestBody parameter, PropertyInfo propertyInfo) { var swaggerRequestBodyAttribute = propertyInfo.GetCustomAttributes() .FirstOrDefault(); @@ -41,7 +39,7 @@ private static void ApplyPropertyAnnotations(OpenApiRequestBody parameter, Prope } } - private static void ApplyParamAnnotations(OpenApiRequestBody requestBody, ParameterInfo parameterInfo) + private static void ApplyParamAnnotations(IOpenApiRequestBody requestBody, ParameterInfo parameterInfo) { var swaggerRequestBodyAttribute = parameterInfo.GetCustomAttribute(); @@ -51,7 +49,7 @@ private static void ApplyParamAnnotations(OpenApiRequestBody requestBody, Parame } } - private static void ApplySwaggerRequestBodyAttribute(OpenApiRequestBody parameter, SwaggerRequestBodyAttribute swaggerRequestBodyAttribute) + private static void ApplySwaggerRequestBodyAttribute(IOpenApiRequestBody parameter, SwaggerRequestBodyAttribute swaggerRequestBodyAttribute) { if (swaggerRequestBodyAttribute.Description is { } description) { diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSchemaFilter.cs index ae0829161b..58fcd2c90f 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsSchemaFilter.cs @@ -1,6 +1,6 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations; @@ -9,7 +9,7 @@ public class AnnotationsSchemaFilter(IServiceProvider serviceProvider) : ISchema { private readonly IServiceProvider _serviceProvider = serviceProvider; - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { ApplyTypeAnnotations(schema, context); @@ -26,7 +26,7 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context) } } - private void ApplyTypeAnnotations(OpenApiSchema schema, SchemaFilterContext context) + private void ApplyTypeAnnotations(IOpenApiSchema schema, SchemaFilterContext context) { var schemaAttribute = context.Type.GetCustomAttributes() .FirstOrDefault(); @@ -50,7 +50,7 @@ private void ApplyTypeAnnotations(OpenApiSchema schema, SchemaFilterContext cont } } - private static void ApplyParamAnnotations(OpenApiSchema schema, ParameterInfo parameterInfo) + private static void ApplyParamAnnotations(IOpenApiSchema schema, ParameterInfo parameterInfo) { var schemaAttribute = parameterInfo.GetCustomAttributes() .FirstOrDefault(); @@ -61,7 +61,7 @@ private static void ApplyParamAnnotations(OpenApiSchema schema, ParameterInfo pa } } - private static void ApplyMemberAnnotations(OpenApiSchema schema, MemberInfo memberInfo) + private static void ApplyMemberAnnotations(IOpenApiSchema schema, MemberInfo memberInfo) { var schemaAttribute = memberInfo.GetCustomAttributes() .FirstOrDefault(); @@ -72,41 +72,55 @@ private static void ApplyMemberAnnotations(OpenApiSchema schema, MemberInfo memb } } - private static void ApplySchemaAttribute(OpenApiSchema schema, SwaggerSchemaAttribute schemaAttribute) + private static void ApplySchemaAttribute(IOpenApiSchema schema, SwaggerSchemaAttribute schemaAttribute) { if (schemaAttribute.Description is { } description) { schema.Description = description; } + if (schema is not OpenApiSchema concrete) + { + return; + } + if (schemaAttribute.Format is { } format) { - schema.Format = format; + concrete.Format = format; } if (schemaAttribute.ReadOnlyFlag is { } readOnly) { - schema.ReadOnly = readOnly; + concrete.ReadOnly = readOnly; } if (schemaAttribute.WriteOnlyFlag is { } writeOnly) { - schema.WriteOnly = writeOnly; + concrete.WriteOnly = writeOnly; } if (schemaAttribute.NullableFlag is { } nullable) { - schema.Nullable = nullable; + // See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/3387 + if (nullable) + { + concrete.Type ??= JsonSchemaType.Null; + concrete.Type |= JsonSchemaType.Null; + } + else if (concrete.Type.HasValue) + { + concrete.Type &= ~JsonSchemaType.Null; + } } if (schemaAttribute.Required is { } required) { - schema.Required = new SortedSet(required); + concrete.Required = new SortedSet(required); } if (schemaAttribute.Title is { } title) { - schema.Title = title; + concrete.Title = title; } } } diff --git a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt index 1988ce538e..95e1451167 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt @@ -1,22 +1,16 @@ Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions static Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions.EnableAnnotations(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions options) -> void static Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions.EnableAnnotations(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions options, bool enableAnnotationsForInheritance, bool enableAnnotationsForPolymorphism) -> void -static Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.ApplySwaggerOperationFilterAttributes(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Collections.Generic.IEnumerable controllerAndActionAttributes) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.AnnotationsDocumentFilter() -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.Apply(Microsoft.OpenApi.Models.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.AnnotationsOperationFilter() -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.Apply(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.AnnotationsParameterFilter() -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.Apply(Microsoft.OpenApi.Models.OpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.AnnotationsRequestBodyFilter() -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.Apply(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.AnnotationsSchemaFilter(System.IServiceProvider serviceProvider) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.Apply(Microsoft.OpenApi.Models.OpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute.PropertyName.get -> string Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute.PropertyName.set -> void diff --git a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt index e69de29bb2..72edd514e5 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1,6 @@ +static Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.ApplySwaggerOperationFilterAttributes(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Collections.Generic.IEnumerable controllerAndActionAttributes) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/ApiTestFixture.cs b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/ApiTestFixture.cs index b3f31dc22b..4441d637fb 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/ApiTestFixture.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/ApiTestFixture.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Xunit; namespace Swashbuckle.AspNetCore.ApiTesting.Xunit; @@ -15,7 +15,7 @@ public class ApiTestFixture( private readonly WebApplicationFactory _webAppFactory = webAppFactory; private readonly string _documentName = documentName; - public void Describe(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + public void Describe(string pathTemplate, HttpMethod operationType, OpenApiOperation operationSpec) { _apiTestRunner.ConfigureOperation(_documentName, pathTemplate, operationType, operationSpec); } diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt index 3b608f0e78..c36b8bf81c 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,3 @@ Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.ApiTestFixture(Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase apiTestRunner, Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory webAppFactory, string documentName) -> void -Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.Describe(string pathTemplate, Microsoft.OpenApi.Models.OperationType operationType, Microsoft.OpenApi.Models.OpenApiOperation operationSpec) -> void Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.TestAsync(string operationId, string expectedStatusCode, System.Net.Http.HttpRequestMessage request) -> System.Threading.Tasks.Task diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt index e69de29bb2..4b3b9b0954 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.Describe(string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operationSpec) -> void diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerBase.cs b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerBase.cs index 44580fea53..cd6b23a3de 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerBase.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerBase.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; @@ -24,7 +23,7 @@ public void Configure(Action setupAction) public void ConfigureOperation( string documentName, string pathTemplate, - OperationType operationType, + HttpMethod operationType, OpenApiOperation operation) { var openApiDocument = _options.GetOpenApiDocument(documentName); diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptions.cs b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptions.cs index 0aaad418f2..8ec809aa19 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptions.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptions.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptionsExtensions.cs b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptionsExtensions.cs index d99c6dce55..15a148ec20 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptionsExtensions.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/ApiTestRunnerOptionsExtensions.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; @@ -8,9 +7,13 @@ public static class ApiTestRunnerOptionsExtensions public static void AddOpenApiFile(this ApiTestRunnerOptions options, string documentName, string filePath) { using var fileStream = File.OpenRead(filePath); + using var memoryStream = new MemoryStream(); - var openApiDocument = new OpenApiStreamReader().Read(fileStream, out var diagnostic); - options.OpenApiDocs.Add(documentName, openApiDocument); + fileStream.CopyTo(memoryStream); + memoryStream.Seek(0, SeekOrigin.Begin); + + var result = OpenApiDocument.Load(memoryStream); + options.OpenApiDocs.Add(documentName, result.Document); } public static OpenApiDocument GetOpenApiDocument(this ApiTestRunnerOptions options, string documentName) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/IContentValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/IContentValidator.cs index 220270962b..6cd0e20cc1 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/IContentValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/IContentValidator.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonContentValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonContentValidator.cs index 34f8af5a02..c850e528ab 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonContentValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonContentValidator.cs @@ -1,9 +1,9 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonContentValidator : IContentValidator +public sealed class JsonContentValidator : IContentValidator { private readonly JsonValidator _jsonValidator = new(); diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/IJsonValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/IJsonValidator.cs index 22874ec0d3..15b08c8e5f 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/IJsonValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/IJsonValidator.cs @@ -1,14 +1,14 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; public interface IJsonValidator { - bool CanValidate(OpenApiSchema schema); + bool CanValidate(IOpenApiSchema schema); bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages); diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAllOfValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAllOfValidator.cs index c9751582bd..3fb9b1d01e 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAllOfValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAllOfValidator.cs @@ -1,16 +1,16 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonAllOfValidator(JsonValidator jsonValidator) : IJsonValidator +public sealed class JsonAllOfValidator(JsonValidator jsonValidator) : IJsonValidator { private readonly JsonValidator _jsonValidator = jsonValidator; - public bool CanValidate(OpenApiSchema schema) => schema.AllOf != null && schema.AllOf.Any(); + public bool CanValidate(IOpenApiSchema schema) => schema.AllOf != null && schema.AllOf.Any(); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAnyOfValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAnyOfValidator.cs index 50e0d602b5..cccb037412 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAnyOfValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonAnyOfValidator.cs @@ -1,16 +1,16 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonAnyOfValidator(JsonValidator jsonValidator) : IJsonValidator +public sealed class JsonAnyOfValidator(JsonValidator jsonValidator) : IJsonValidator { private readonly JsonValidator _jsonValidator = jsonValidator; - public bool CanValidate(OpenApiSchema schema) => schema.AnyOf != null && schema.AnyOf.Any(); + public bool CanValidate(IOpenApiSchema schema) => schema.AnyOf != null && schema.AnyOf.Any(); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonArrayValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonArrayValidator.cs index c6d297014f..87e3241f84 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonArrayValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonArrayValidator.cs @@ -1,16 +1,16 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonArrayValidator(IJsonValidator jsonValidator) : IJsonValidator +public sealed class JsonArrayValidator(IJsonValidator jsonValidator) : IJsonValidator { private readonly IJsonValidator _jsonValidator = jsonValidator; - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.Array; + public bool CanValidate(IOpenApiSchema schema) => schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonBooleanValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonBooleanValidator.cs index 9e06b42ac3..4e40b5b894 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonBooleanValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonBooleanValidator.cs @@ -1,14 +1,14 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonBooleanValidator : IJsonValidator +public sealed class JsonBooleanValidator : IJsonValidator { - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.Boolean; + public bool CanValidate(IOpenApiSchema schema) => schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Boolean); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNullValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNullValidator.cs index 8ddf15b53c..fb54f41150 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNullValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNullValidator.cs @@ -1,14 +1,14 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonNullValidator : IJsonValidator +public sealed class JsonNullValidator : IJsonValidator { - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.Null; + public bool CanValidate(IOpenApiSchema schema) => schema.Type == JsonSchemaTypes.Null; public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNumberValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNumberValidator.cs index 99cafbd2a2..8bb2112cf2 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNumberValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonNumberValidator.cs @@ -1,14 +1,14 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonNumberValidator : IJsonValidator +public sealed class JsonNumberValidator : IJsonValidator { - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.Number; + public bool CanValidate(IOpenApiSchema schema) => schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Number); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) @@ -23,39 +23,37 @@ public bool Validate( var errors = new List(); // multipleOf - if (schema.MultipleOf.HasValue && ((numberValue % schema.MultipleOf.Value) != 0)) + if (schema.MultipleOf is { } multipleOf && (numberValue % multipleOf) != 0) { errors.Add($"Path: {instance.Path}. Number is not evenly divisible by multipleOf"); } - // maximum & exclusiveMaximum - if (schema.Maximum.HasValue) + if (schema.ExclusiveMaximum is { } exclusiveMaximum && + decimal.TryParse(exclusiveMaximum, out var exclusiveMaximumValue) && + numberValue >= exclusiveMaximumValue) { - var exclusiveMaximum = schema.ExclusiveMaximum ?? false; + errors.Add($"Path: {instance.Path}. Number is greater than, or equal to, maximum"); + } - if (exclusiveMaximum && (numberValue >= schema.Maximum.Value)) - { - errors.Add($"Path: {instance.Path}. Number is greater than, or equal to, maximum"); - } - else if (numberValue > schema.Maximum.Value) - { - errors.Add($"Path: {instance.Path}. Number is greater than maximum"); - } + if (schema.Maximum is { } maximum && + decimal.TryParse(maximum, out var maximumValue) && + numberValue > maximumValue) + { + errors.Add($"Path: {instance.Path}. Number is greater than maximum"); } - // minimum & exclusiveMinimum - if (schema.Minimum.HasValue) + if (schema.ExclusiveMinimum is { } exclusiveMinimum && + decimal.TryParse(exclusiveMinimum, out var exclusiveMinimumValue) && + numberValue <= exclusiveMinimumValue) { - var exclusiveMinimum = schema.ExclusiveMinimum ?? false; + errors.Add($"Path: {instance.Path}. Number is less than, or equal to, minimum"); + } - if (exclusiveMinimum && (numberValue <= schema.Minimum.Value)) - { - errors.Add($"Path: {instance.Path}. Number is less than, or equal to, minimum"); - } - else if (numberValue < schema.Minimum.Value) - { - errors.Add($"Path: {instance.Path}. Number is less than minimum"); - } + if (schema.Minimum is { } minimum && + decimal.TryParse(minimum, out var minimumValue) && + numberValue < minimumValue) + { + errors.Add($"Path: {instance.Path}. Number is less than minimum"); } errorMessages = errors; diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonObjectValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonObjectValidator.cs index 27b4cfd07b..d573af0d25 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonObjectValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonObjectValidator.cs @@ -1,16 +1,16 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonObjectValidator(IJsonValidator jsonValidator) : IJsonValidator +public sealed class JsonObjectValidator(IJsonValidator jsonValidator) : IJsonValidator { private readonly IJsonValidator _jsonValidator = jsonValidator; - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.Object; + public bool CanValidate(IOpenApiSchema schema) => schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Object); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonOneOfValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonOneOfValidator.cs index cb3b449e9a..8179675ccb 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonOneOfValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonOneOfValidator.cs @@ -1,16 +1,16 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonOneOfValidator(JsonValidator jsonValidator) : IJsonValidator +public sealed class JsonOneOfValidator(JsonValidator jsonValidator) : IJsonValidator { private readonly JsonValidator _jsonValidator = jsonValidator; - public bool CanValidate(OpenApiSchema schema) => schema.OneOf != null && schema.OneOf.Any(); + public bool CanValidate(IOpenApiSchema schema) => schema.OneOf != null && schema.OneOf.Any(); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonStringValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonStringValidator.cs index 48bb18bdca..bf4ff79db6 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonStringValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonStringValidator.cs @@ -1,15 +1,15 @@ using System.Text.RegularExpressions; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonStringValidator : IJsonValidator +public sealed class JsonStringValidator : IJsonValidator { - public bool CanValidate(OpenApiSchema schema) => schema.Type == JsonSchemaTypes.String; + public bool CanValidate(IOpenApiSchema schema) => schema.Type is { } type && type.HasFlag(JsonSchemaTypes.String); public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonValidator.cs index 553a77c9d4..3f15d42025 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/JsonValidation/JsonValidator.cs @@ -1,9 +1,9 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; namespace Swashbuckle.AspNetCore.ApiTesting; -public class JsonValidator : IJsonValidator +public sealed class JsonValidator : IJsonValidator { private readonly IEnumerable _subValidators; @@ -23,17 +23,18 @@ public JsonValidator() ]; } - public bool CanValidate(OpenApiSchema schema) => true; + public bool CanValidate(IOpenApiSchema schema) => true; public bool Validate( - OpenApiSchema schema, + IOpenApiSchema schema, OpenApiDocument openApiDocument, JToken instance, out IEnumerable errorMessages) { - schema = schema.Reference != null - ? (OpenApiSchema)openApiDocument.ResolveReference(schema.Reference) - : schema; + if (schema is OpenApiSchemaReference reference && !openApiDocument.Components.Schemas.Any((p) => p.Key == reference.Reference.Id)) + { + throw new InvalidOperationException($"Invalid Reference identifier '{reference.Reference.Id}'."); + } var errors = new List(); diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiDocumentExtensions.cs b/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiDocumentExtensions.cs index caaa119b35..b7173b6038 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiDocumentExtensions.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiDocumentExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; @@ -8,19 +8,25 @@ internal static bool TryFindOperationById( this OpenApiDocument openApiDocument, string operationId, out string pathTemplate, - out OperationType operationType) + out HttpMethod operationType) { - foreach (var pathEntry in openApiDocument.Paths ?? []) + if (openApiDocument.Paths is { Count: > 0 } paths) { - var pathItem = pathEntry.Value; - - foreach (var operationEntry in pathItem.Operations) + foreach (var pathEntry in paths) { - if (operationEntry.Value.OperationId == operationId) + var pathItem = pathEntry.Value; + + if (pathItem.Operations is { Count: > 0 } operations) { - pathTemplate = pathEntry.Key; - operationType = operationEntry.Key; - return true; + foreach (var operation in operations) + { + if (operation.Value.OperationId == operationId) + { + pathTemplate = pathEntry.Key; + operationType = operation.Key; + return true; + } + } } } } @@ -33,8 +39,8 @@ internal static bool TryFindOperationById( internal static OpenApiOperation GetOperationByPathAndType( this OpenApiDocument openApiDocument, string pathTemplate, - OperationType operationType, - out OpenApiPathItem pathSpec) + HttpMethod operationType, + out IOpenApiPathItem pathSpec) { if (openApiDocument.Paths.TryGetValue(pathTemplate, out pathSpec)) { diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiSchemaExtensions.cs b/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiSchemaExtensions.cs index 55c1e724a1..ba8067b2dc 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiSchemaExtensions.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/OpenApiSchemaExtensions.cs @@ -1,55 +1,55 @@ using System.Text; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; -public static class OpenApiSchemaExtensions +internal static class OpenApiSchemaExtensions { internal static bool TryParse(this OpenApiSchema schema, string stringValue, out object typedValue) { typedValue = null; - if (schema.Type == JsonSchemaTypes.Integer && schema.Format == "int64" && long.TryParse(stringValue, out long longValue)) + if (IsType(schema.Type, JsonSchemaTypes.Integer) && schema.Format == "int64" && long.TryParse(stringValue, out long longValue)) { typedValue = longValue; } - else if (schema.Type == JsonSchemaTypes.Integer && int.TryParse(stringValue, out int intValue)) + else if (IsType(schema.Type, JsonSchemaTypes.Integer) && int.TryParse(stringValue, out int intValue)) { typedValue = intValue; } - else if (schema.Type == JsonSchemaTypes.Number && schema.Format == "double" && double.TryParse(stringValue, out double doubleValue)) + else if (IsType(schema.Type, JsonSchemaTypes.Number) && schema.Format == "double" && double.TryParse(stringValue, out double doubleValue)) { typedValue = doubleValue; } - else if (schema.Type == JsonSchemaTypes.Number && float.TryParse(stringValue, out float floatValue)) + else if (IsType(schema.Type, JsonSchemaTypes.Number) && float.TryParse(stringValue, out float floatValue)) { typedValue = floatValue; } - else if (schema.Type == JsonSchemaTypes.String && schema.Format == "byte" && byte.TryParse(stringValue, out byte byteValue)) + else if (IsType(schema.Type, JsonSchemaTypes.String) && schema.Format == "byte" && byte.TryParse(stringValue, out byte byteValue)) { typedValue = byteValue; } - else if (schema.Type == JsonSchemaTypes.Boolean && bool.TryParse(stringValue, out bool boolValue)) + else if (IsType(schema.Type, JsonSchemaTypes.Boolean) && bool.TryParse(stringValue, out bool boolValue)) { typedValue = boolValue; } - else if (schema.Type == JsonSchemaTypes.String && schema.Format == "date" && DateTime.TryParse(stringValue, out DateTime dateValue)) + else if (IsType(schema.Type, JsonSchemaTypes.String) && schema.Format == "date" && DateTime.TryParse(stringValue, out DateTime dateValue)) { typedValue = dateValue; } - else if (schema.Type == JsonSchemaTypes.String && schema.Format == "date-time" && DateTime.TryParse(stringValue, out DateTime dateTimeValue)) + else if (IsType(schema.Type, JsonSchemaTypes.String) && schema.Format == "date-time" && DateTime.TryParse(stringValue, out DateTime dateTimeValue)) { typedValue = dateTimeValue; } - else if (schema.Type == JsonSchemaTypes.String && schema.Format == "uuid" && Guid.TryParse(stringValue, out Guid uuidValue)) + else if (IsType(schema.Type, JsonSchemaTypes.String) && schema.Format == "uuid" && Guid.TryParse(stringValue, out Guid uuidValue)) { typedValue = uuidValue; } - else if (schema.Type == JsonSchemaTypes.String) + else if (IsType(schema.Type, JsonSchemaTypes.String)) { typedValue = stringValue; } - else if (schema.Type == JsonSchemaTypes.Array) + else if (IsType(schema.Type, JsonSchemaTypes.Array)) { var arrayValue = schema.Items == null ? stringValue.Split(',') @@ -67,13 +67,16 @@ internal static bool TryParse(this OpenApiSchema schema, string stringValue, out } return typedValue != null; + + static bool IsType(JsonSchemaType? type, JsonSchemaType target) + => type is { } value && value.HasFlag(target); } internal static string TypeIdentifier(this OpenApiSchema schema) { var idBuilder = new StringBuilder(); - idBuilder.Append(schema.Type); + idBuilder.Append(schema.Type.ToString().ToLowerInvariant()); if (schema.Type == JsonSchemaTypes.Array && schema.Items != null) { diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt index 3494753fbe..476a5fce9e 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt @@ -1,10 +1,7 @@ static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.AddOpenApiFile(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName, string filePath) -> void -static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.GetOpenApiDocument(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName) -> Microsoft.OpenApi.Models.OpenApiDocument Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ApiTestRunnerBase() -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Configure(System.Action setupAction) -> void -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ConfigureOperation(string documentName, string pathTemplate, Microsoft.OpenApi.Models.OperationType operationType, Microsoft.OpenApi.Models.OpenApiOperation operation) -> void -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Dispose() -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.TestAsync(string documentName, string operationId, string expectedStatusCode, System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpClient httpClient) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.ApiTestRunnerOptions() -> void @@ -13,70 +10,43 @@ Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.FileOutputRoot.get -> str Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.FileOutputRoot.set -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.GenerateOpenApiFiles.get -> bool Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.GenerateOpenApiFiles.set -> void -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.OpenApiDocs.get -> System.Collections.Generic.Dictionary Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions Swashbuckle.AspNetCore.ApiTesting.ContentDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.ContentDoesNotMatchSpecException.ContentDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.HttpHeadersExtensions Swashbuckle.AspNetCore.ApiTesting.IContentValidator Swashbuckle.AspNetCore.ApiTesting.IContentValidator.CanValidate(string mediaType) -> bool -Swashbuckle.AspNetCore.ApiTesting.IContentValidator.Validate(Microsoft.OpenApi.Models.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void Swashbuckle.AspNetCore.ApiTesting.IJsonValidator -Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator -Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.JsonAllOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator -Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.JsonAnyOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator -Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.JsonArrayValidator(Swashbuckle.AspNetCore.ApiTesting.IJsonValidator jsonValidator) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator -Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.JsonBooleanValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.CanValidate(string mediaType) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.JsonContentValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.Validate(Microsoft.OpenApi.Models.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator -Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.JsonNullValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator -Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.JsonNumberValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator -Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.JsonObjectValidator(Swashbuckle.AspNetCore.ApiTesting.IJsonValidator jsonValidator) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator -Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.JsonOneOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator -Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.JsonStringValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonValidator -Swashbuckle.AspNetCore.ApiTesting.JsonValidator.CanValidate(Microsoft.OpenApi.Models.OpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonValidator.JsonValidator() -> void -Swashbuckle.AspNetCore.ApiTesting.JsonValidator.Validate(Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.OpenApiDocumentExtensions -Swashbuckle.AspNetCore.ApiTesting.OpenApiSchemaExtensions Swashbuckle.AspNetCore.ApiTesting.RequestDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.RequestDoesNotMatchSpecException.RequestDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.RequestValidator Swashbuckle.AspNetCore.ApiTesting.RequestValidator.RequestValidator(System.Collections.Generic.IEnumerable contentValidators) -> void -Swashbuckle.AspNetCore.ApiTesting.RequestValidator.Validate(System.Net.Http.HttpRequestMessage request, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, string pathTemplate, Microsoft.OpenApi.Models.OperationType operationType) -> void Swashbuckle.AspNetCore.ApiTesting.ResponseDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.ResponseDoesNotMatchSpecException.ResponseDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.ResponseValidator Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.ResponseValidator(System.Collections.Generic.IEnumerable contentValidators) -> void -Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.Validate(System.Net.Http.HttpResponseMessage response, Microsoft.OpenApi.Models.OpenApiDocument openApiDocument, string pathTemplate, Microsoft.OpenApi.Models.OperationType operationType, string expectedStatusCode) -> void + diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt index e69de29bb2..2e5e2aa1a7 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1,30 @@ +static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.GetOpenApiDocument(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName) -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ConfigureOperation(string documentName, string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operation) -> void +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Dispose() -> void +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.OpenApiDocs.get -> System.Collections.Generic.Dictionary +Swashbuckle.AspNetCore.ApiTesting.IContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void +Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.JsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool +Swashbuckle.AspNetCore.ApiTesting.RequestValidator.Validate(System.Net.Http.HttpRequestMessage request, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType) -> void +Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.Validate(System.Net.Http.HttpResponseMessage response, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType, string expectedStatusCode) -> void diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs index d071761b0e..e557f15449 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs @@ -3,11 +3,11 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Template; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; -public class RequestValidator(IEnumerable contentValidators) +public sealed class RequestValidator(IEnumerable contentValidators) { private readonly IEnumerable _contentValidators = contentValidators; @@ -15,10 +15,20 @@ public void Validate( HttpRequestMessage request, OpenApiDocument openApiDocument, string pathTemplate, - OperationType operationType) + HttpMethod operationType) { var operationSpec = openApiDocument.GetOperationByPathAndType(pathTemplate, operationType, out var pathSpec); - OpenApiParameter[] parameterSpecs = [.. pathSpec.Parameters, .. operationSpec.Parameters]; + IOpenApiParameter[] parameterSpecs = []; + + if (pathSpec.Parameters is { Count: > 0 } pathParameters) + { + parameterSpecs = [.. parameterSpecs, .. pathParameters]; + } + + if (operationSpec.Parameters is { Count: > 0 } operationParameters) + { + parameterSpecs = [.. parameterSpecs, .. operationParameters]; + } // Convert to absolute Uri as a workaround to limitation with Uri class - i.e. most of it's methods are not supported for relative Uri's. var requestUri = new Uri(new Uri("http://tempuri.org"), request.RequestUri); @@ -33,9 +43,9 @@ public void Validate( throw new RequestDoesNotMatchSpecException($"Request method '{request.Method}' does not match specified operation type '{operationType}'"); } - ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Path), openApiDocument, pathNameValues); - ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Query), openApiDocument, HttpUtility.ParseQueryString(requestUri.Query)); - ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Header), openApiDocument, request.Headers.ToNameValueCollection()); + ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Path), pathNameValues); + ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Query), HttpUtility.ParseQueryString(requestUri.Query)); + ValidateParameters(parameterSpecs.Where(p => p.In == ParameterLocation.Header), request.Headers.ToNameValueCollection()); if (operationSpec.RequestBody != null) { @@ -64,8 +74,7 @@ private static bool TryParsePathNameValues(string pathTemplate, string requestUr private static void ValidateParameters( - IEnumerable parameterSpecs, - OpenApiDocument openApiDocument, + IEnumerable parameterSpecs, NameValueCollection parameterNameValues) { foreach (var parameterSpec in parameterSpecs) @@ -82,23 +91,15 @@ private static void ValidateParameters( continue; } - var schema = parameterSpec.Schema.Reference != null ? - (OpenApiSchema)openApiDocument.ResolveReference(parameterSpec.Schema.Reference) - : parameterSpec.Schema; - - if (!schema.TryParse(value, out object typedValue)) + if (parameterSpec.Schema is OpenApiSchema schema && !schema.TryParse(value, out object typedValue)) { - throw new RequestDoesNotMatchSpecException($"Parameter '{parameterSpec.Name}' is not of type '{parameterSpec.Schema.TypeIdentifier()}'"); + throw new RequestDoesNotMatchSpecException($"Parameter '{parameterSpec.Name}' is not of type '{schema.TypeIdentifier()}'"); } } } - private void ValidateContent(OpenApiRequestBody requestBodySpec, OpenApiDocument openApiDocument, HttpContent content) + private void ValidateContent(IOpenApiRequestBody requestBodySpec, OpenApiDocument openApiDocument, HttpContent content) { - requestBodySpec = requestBodySpec.Reference != null ? - (OpenApiRequestBody)openApiDocument.ResolveReference(requestBodySpec.Reference) - : requestBodySpec; - if (requestBodySpec.Required && content == null) { throw new RequestDoesNotMatchSpecException("Required content is not present"); diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs index 2232a0f30a..6954c352d6 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs @@ -1,9 +1,9 @@ using System.Collections.Specialized; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.ApiTesting; -public class ResponseValidator(IEnumerable contentValidators) +public sealed class ResponseValidator(IEnumerable contentValidators) { private readonly IEnumerable _contentValidators = contentValidators; @@ -11,7 +11,7 @@ public void Validate( HttpResponseMessage response, OpenApiDocument openApiDocument, string pathTemplate, - OperationType operationType, + HttpMethod operationType, string expectedStatusCode) { var operationSpec = openApiDocument.GetOperationByPathAndType(pathTemplate, operationType, out _); @@ -26,7 +26,7 @@ public void Validate( throw new ResponseDoesNotMatchSpecException($"Status code '{statusCode}' does not match expected value '{expectedStatusCode}'"); } - ValidateHeaders(responseSpec.Headers, openApiDocument, response.Headers.ToNameValueCollection()); + ValidateHeaders(responseSpec.Headers, response.Headers.ToNameValueCollection()); if (responseSpec.Content != null && responseSpec.Content.Keys.Count != 0) { @@ -35,10 +35,14 @@ public void Validate( } private static void ValidateHeaders( - IDictionary headerSpecs, - OpenApiDocument openApiDocument, + IDictionary headerSpecs, NameValueCollection headerValues) { + if (headerSpecs is null) + { + return; + } + foreach (var entry in headerSpecs) { var value = headerValues[entry.Key]; @@ -54,18 +58,10 @@ private static void ValidateHeaders( continue; } - var schema = headerSpec.Schema.Reference != null ? - (OpenApiSchema)openApiDocument.ResolveReference(headerSpec.Schema.Reference) - : headerSpec.Schema; - - if (value == null) - { - continue; - } - - if (!schema.TryParse(value, out object typedValue)) + if (headerSpec.Schema is OpenApiSchema schema && + !schema.TryParse(value, out object typedValue)) { - throw new ResponseDoesNotMatchSpecException($"Header '{entry.Key}' is not of type '{headerSpec.Schema.TypeIdentifier()}'"); + throw new ResponseDoesNotMatchSpecException($"Header '{entry.Key}' is not of type '{schema.TypeIdentifier()}'"); } } } diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj b/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj index 34cf0cd49d..347bf3cd07 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj +++ b/src/Swashbuckle.AspNetCore.ApiTesting/Swashbuckle.AspNetCore.ApiTesting.csproj @@ -10,8 +10,7 @@ - - + @@ -23,6 +22,10 @@ + + + + diff --git a/src/Swashbuckle.AspNetCore.Cli/Program.cs b/src/Swashbuckle.AspNetCore.Cli/Program.cs index ceea11ceeb..14d65ac17a 100644 --- a/src/Swashbuckle.AspNetCore.Cli/Program.cs +++ b/src/Swashbuckle.AspNetCore.Cli/Program.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Microsoft.OpenApi; -using Microsoft.OpenApi.Writers; using Swashbuckle.AspNetCore.Swagger; namespace Swashbuckle.AspNetCore.Cli; @@ -105,6 +104,7 @@ public static int Main(string[] args) { "2.0" => OpenApiSpecVersion.OpenApi2_0, "3.0" => OpenApiSpecVersion.OpenApi3_0, + "3.1" => OpenApiSpecVersion.OpenApi3_1, _ => throw new NotSupportedException($"The specified OpenAPI version \"{versionArg}\" is not supported."), }; } @@ -115,17 +115,7 @@ public static int Main(string[] args) } else { - switch (specVersion) - { - case OpenApiSpecVersion.OpenApi2_0: - swagger.SerializeAsV2(writer); - break; - - case OpenApiSpecVersion.OpenApi3_0: - default: - swagger.SerializeAsV3(writer); - break; - } + swagger.SerializeAs(specVersion, writer); } if (outputPath != null) diff --git a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj index b44f8652fb..1832b2be36 100644 --- a/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj +++ b/src/Swashbuckle.AspNetCore.Newtonsoft/Swashbuckle.AspNetCore.Newtonsoft.csproj @@ -22,6 +22,10 @@ + + + + diff --git a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj index 52c1c86fe5..ae62253e06 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj +++ b/src/Swashbuckle.AspNetCore.ReDoc/Swashbuckle.AspNetCore.ReDoc.csproj @@ -78,7 +78,7 @@ - <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net9.0 + <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net10.0 <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472 diff --git a/src/Swashbuckle.AspNetCore.Swagger/IAsyncSwaggerProvider.cs b/src/Swashbuckle.AspNetCore.Swagger/IAsyncSwaggerProvider.cs index 8224c06638..6ca2f6bd0e 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/IAsyncSwaggerProvider.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/IAsyncSwaggerProvider.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.Swagger; diff --git a/src/Swashbuckle.AspNetCore.Swagger/ISwaggerDocumentSerializer.cs b/src/Swashbuckle.AspNetCore.Swagger/ISwaggerDocumentSerializer.cs index e69c1e3ffc..9ee2edf303 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/ISwaggerDocumentSerializer.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/ISwaggerDocumentSerializer.cs @@ -1,6 +1,4 @@ using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; namespace Swashbuckle.AspNetCore.Swagger; diff --git a/src/Swashbuckle.AspNetCore.Swagger/ISwaggerProvider.cs b/src/Swashbuckle.AspNetCore.Swagger/ISwaggerProvider.cs index 066884fac4..3b6ce2f0c1 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/ISwaggerProvider.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/ISwaggerProvider.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.Swagger; diff --git a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt index 2ea801b6d3..a0d2fb49b5 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt @@ -7,24 +7,19 @@ static Microsoft.AspNetCore.Builder.SwaggerBuilderExtensions.UseSwagger(this Mic static Microsoft.Extensions.DependencyInjection.SwaggerOptionsExtensions.SetCustomDocumentSerializer(this Swashbuckle.AspNetCore.Swagger.SwaggerOptions swaggerOptions, params object[] constructorParameters) -> void static Microsoft.Extensions.DependencyInjection.SwaggerServiceCollectionExtensions.ConfigureSwagger(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) -> void Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider -Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider.GetDocumentNames() -> System.Collections.Generic.IList Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer -Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer.SerializeDocument(Microsoft.OpenApi.Models.OpenApiDocument document, Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void Swashbuckle.AspNetCore.Swagger.ISwaggerProvider -Swashbuckle.AspNetCore.Swagger.ISwaggerProvider.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.Models.OpenApiDocument Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.set -> void -Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.PreSerializeFilters.get -> System.Collections.Generic.List> Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SwaggerEndpointOptions() -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.get -> Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.set -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.set -> void -Swashbuckle.AspNetCore.Swagger.SwaggerOptions.PreSerializeFilters.get -> System.Collections.Generic.List> Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.get -> string Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.set -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SwaggerOptions() -> void diff --git a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt index 5f282702bb..f5d8dc9185 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ - \ No newline at end of file +Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer.SerializeDocument(Microsoft.OpenApi.OpenApiDocument document, Microsoft.OpenApi.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void +Swashbuckle.AspNetCore.Swagger.ISwaggerProvider.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.PreSerializeFilters.get -> System.Collections.Generic.List> +Swashbuckle.AspNetCore.Swagger.SwaggerOptions.PreSerializeFilters.get -> System.Collections.Generic.List> diff --git a/src/Swashbuckle.AspNetCore.Swagger/SwaggerEndpointOptions.cs b/src/Swashbuckle.AspNetCore.Swagger/SwaggerEndpointOptions.cs index a45d792d3c..9a8ebcb256 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/SwaggerEndpointOptions.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/SwaggerEndpointOptions.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; namespace Swashbuckle.AspNetCore.Swagger; diff --git a/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs b/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs index 944dcac9f8..1256335bd8 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/SwaggerMiddleware.cs @@ -5,8 +5,7 @@ using Microsoft.AspNetCore.Routing.Patterns; using Microsoft.AspNetCore.Routing.Template; using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.Swagger; @@ -174,17 +173,7 @@ private void SerializeDocument( } else { - switch (_options.OpenApiVersion) - { - case Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0: - document.SerializeAsV2(writer); - break; - - case Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_0: - default: - document.SerializeAsV3(writer); - break; - } + document.SerializeAs(_options.OpenApiVersion, writer); } } } diff --git a/src/Swashbuckle.AspNetCore.Swagger/SwaggerOptions.cs b/src/Swashbuckle.AspNetCore.Swagger/SwaggerOptions.cs index 4ee7f48ed2..afea3d4df2 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/SwaggerOptions.cs +++ b/src/Swashbuckle.AspNetCore.Swagger/SwaggerOptions.cs @@ -1,6 +1,5 @@ using Microsoft.AspNetCore.Http; using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; namespace Swashbuckle.AspNetCore.Swagger; diff --git a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj index 6346a9732e..506e74072b 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj +++ b/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSchemaGeneratorOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSchemaGeneratorOptions.cs index 6017437bf7..09b2dc9397 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSchemaGeneratorOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSchemaGeneratorOptions.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -22,7 +22,7 @@ public void Configure(SchemaGeneratorOptions options) private static void DeepCopy(SchemaGeneratorOptions source, SchemaGeneratorOptions target) { - target.CustomTypeMappings = new Dictionary>(source.CustomTypeMappings); + target.CustomTypeMappings = new Dictionary>(source.CustomTypeMappings); target.UseInlineDefinitionsForEnums = source.UseInlineDefinitionsForEnums; target.SchemaIdSelector = source.SchemaIdSelector; target.IgnoreObsoleteProperties = source.IgnoreObsoleteProperties; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs index bf9bea3344..846ab0c327 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/ConfigureSwaggerGeneratorOptions.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -91,7 +91,7 @@ public static void DeepCopy(SwaggerGeneratorOptions source, SwaggerGeneratorOpti target.DescribeAllParametersInCamelCase = source.DescribeAllParametersInCamelCase; target.SchemaComparer = source.SchemaComparer; target.Servers = [.. source.Servers]; - target.SecuritySchemes = new Dictionary(source.SecuritySchemes); + target.SecuritySchemes = new Dictionary(source.SecuritySchemes); target.SecurityRequirements = [.. source.SecurityRequirements]; target.ParameterFilters = [.. source.ParameterFilters]; target.ParameterAsyncFilters = [.. source.ParameterAsyncFilters]; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/DocumentProvider.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/DocumentProvider.cs index d46da87d53..649d0bef3e 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/DocumentProvider.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/DocumentProvider.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Writers; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; @@ -29,17 +29,7 @@ public async Task GenerateAsync(string documentName, TextWriter writer) } else { - switch (_options.OpenApiVersion) - { - case OpenApi.OpenApiSpecVersion.OpenApi2_0: - swagger.SerializeAsV2(jsonWriter); - break; - - default: - case OpenApi.OpenApiSpecVersion.OpenApi3_0: - swagger.SerializeAsV3(jsonWriter); - break; - } + swagger.SerializeAs(_options.OpenApiVersion, jsonWriter); } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs index c2df6a5b4a..dc4f4df057 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/DependencyInjection/SwaggerGenOptionsExtensions.cs @@ -2,7 +2,7 @@ using System.Xml.XPath; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Microsoft.Extensions.DependencyInjection; @@ -132,7 +132,7 @@ public static void AddServer(this SwaggerGenOptions swaggerGenOptions, OpenApiSe public static void AddSecurityDefinition( this SwaggerGenOptions swaggerGenOptions, string name, - OpenApiSecurityScheme securityScheme) + IOpenApiSecurityScheme securityScheme) { swaggerGenOptions.SwaggerGeneratorOptions.SecuritySchemes.Add(name, securityScheme); } @@ -147,7 +147,7 @@ public static void AddSecurityDefinition( /// public static void AddSecurityRequirement( this SwaggerGenOptions swaggerGenOptions, - OpenApiSecurityRequirement securityRequirement) + Func securityRequirement) { swaggerGenOptions.SwaggerGeneratorOptions.SecurityRequirements.Add(securityRequirement); } @@ -161,7 +161,7 @@ public static void AddSecurityRequirement( public static void MapType( this SwaggerGenOptions swaggerGenOptions, Type type, - Func schemaFactory) + Func schemaFactory) { swaggerGenOptions.SchemaGeneratorOptions.CustomTypeMappings.Add(type, schemaFactory); } @@ -174,7 +174,7 @@ public static void MapType( /// A factory method that generates Schema's for the provided type public static void MapType( this SwaggerGenOptions swaggerGenOptions, - Func schemaFactory) + Func schemaFactory) { swaggerGenOptions.MapType(typeof(T), schemaFactory); } @@ -312,7 +312,7 @@ public static void NonNullableReferenceTypesAsRequired(this SwaggerGenOptions sw /// Currently only supports JWT Bearer authentication public static void InferSecuritySchemes( this SwaggerGenOptions swaggerGenOptions, - Func, IDictionary> securitySchemesSelector = null) + Func, IDictionary> securitySchemesSelector = null) { swaggerGenOptions.SwaggerGeneratorOptions.InferSecuritySchemes = true; swaggerGenOptions.SwaggerGeneratorOptions.SecuritySchemesSelector = securitySchemesSelector; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/JsonModelFactory.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/JsonModelFactory.cs index c7b230400a..afebf6e5ac 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/JsonModelFactory.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/JsonModelFactory.cs @@ -1,10 +1,9 @@ -using Microsoft.OpenApi.Any; -using Swashbuckle.AspNetCore.SwaggerGen; +using System.Text.Json.Nodes; namespace Swashbuckle.AspNetCore; internal static class JsonModelFactory { - public static IOpenApiAny CreateFromJson(string json) - => OpenApiAnyFactory.CreateFromJson(json); + public static JsonNode CreateFromJson(string json) + => json is null ? null : JsonNode.Parse(json); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt index da26904fbb..5d4ff3cf46 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt @@ -9,9 +9,6 @@ static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddP static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddRequestBodyAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddRequestBodyFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSchemaFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityDefinition(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.Models.OpenApiSecurityScheme securityScheme) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityRequirement(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, Microsoft.OpenApi.Models.OpenApiSecurityRequirement securityRequirement) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddServer(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, Microsoft.OpenApi.Models.OpenApiServer server) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.CustomOperationIds(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func operationIdSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.CustomSchemaIds(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaIdSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DescribeAllParametersInCamelCase(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void @@ -23,9 +20,6 @@ static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.Igno static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string filePath, bool includeControllerXmlComments = false) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func xmlDocFactory, bool includeControllerXmlComments = false) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Reflection.Assembly assembly, bool includeControllerXmlComments = false) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.InferSecuritySchemes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func, System.Collections.Generic.IDictionary> securitySchemesSelector = null) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Type type, System.Func schemaFactory) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaFactory) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.NonNullableReferenceTypesAsRequired(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void @@ -41,7 +35,6 @@ static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.Sele static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SelectSubTypesUsing(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func> customSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SortSchemasWith(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Collections.Generic.IComparer schemaComparer) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SupportNonNullableReferenceTypes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SwaggerDoc(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.Models.OpenApiInfo info) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.TagActionsBy(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func> tagsSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.UseAllOfForInheritance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.UseAllOfToExtendReferenceSchemas(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void @@ -64,8 +57,6 @@ static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.GetInlineAndMetada static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.IsDictionaryValueNonNullable(this System.Reflection.MemberInfo memberInfo) -> bool static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.IsNonNullableReferenceType(this System.Reflection.MemberInfo memberInfo) -> bool static Swashbuckle.AspNetCore.SwaggerGen.MethodInfoExtensions.GetUnderlyingGenericTypeMethod(this System.Reflection.MethodInfo constructedTypeMethod) -> System.Reflection.MethodInfo -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyRouteConstraints(this Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo) -> void -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyValidationAttributes(this Microsoft.OpenApi.Models.OpenApiSchema schema, System.Collections.Generic.IEnumerable customAttributes) -> void static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.HasAttribute(this System.Reflection.PropertyInfo property) -> bool static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.IsPubliclyReadable(this System.Reflection.PropertyInfo property) -> bool static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.IsPubliclyWritable(this System.Reflection.PropertyInfo property) -> bool @@ -132,25 +123,15 @@ Swashbuckle.AspNetCore.SwaggerGen.FilterDescriptor.Type.get -> System.Type Swashbuckle.AspNetCore.SwaggerGen.FilterDescriptor.Type.set -> void Swashbuckle.AspNetCore.SwaggerGen.IDictionary Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter -Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter -Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter.Apply(Microsoft.OpenApi.Models.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter -Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter -Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter.Apply(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter -Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter -Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter.Apply(Microsoft.OpenApi.Models.OpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter -Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter -Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter.Apply(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter -Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter.Apply(Microsoft.OpenApi.Models.OpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator -Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.Models.OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver.GetDataContractForType(System.Type type) -> Swashbuckle.AspNetCore.SwaggerGen.DataContract Swashbuckle.AspNetCore.SwaggerGen.JsonSerializerDataContractResolver @@ -165,13 +146,11 @@ Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.ApiDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.MethodInfo.get -> System.Reflection.MethodInfo -Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.OperationFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription apiDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaRegistry, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MethodInfo methodInfo) -> void Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ApiParameterDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.DocumentName.get -> string -Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription apiParameterDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.PropertyInfo propertyInfo = null, System.Reflection.ParameterInfo parameterInfo = null) -> void Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterInfo.get -> System.Reflection.ParameterInfo Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.PropertyInfo.get -> System.Reflection.PropertyInfo Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator @@ -181,7 +160,6 @@ Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.BodyParameterDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.FormParameterDescriptions.get -> System.Collections.Generic.IEnumerable -Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.RequestBodyFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription bodyParameterDescription, System.Collections.Generic.IEnumerable formParameterDescriptions, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository) -> void Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext @@ -193,10 +171,8 @@ Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.SchemaGenerator.get -> Swa Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.Type.get -> System.Type Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator -Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.Models.OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.SchemaGenerator(Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions generatorOptions, Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver serializerDataContractResolver) -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions -Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.get -> System.Collections.Generic.IDictionary> Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.set -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.DiscriminatorNameSelector.get -> System.Func Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.DiscriminatorNameSelector.set -> void @@ -224,19 +200,14 @@ Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseInlineDefinitionsFor Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseOneOfForPolymorphism.get -> bool Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseOneOfForPolymorphism.set -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.AddDefinition(string schemaId, Microsoft.OpenApi.Models.OpenApiSchema schema) -> Microsoft.OpenApi.Models.OpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.RegisterType(System.Type type, string schemaId) -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.SchemaRepository(string documentName = null) -> void -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.Schemas.get -> System.Collections.Generic.Dictionary -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.TryLookupByType(System.Type type, out Microsoft.OpenApi.Models.OpenApiSchema referenceSchema) -> bool Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention.Apply(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel application) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention.SwaggerApplicationConvention() -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetDocumentNames() -> System.Collections.Generic.IList -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.Models.OpenApiDocument -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.SwaggerGenerator(Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options, Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.SwaggerGenerator(Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options, Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider authenticationSchemeProvider) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException @@ -275,17 +246,12 @@ Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.RequestBodyFilters.get Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.RequestBodyFilters.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SchemaComparer.get -> System.Collections.Generic.IComparer Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SchemaComparer.set -> void -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.get -> System.Collections.Generic.IList Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.set -> void -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.get -> System.Collections.Generic.IDictionary Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.set -> void -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.get -> System.Func, System.Collections.Generic.IDictionary> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.set -> void -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.get -> System.Collections.Generic.List Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SortKeySelector.get -> System.Func Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SortKeySelector.set -> void -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.get -> System.Collections.Generic.IDictionary Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerGeneratorOptions() -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.TagsSelector.get -> System.Func> @@ -310,21 +276,10 @@ Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions.SwaggerGeneratorOptions.set Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions.SwaggerGenOptions() -> void Swashbuckle.AspNetCore.SwaggerGen.TypeExtensions Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.Apply(Microsoft.OpenApi.Models.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.XmlCommentsDocumentFilter(System.Xml.XPath.XPathDocument xmlDoc) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.XmlCommentsDocumentFilter(System.Xml.XPath.XPathDocument xmlDoc, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsNodeNameHelper Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsNodeNameHelper.XmlCommentsNodeNameHelper() -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.Apply(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.XmlCommentsOperationFilter(System.Xml.XPath.XPathDocument xmlDoc) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.Apply(Microsoft.OpenApi.Models.OpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.XmlCommentsParameterFilter(System.Xml.XPath.XPathDocument xmlDoc) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.Apply(Microsoft.OpenApi.Models.OpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.XmlCommentsRequestBodyFilter(System.Xml.XPath.XPathDocument xmlDoc) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.Apply(Microsoft.OpenApi.Models.OpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.XmlCommentsSchemaFilter(System.Xml.XPath.XPathDocument xmlDoc) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsTextHelper diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt index e69de29bb2..7e384c4c92 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt @@ -0,0 +1,47 @@ +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityDefinition(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.IOpenApiSecurityScheme securityScheme) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityRequirement(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func securityRequirement) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddServer(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, Microsoft.OpenApi.OpenApiServer server) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.InferSecuritySchemes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func, System.Collections.Generic.IDictionary> securitySchemesSelector = null) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Type type, System.Func schemaFactory) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaFactory) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SwaggerDoc(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.OpenApiInfo info) -> void +static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyRouteConstraints(this Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo) -> void +static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyValidationAttributes(this Microsoft.OpenApi.IOpenApiSchema schema, System.Collections.Generic.IEnumerable customAttributes) -> void +Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema +Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.OperationFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription apiDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaRegistry, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.MethodInfo methodInfo) -> void +Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription apiParameterDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.PropertyInfo propertyInfo = null, System.Reflection.ParameterInfo parameterInfo = null) -> void +Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.RequestBodyFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription bodyParameterDescription, System.Collections.Generic.IEnumerable formParameterDescriptions, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document) -> void +Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema +Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.get -> System.Collections.Generic.IDictionary> +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.AddDefinition(string schemaId, Microsoft.OpenApi.OpenApiSchema schema) -> Microsoft.OpenApi.OpenApiSchemaReference +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.Schemas.get -> System.Collections.Generic.Dictionary +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.TryLookupByType(System.Type type, out Microsoft.OpenApi.OpenApiSchemaReference referenceSchema) -> bool +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.get -> System.Collections.Generic.IList> +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.get -> System.Collections.Generic.IDictionary +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.get -> System.Func, System.Collections.Generic.IDictionary> +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.get -> System.Collections.Generic.List +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.get -> System.Collections.Generic.IDictionary +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.XmlCommentsDocumentFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.XmlCommentsOperationFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.XmlCommentsParameterFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.XmlCommentsRequestBodyFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.XmlCommentsSchemaFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Shipped.txt deleted file mode 100644 index 02932c3763..0000000000 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ /dev/null @@ -1,4 +0,0 @@ -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory.CreateFromJson(string json) -> Microsoft.OpenApi.Any.IOpenApiAny -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory.CreateFromJson(string json, System.Text.Json.JsonSerializerOptions options) -> Microsoft.OpenApi.Any.IOpenApiAny -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ResolveType(this Microsoft.OpenApi.Models.OpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository) -> string -Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Unshipped.txt deleted file mode 100644 index 5f282702bb..0000000000 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Shipped.txt deleted file mode 100644 index 02932c3763..0000000000 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Shipped.txt +++ /dev/null @@ -1,4 +0,0 @@ -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory.CreateFromJson(string json) -> Microsoft.OpenApi.Any.IOpenApiAny -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory.CreateFromJson(string json, System.Text.Json.JsonSerializerOptions options) -> Microsoft.OpenApi.Any.IOpenApiAny -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ResolveType(this Microsoft.OpenApi.Models.OpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository) -> string -Swashbuckle.AspNetCore.SwaggerGen.OpenApiAnyFactory diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Unshipped.txt deleted file mode 100644 index 5f282702bb..0000000000 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/net9.0/PublicAPI.Unshipped.txt +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISchemaFilter.cs index 6dbebbae80..49d80112fb 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/ISchemaFilter.cs @@ -1,8 +1,8 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface ISchemaFilter { - void Apply(OpenApiSchema schema, SchemaFilterContext context); + void Apply(IOpenApiSchema schema, SchemaFilterContext context); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/OpenApiSchemaExtensions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/OpenApiSchemaExtensions.cs index 68071983f4..34d0c3eae9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/OpenApiSchemaExtensions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/OpenApiSchemaExtensions.cs @@ -3,7 +3,7 @@ using System.Globalization; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Routing.Constraints; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using AnnotationsDataType = System.ComponentModel.DataAnnotations.DataType; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -30,7 +30,15 @@ public static class OpenApiSchemaExtensions [AnnotationsDataType.Upload] = "binary", }; - public static void ApplyValidationAttributes(this OpenApiSchema schema, IEnumerable customAttributes) + public static void ApplyValidationAttributes(this IOpenApiSchema schema, IEnumerable customAttributes) + { + if (schema is OpenApiSchema concrete) + { + ApplyValidationAttributes(concrete, customAttributes); + } + } + + private static void ApplyValidationAttributes(OpenApiSchema schema, IEnumerable customAttributes) { foreach (var attribute in customAttributes) { @@ -77,7 +85,15 @@ public static void ApplyValidationAttributes(this OpenApiSchema schema, IEnumera } } - public static void ApplyRouteConstraints(this OpenApiSchema schema, ApiParameterRouteInfo routeInfo) + public static void ApplyRouteConstraints(this IOpenApiSchema schema, ApiParameterRouteInfo routeInfo) + { + if (schema is OpenApiSchema concrete) + { + ApplyRouteConstraints(concrete, routeInfo); + } + } + + private static void ApplyRouteConstraints(OpenApiSchema schema, ApiParameterRouteInfo routeInfo) { foreach (var constraint in routeInfo.Constraints) { @@ -128,19 +144,23 @@ public static void ApplyRouteConstraints(this OpenApiSchema schema, ApiParameter } } - public static string ResolveType(this OpenApiSchema schema, SchemaRepository schemaRepository) + internal static JsonSchemaType? ResolveType(this IOpenApiSchema schema, SchemaRepository schemaRepository) { - if (schema.Reference != null && schemaRepository.Schemas.TryGetValue(schema.Reference.Id, out OpenApiSchema definitionSchema)) + if (schema is OpenApiSchemaReference reference && + schemaRepository.Schemas.TryGetValue(reference.Reference.Id, out var definitionSchema)) { return definitionSchema.ResolveType(schemaRepository); } - foreach (var subSchema in schema.AllOf) + if (schema.AllOf is { Count: > 0 } allOf) { - var type = subSchema.ResolveType(schemaRepository); - if (type != null) + foreach (var subSchema in allOf) { - return type; + var type = subSchema.ResolveType(schemaRepository); + if (type != null) + { + return type; + } } } @@ -157,7 +177,7 @@ private static void ApplyDataTypeAttribute(OpenApiSchema schema, DataTypeAttribu private static void ApplyMinLengthAttribute(OpenApiSchema schema, MinLengthAttribute minLengthAttribute) { - if (schema.Type == JsonSchemaTypes.Array) + if (schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array)) { schema.MinItems = minLengthAttribute.Length; } @@ -169,7 +189,7 @@ private static void ApplyMinLengthAttribute(OpenApiSchema schema, MinLengthAttri private static void ApplyMinLengthRouteConstraint(OpenApiSchema schema, MinLengthRouteConstraint minLengthRouteConstraint) { - if (schema.Type == JsonSchemaTypes.Array) + if (schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array)) { schema.MinItems = minLengthRouteConstraint.MinLength; } @@ -181,7 +201,7 @@ private static void ApplyMinLengthRouteConstraint(OpenApiSchema schema, MinLengt private static void ApplyMaxLengthAttribute(OpenApiSchema schema, MaxLengthAttribute maxLengthAttribute) { - if (schema.Type == JsonSchemaTypes.Array) + if (schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array)) { schema.MaxItems = maxLengthAttribute.Length; } @@ -193,7 +213,7 @@ private static void ApplyMaxLengthAttribute(OpenApiSchema schema, MaxLengthAttri private static void ApplyMaxLengthRouteConstraint(OpenApiSchema schema, MaxLengthRouteConstraint maxLengthRouteConstraint) { - if (schema.Type == JsonSchemaTypes.Array) + if (schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array)) { schema.MaxItems = maxLengthRouteConstraint.MaxLength; } @@ -205,7 +225,7 @@ private static void ApplyMaxLengthRouteConstraint(OpenApiSchema schema, MaxLengt private static void ApplyLengthAttribute(OpenApiSchema schema, LengthAttribute lengthAttribute) { - if (schema.Type == JsonSchemaTypes.Array) + if (schema.Type is { } type && type.HasFlag(JsonSchemaTypes.Array)) { schema.MinItems = lengthAttribute.MinimumLength; schema.MaxItems = lengthAttribute.MaximumLength; @@ -224,11 +244,16 @@ private static void ApplyBase64Attribute(OpenApiSchema schema) private static void ApplyRangeAttribute(OpenApiSchema schema, RangeAttribute rangeAttribute) { - if (rangeAttribute.Maximum is int maximumInteger) + object maximumValue = null; + object minimumValue = null; + + if (rangeAttribute.Maximum is double || rangeAttribute.Minimum is int) { - // The range was set with the RangeAttribute(int, int) constructor - schema.Maximum = maximumInteger; - schema.Minimum = (int)rangeAttribute.Minimum; + // The range was set with the RangeAttribute(double, double) or RangeAttribute(int, int) + // constructor so we can safely convert the values to strings using the invariant culture + // as we have primitive values to operate on. + maximumValue = rangeAttribute.Maximum; + minimumValue = rangeAttribute.Minimum; } else { @@ -245,37 +270,48 @@ private static void ApplyRangeAttribute(OpenApiSchema schema, RangeAttribute ran if (decimal.TryParse(maxString, NumberStyles.Any, targetCulture, out var value)) { - schema.Maximum = value; + maximumValue = value; } if (decimal.TryParse(minString, NumberStyles.Any, targetCulture, out value)) { - schema.Minimum = value; + minimumValue = value; } } - if (rangeAttribute.MinimumIsExclusive) + // Ensure that the conversion to string is done using the invariant culture so valid JSON is generated + if (maximumValue is not null) + { + schema.Maximum = Convert.ToString(maximumValue, CultureInfo.InvariantCulture); + } + + if (minimumValue is not null) { - schema.ExclusiveMinimum = true; + schema.Minimum = Convert.ToString(minimumValue, CultureInfo.InvariantCulture); } if (rangeAttribute.MaximumIsExclusive) { - schema.ExclusiveMaximum = true; + schema.ExclusiveMaximum = schema.Maximum; + } + + if (rangeAttribute.MinimumIsExclusive) + { + schema.ExclusiveMinimum = schema.Minimum; } } private static void ApplyRangeRouteConstraint(OpenApiSchema schema, RangeRouteConstraint rangeRouteConstraint) { - schema.Maximum = rangeRouteConstraint.Max; - schema.Minimum = rangeRouteConstraint.Min; + schema.Maximum = rangeRouteConstraint.Max.ToString(CultureInfo.InvariantCulture); + schema.Minimum = rangeRouteConstraint.Min.ToString(CultureInfo.InvariantCulture); } private static void ApplyMinRouteConstraint(OpenApiSchema schema, MinRouteConstraint minRouteConstraint) - => schema.Minimum = minRouteConstraint.Min; + => schema.Minimum = minRouteConstraint.Min.ToString(CultureInfo.InvariantCulture); private static void ApplyMaxRouteConstraint(OpenApiSchema schema, MaxRouteConstraint maxRouteConstraint) - => schema.Maximum = maxRouteConstraint.Max; + => schema.Maximum = maxRouteConstraint.Max.ToString(CultureInfo.InvariantCulture); private static void ApplyRegularExpressionAttribute(OpenApiSchema schema, RegularExpressionAttribute regularExpressionAttribute) { diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index f483d9134e..22d03a45c3 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -2,11 +2,10 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Reflection; -using System.Text.Json; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -17,7 +16,7 @@ public class SchemaGenerator( private readonly SchemaGeneratorOptions _generatorOptions = generatorOptions; private readonly ISerializerDataContractResolver _serializerDataContractResolver = serializerDataContractResolver; - public OpenApiSchema GenerateSchema( + public IOpenApiSchema GenerateSchema( Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo = null, @@ -38,7 +37,7 @@ public OpenApiSchema GenerateSchema( } } - private OpenApiSchema GenerateSchemaForMember( + private IOpenApiSchema GenerateSchemaForMember( Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo, @@ -50,13 +49,15 @@ private OpenApiSchema GenerateSchemaForMember( ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); - if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema.Reference != null) + if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema is OpenApiSchemaReference reference) { - schema.AllOf = [new OpenApiSchema { Reference = schema.Reference }]; - schema.Reference = null; + schema = new OpenApiSchema() + { + AllOf = [reference], + }; } - if (schema.Reference == null) + if (schema is OpenApiSchema concrete) { var customAttributes = memberInfo.GetInlineAndMetadataAttributes(); @@ -65,23 +66,25 @@ private OpenApiSchema GenerateSchemaForMember( { var requiredAttribute = customAttributes.OfType().FirstOrDefault(); - schema.Nullable = IsNullable(requiredAttribute, dataProperty, memberInfo); + var nullable = IsNullable(requiredAttribute, dataProperty, memberInfo); + + SetNullable(concrete, nullable); - schema.ReadOnly = dataProperty.IsReadOnly; - schema.WriteOnly = dataProperty.IsWriteOnly; - schema.MinLength = modelType == typeof(string) && requiredAttribute is { AllowEmptyStrings: false } ? 1 : null; + concrete.ReadOnly = dataProperty.IsReadOnly; + concrete.WriteOnly = dataProperty.IsWriteOnly; + concrete.MinLength = modelType == typeof(string) && requiredAttribute is { AllowEmptyStrings: false } ? 1 : null; } var defaultValueAttribute = customAttributes.OfType().FirstOrDefault(); if (defaultValueAttribute != null) { - schema.Default = GenerateDefaultValue(dataContract, modelType, defaultValueAttribute.Value); + concrete.Default = GenerateDefaultValue(dataContract, modelType, defaultValueAttribute.Value); } var obsoleteAttribute = customAttributes.OfType().FirstOrDefault(); if (obsoleteAttribute != null) { - schema.Deprecated = true; + concrete.Deprecated = true; } // NullableAttribute behaves differently for Dictionaries @@ -97,9 +100,9 @@ private OpenApiSchema GenerateSchemaForMember( genericTypes.Any(t => t.GetGenericTypeDefinition() == typeof(IDictionary<,>)) || genericTypes.Any(t => t.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)); - if (isDictionaryType) + if (isDictionaryType && schema.AdditionalProperties is OpenApiSchema additionalProperties) { - schema.AdditionalProperties.Nullable = !memberInfo.IsDictionaryValueNonNullable(); + SetNullable(additionalProperties, !memberInfo.IsDictionaryValueNonNullable()); } } @@ -123,7 +126,7 @@ private bool IsNullable(RequiredAttribute requiredAttribute, DataProperty dataPr return nullable; } - private OpenApiSchema GenerateSchemaForParameter( + private IOpenApiSchema GenerateSchemaForParameter( Type modelType, SchemaRepository schemaRepository, ParameterInfo parameterInfo, @@ -135,13 +138,12 @@ private OpenApiSchema GenerateSchemaForParameter( ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); - if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema.Reference != null) + if (_generatorOptions.UseAllOfToExtendReferenceSchemas && schema is OpenApiSchemaReference reference) { - schema.AllOf = [new OpenApiSchema { Reference = schema.Reference }]; - schema.Reference = null; + schema = new OpenApiSchema() { AllOf = [reference] }; } - if (schema.Reference == null) + if (schema is OpenApiSchema concrete) { var customAttributes = parameterInfo.GetCustomAttributes(); @@ -151,7 +153,7 @@ private OpenApiSchema GenerateSchemaForParameter( if (defaultValue != null) { - schema.Default = GenerateDefaultValue(dataContract, modelType, defaultValue); + concrete.Default = GenerateDefaultValue(dataContract, modelType, defaultValue); } schema.ApplyValidationAttributes(customAttributes); @@ -166,7 +168,7 @@ private OpenApiSchema GenerateSchemaForParameter( return schema; } - private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository) + private IOpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository) { var dataContract = GetDataContractFor(modelType); @@ -174,12 +176,12 @@ private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository sch ? GeneratePolymorphicSchema(schemaRepository, knownTypesDataContracts) : GenerateConcreteSchema(dataContract, schemaRepository); - if (schema.Reference == null) + if (schema is not OpenApiSchemaReference) { ApplyFilters(schema, modelType, schemaRepository); - if (Nullable.GetUnderlyingType(modelType) != null) + if (Nullable.GetUnderlyingType(modelType) != null && schema is OpenApiSchema concrete) { - schema.Nullable = true; + SetNullable(concrete, true); } } @@ -233,9 +235,9 @@ private OpenApiSchema GeneratePolymorphicSchema( typeof(System.IO.Pipelines.PipeReader), ]; - private OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) + private IOpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRepository schemaRepository) { - if (TryGetCustomTypeMapping(dataContract.UnderlyingType, out Func customSchemaFactory)) + if (TryGetCustomTypeMapping(dataContract.UnderlyingType, out Func customSchemaFactory)) { return customSchemaFactory(); } @@ -294,7 +296,7 @@ private OpenApiSchema GenerateConcreteSchema(DataContract dataContract, SchemaRe : schemaFactory(); } - private bool TryGetCustomTypeMapping(Type modelType, out Func schemaFactory) + private bool TryGetCustomTypeMapping(Type modelType, out Func schemaFactory) { return _generatorOptions.CustomTypeMappings.TryGetValue(modelType, out schemaFactory) || @@ -315,6 +317,14 @@ private static OpenApiSchema CreatePrimitiveSchema(DataContract dataContract) { var enumValues = underlyingType.GetEnumValues().Cast(); + /* + See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3428 + if (dataContract.UnderlyingType != underlyingType) + { + SetNullable(schema, true); + enumValues = enumValues.Append(null); + }*/ + schema.Enum = [.. enumValues .Select(value => dataContract.JsonConverter(value)) .Distinct() @@ -368,7 +378,7 @@ private OpenApiSchema CreateObjectSchema(DataContract dataContract, SchemaReposi var schema = new OpenApiSchema { Type = JsonSchemaTypes.Object, - Properties = new Dictionary(), + Properties = new Dictionary(), Required = new SortedSet(), AdditionalPropertiesAllowed = false }; @@ -385,10 +395,12 @@ private OpenApiSchema CreateObjectSchema(DataContract dataContract, SchemaReposi if (_generatorOptions.UseAllOfForInheritance) { root = new OpenApiSchema(); + root.AllOf ??= []; root.AllOf.Add(baseTypeSchema); } else { + schema.AllOf ??= []; schema.AllOf.Add(baseTypeSchema); } @@ -455,6 +467,11 @@ private OpenApiSchema CreateObjectSchema(DataContract dataContract, SchemaReposi root.AllOf.Add(schema); } + if (schema.Required?.Count > 1) + { + schema.Required = new SortedSet(schema.Required); + } + return root; } @@ -503,14 +520,18 @@ private bool TryGetDiscriminatorFor( if (discriminatorValue != null) { - discriminator.Mapping.Add(discriminatorValue, GenerateConcreteSchema(knownTypeDataContract, schemaRepository).Reference.ReferenceV3); + if (GenerateConcreteSchema(knownTypeDataContract, schemaRepository) is OpenApiSchemaReference reference) + { + discriminator.Mapping ??= new Dictionary(); + discriminator.Mapping.Add(discriminatorValue, reference); + } } } return true; } - private OpenApiSchema GenerateReferencedSchema( + private OpenApiSchemaReference GenerateReferencedSchema( DataContract dataContract, SchemaRepository schemaRepository, Func definitionFactory) @@ -532,7 +553,7 @@ private OpenApiSchema GenerateReferencedSchema( } private void ApplyFilters( - OpenApiSchema schema, + IOpenApiSchema schema, Type type, SchemaRepository schemaRepository, MemberInfo memberInfo = null, @@ -551,7 +572,7 @@ private void ApplyFilters( } } - private Microsoft.OpenApi.Any.IOpenApiAny GenerateDefaultValue( + private System.Text.Json.Nodes.JsonNode GenerateDefaultValue( DataContract dataContract, Type modelType, object defaultValue) @@ -571,6 +592,20 @@ private Microsoft.OpenApi.Any.IOpenApiAny GenerateDefaultValue( return JsonModelFactory.CreateFromJson(defaultAsJson); } - private static string FromDataType(DataType dataType) - => dataType.ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture); + private static void SetNullable(OpenApiSchema schema, bool nullable) + { + // See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/3387 + if (nullable) + { + schema.Type ??= JsonSchemaType.Null; + schema.Type |= JsonSchemaType.Null; + } + else if (schema.Type.HasValue) + { + schema.Type &= ~JsonSchemaType.Null; + } + } + + private static JsonSchemaType FromDataType(DataType dataType) => + Enum.Parse(dataType.ToString()); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs index dd12f4dba3..1ce3a270f0 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGeneratorOptions.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -6,7 +6,7 @@ public class SchemaGeneratorOptions { public SchemaGeneratorOptions() { - CustomTypeMappings = new Dictionary>(); + CustomTypeMappings = new Dictionary>(); SchemaIdSelector = DefaultSchemaIdSelector; SubTypesSelector = DefaultSubTypesSelector; DiscriminatorNameSelector = DefaultDiscriminatorNameSelector; @@ -14,7 +14,7 @@ public SchemaGeneratorOptions() SchemaFilters = []; } - public IDictionary> CustomTypeMappings { get; set; } + public IDictionary> CustomTypeMappings { get; set; } public bool UseInlineDefinitionsForEnums { get; set; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentAsyncFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentAsyncFilter.cs index 48c4753450..eabf06e659 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentAsyncFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentAsyncFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentFilter.cs index 40c087d5d9..34709b12d9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IDocumentFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationAsyncFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationAsyncFilter.cs index 59ace9898f..34a51a0dbf 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationAsyncFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationAsyncFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationFilter.cs index 44b280e74c..d03c299388 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IOperationFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterAsyncFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterAsyncFilter.cs index 8294a08462..3512c98759 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterAsyncFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterAsyncFilter.cs @@ -1,8 +1,8 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface IParameterAsyncFilter { - Task ApplyAsync(OpenApiParameter parameter, ParameterFilterContext context, CancellationToken cancellationToken); + Task ApplyAsync(IOpenApiParameter parameter, ParameterFilterContext context, CancellationToken cancellationToken); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterFilter.cs index 636ea14893..0bcd3d264d 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IParameterFilter.cs @@ -1,8 +1,8 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface IParameterFilter { - void Apply(OpenApiParameter parameter, ParameterFilterContext context); + void Apply(IOpenApiParameter parameter, ParameterFilterContext context); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyAsyncFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyAsyncFilter.cs index a7019626a2..b7f95a506b 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyAsyncFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyAsyncFilter.cs @@ -1,8 +1,8 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface IRequestBodyAsyncFilter { - Task ApplyAsync(OpenApiRequestBody requestBody, RequestBodyFilterContext context, CancellationToken cancellationToken); + Task ApplyAsync(IOpenApiRequestBody requestBody, RequestBodyFilterContext context, CancellationToken cancellationToken); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyFilter.cs index e26acb75b3..7d379bd982 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/IRequestBodyFilter.cs @@ -1,8 +1,8 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface IRequestBodyFilter { - void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context); + void Apply(IOpenApiRequestBody requestBody, RequestBodyFilterContext context); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ISchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ISchemaGenerator.cs index 5c0181f2be..993b6114c4 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ISchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ISchemaGenerator.cs @@ -1,12 +1,12 @@ using System.Reflection; -using Microsoft.OpenApi.Models; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; public interface ISchemaGenerator { - OpenApiSchema GenerateSchema( + IOpenApiSchema GenerateSchema( Type modelType, SchemaRepository schemaRepository, MemberInfo memberInfo = null, diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OpenApiAnyFactory.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OpenApiAnyFactory.cs deleted file mode 100644 index 32cb72909b..0000000000 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OpenApiAnyFactory.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Text.Json; -using Microsoft.OpenApi.Any; - -namespace Swashbuckle.AspNetCore.SwaggerGen; - -public static class OpenApiAnyFactory -{ - public static IOpenApiAny CreateFromJson(string json) - => CreateFromJson(json, null); - - public static IOpenApiAny CreateFromJson(string json, JsonSerializerOptions options) - { - if (json is null) - { - return null; - } - - try - { - var element = JsonSerializer.Deserialize(json, options); - return CreateFromJsonElement(element); - } - catch (Exception) - { - return null; - } - } - - private static OpenApiArray CreateOpenApiArray(JsonElement jsonElement) - { - var openApiArray = new OpenApiArray(); - - foreach (var item in jsonElement.EnumerateArray()) - { - openApiArray.Add(CreateFromJsonElement(item)); - } - - return openApiArray; - } - - private static OpenApiObject CreateOpenApiObject(JsonElement jsonElement) - { - var openApiObject = new OpenApiObject(); - - foreach (var property in jsonElement.EnumerateObject()) - { - openApiObject.Add(property.Name, CreateFromJsonElement(property.Value)); - } - - return openApiObject; - } - - private static IOpenApiAny CreateFromJsonElement(JsonElement jsonElement) - { - if (jsonElement.ValueKind == JsonValueKind.Null) - { - return new OpenApiNull(); - } - - if (jsonElement.ValueKind == JsonValueKind.True || jsonElement.ValueKind == JsonValueKind.False) - { - return new OpenApiBoolean(jsonElement.GetBoolean()); - } - - if (jsonElement.ValueKind == JsonValueKind.Number) - { - if (jsonElement.TryGetInt32(out int intValue)) - { - return new OpenApiInteger(intValue); - } - - if (jsonElement.TryGetInt64(out long longValue)) - { - return new OpenApiLong(longValue); - } - - if (jsonElement.TryGetSingle(out float floatValue) && !float.IsInfinity(floatValue)) - { - return new OpenApiFloat(floatValue); - } - - if (jsonElement.TryGetDouble(out double doubleValue)) - { - return new OpenApiDouble(doubleValue); - } - } - - if (jsonElement.ValueKind == JsonValueKind.String) - { - return new OpenApiString(jsonElement.ToString()); - } - - if (jsonElement.ValueKind == JsonValueKind.Array) - { - return CreateOpenApiArray(jsonElement); - } - - if (jsonElement.ValueKind == JsonValueKind.Object) - { - return CreateOpenApiObject(jsonElement); - } - - throw new ArgumentException($"Unsupported value kind {jsonElement.ValueKind}"); - } -} diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OperationFilterContext.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OperationFilterContext.cs index e71059c169..daa3e312e9 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OperationFilterContext.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/OperationFilterContext.cs @@ -1,5 +1,6 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -7,6 +8,7 @@ public class OperationFilterContext( ApiDescription apiDescription, ISchemaGenerator schemaRegistry, SchemaRepository schemaRepository, + OpenApiDocument document, MethodInfo methodInfo) { public ApiDescription ApiDescription { get; } = apiDescription; @@ -17,5 +19,7 @@ public class OperationFilterContext( public MethodInfo MethodInfo { get; } = methodInfo; + public OpenApiDocument Document { get; } = document; + public string DocumentName => SchemaRepository.DocumentName; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ParameterFilterContext.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ParameterFilterContext.cs index 7defd60774..6c951bc874 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ParameterFilterContext.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/ParameterFilterContext.cs @@ -1,5 +1,6 @@ using System.Reflection; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -7,6 +8,7 @@ public class ParameterFilterContext( ApiParameterDescription apiParameterDescription, ISchemaGenerator schemaGenerator, SchemaRepository schemaRepository, + OpenApiDocument document, PropertyInfo propertyInfo = null, ParameterInfo parameterInfo = null) { @@ -20,5 +22,7 @@ public class ParameterFilterContext( public ParameterInfo ParameterInfo { get; } = parameterInfo; + public OpenApiDocument Document { get; } = document; + public string DocumentName => SchemaRepository.DocumentName; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/RequestBodyFilterContext.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/RequestBodyFilterContext.cs index fd01cd3552..d9beeb9244 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/RequestBodyFilterContext.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/RequestBodyFilterContext.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -6,7 +7,8 @@ public class RequestBodyFilterContext( ApiParameterDescription bodyParameterDescription, IEnumerable formParameterDescriptions, ISchemaGenerator schemaGenerator, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { public ApiParameterDescription BodyParameterDescription { get; } = bodyParameterDescription; @@ -16,5 +18,7 @@ public class RequestBodyFilterContext( public SchemaRepository SchemaRepository { get; } = schemaRepository; + public OpenApiDocument Document { get; } = document; + public string DocumentName => SchemaRepository.DocumentName; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SchemaRepository.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SchemaRepository.cs index 82753cc036..537e29bf02 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SchemaRepository.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SchemaRepository.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -8,7 +8,7 @@ public class SchemaRepository(string documentName = null) public string DocumentName { get; } = documentName; - public Dictionary Schemas { get; private set; } = []; + public Dictionary Schemas { get; private set; } = []; public void RegisterType(Type type, string schemaId) { @@ -24,14 +24,11 @@ public void RegisterType(Type type, string schemaId) _reservedIds.Add(type, schemaId); } - public bool TryLookupByType(Type type, out OpenApiSchema referenceSchema) + public bool TryLookupByType(Type type, out OpenApiSchemaReference referenceSchema) { if (_reservedIds.TryGetValue(type, out string schemaId)) { - referenceSchema = new OpenApiSchema - { - Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = schemaId } - }; + referenceSchema = new OpenApiSchemaReference(schemaId); return true; } @@ -39,13 +36,10 @@ public bool TryLookupByType(Type type, out OpenApiSchema referenceSchema) return false; } - public OpenApiSchema AddDefinition(string schemaId, OpenApiSchema schema) + public OpenApiSchemaReference AddDefinition(string schemaId, OpenApiSchema schema) { Schemas.Add(schemaId, schema); - return new OpenApiSchema - { - Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = schemaId } - }; + return new OpenApiSchemaReference(schemaId); } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs index 55c44f9c3d..2f57b2dbc4 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.Swagger; @@ -39,13 +39,19 @@ public async Task GetSwaggerAsync( var (filterContext, document) = GetSwaggerDocumentWithoutPaths(documentName, host, basePath); document.Paths = await GeneratePathsAsync(document, filterContext.ApiDescriptions, filterContext.SchemaRepository); - document.Components.SecuritySchemes = await GetSecuritySchemesAsync(); + + // See https://github.com/microsoft/OpenAPI.NET/issues/2300#issuecomment-2775307399 + foreach (var scheme in await GetSecuritySchemesAsync()) + { + document.AddComponent(scheme.Key, scheme.Value); + } if (_options.SecurityRequirements is { Count: > 0 } requirements) { foreach (var requirement in requirements) { - document.SecurityRequirements.Add(requirement); + document.Security ??= []; + document.Security.Add(requirement(document)); } } @@ -59,7 +65,7 @@ public async Task GetSwaggerAsync( filter.Apply(document, filterContext); } - SortSchemas(document); + SortDocument(document); return document; } @@ -71,13 +77,19 @@ public OpenApiDocument GetSwagger(string documentName, string host = null, strin var (filterContext, document) = GetSwaggerDocumentWithoutPaths(documentName, host, basePath); document.Paths = GeneratePaths(document, filterContext.ApiDescriptions, filterContext.SchemaRepository); - document.Components.SecuritySchemes = GetSecuritySchemesAsync().Result; + + // See https://github.com/microsoft/OpenAPI.NET/issues/2300#issuecomment-2775307399 + foreach (var scheme in GetSecuritySchemesAsync().Result) + { + document.AddComponent(scheme.Key, scheme.Value); + } if (_options.SecurityRequirements is { Count: > 0 } requirements) { foreach (var requirement in requirements) { - document.SecurityRequirements.Add(requirement); + document.Security ??= []; + document.Security.Add(requirement(document)); } } @@ -86,7 +98,7 @@ public OpenApiDocument GetSwagger(string documentName, string host = null, strin filter.Apply(document, filterContext); } - SortSchemas(document); + SortDocument(document); return document; } @@ -113,9 +125,9 @@ public OpenApiDocument GetSwagger(string documentName, string host = null, strin public IList GetDocumentNames() => [.. _options.SwaggerDocs.Keys]; - private void SortSchemas(OpenApiDocument document) + private void SortDocument(OpenApiDocument document) { - document.Components.Schemas = new SortedDictionary(document.Components.Schemas, _options.SchemaComparer); + document.Components.Schemas = new SortedDictionary(document.Components.Schemas, _options.SchemaComparer); } private (DocumentFilterContext, OpenApiDocument) GetSwaggerDocumentWithoutPaths(string documentName, string host = null, string basePath = null) @@ -150,11 +162,11 @@ private void SortSchemas(OpenApiDocument document) return (new DocumentFilterContext(applicableApiDescriptions, _schemaGenerator, schemaRepository), swaggerDoc); } - private async Task> GetSecuritySchemesAsync() + private async Task> GetSecuritySchemesAsync() { if (!_options.InferSecuritySchemes) { - return new Dictionary(_options.SecuritySchemes); + return new Dictionary(_options.SecuritySchemes); } var authenticationSchemes = (_authenticationSchemeProvider is not null) @@ -177,7 +189,7 @@ private async Task> GetSecurityScheme Scheme = "bearer", // "bearer" refers to the header name here In = ParameterLocation.Header, BearerFormat = "Json Web Token" - }); + } as IOpenApiSecurityScheme); } private List GenerateServers(string host, string basePath) @@ -196,7 +208,7 @@ private async Task GeneratePathsAsync( OpenApiDocument document, IEnumerable apiDescriptions, SchemaRepository schemaRepository, - Func, SchemaRepository, Task>> operationsGenerator) + Func, SchemaRepository, Task>> operationsGenerator) { var apiDescriptionsByPath = apiDescriptions .OrderBy(_options.SortKeySelector) @@ -211,7 +223,7 @@ private async Task GeneratePathsAsync( { Operations = await operationsGenerator(document, group, schemaRepository) }); - }; + } return paths; } @@ -240,7 +252,7 @@ private async Task GeneratePathsAsync( GenerateOperationsAsync); } - private IEnumerable<(OperationType, ApiDescription)> GetOperationsGroupedByMethod( + private IEnumerable<(HttpMethod, ApiDescription)> GetOperationsGroupedByMethod( IEnumerable apiDescriptions) { return apiDescriptions @@ -249,13 +261,13 @@ private async Task GeneratePathsAsync( .Select(PrepareGenerateOperation); } - private Dictionary GenerateOperations( + private Dictionary GenerateOperations( OpenApiDocument document, IEnumerable apiDescriptions, SchemaRepository schemaRepository) { var apiDescriptionsByMethod = GetOperationsGroupedByMethod(apiDescriptions); - var operations = new Dictionary(); + var operations = new Dictionary(); foreach ((var operationType, var description) in apiDescriptionsByMethod) { @@ -265,13 +277,13 @@ private Dictionary GenerateOperations( return operations; } - private async Task> GenerateOperationsAsync( + private async Task> GenerateOperationsAsync( OpenApiDocument document, IEnumerable apiDescriptions, SchemaRepository schemaRepository) { var apiDescriptionsByMethod = GetOperationsGroupedByMethod(apiDescriptions); - var operations = new Dictionary(); + var operations = new Dictionary(); foreach ((var operationType, var description) in apiDescriptionsByMethod) { @@ -281,7 +293,7 @@ private async Task> GenerateOperatio return operations; } - private (OperationType OperationType, ApiDescription ApiDescription) PrepareGenerateOperation(IGrouping group) + private (HttpMethod OperationType, ApiDescription ApiDescription) PrepareGenerateOperation(IGrouping group) { var httpMethod = group.Key ?? throw new SwaggerGeneratorException(string.Format( "Ambiguous HTTP method for action - {0}. " + @@ -319,11 +331,11 @@ private async Task GenerateOperationAsync( OpenApiDocument document, ApiDescription apiDescription, SchemaRepository schemaRepository, - Func>> parametersGenerator, - Func> bodyGenerator, + Func>> parametersGenerator, + Func> bodyGenerator, Func applyFilters) { - var operation = await GenerateOpenApiOperationFromMetadataAsync(apiDescription, schemaRepository); + var operation = await GenerateOpenApiOperationFromMetadataAsync(apiDescription, schemaRepository, document); try { @@ -331,8 +343,8 @@ private async Task GenerateOperationAsync( { Tags = GenerateOperationTags(document, apiDescription), OperationId = _options.OperationIdSelector(apiDescription), - Parameters = await parametersGenerator(apiDescription, schemaRepository), - RequestBody = await bodyGenerator(apiDescription, schemaRepository), + Parameters = await parametersGenerator(apiDescription, schemaRepository, document), + RequestBody = await bodyGenerator(apiDescription, schemaRepository, document), Responses = GenerateResponses(apiDescription, schemaRepository), Deprecated = apiDescription.CustomAttributes().OfType().Any(), Summary = GenerateSummary(apiDescription), @@ -340,7 +352,7 @@ private async Task GenerateOperationAsync( }; apiDescription.TryGetMethodInfo(out MethodInfo methodInfo); - var filterContext = new OperationFilterContext(apiDescription, _schemaGenerator, schemaRepository, methodInfo); + var filterContext = new OperationFilterContext(apiDescription, _schemaGenerator, schemaRepository, document, methodInfo); await applyFilters(operation, filterContext); @@ -360,8 +372,8 @@ private OpenApiOperation GenerateOperation(OpenApiDocument document, ApiDescript document, apiDescription, schemaRepository, - (description, repository) => Task.FromResult(GenerateParameters(description, repository)), - (description, repository) => Task.FromResult(GenerateRequestBody(description, repository)), + (description, repository, document) => Task.FromResult(GenerateParameters(description, repository, document)), + (description, repository, document) => Task.FromResult(GenerateRequestBody(description, repository, document)), (operation, filterContext) => { foreach (var filter in _options.OperationFilters) @@ -398,7 +410,10 @@ private async Task GenerateOperationAsync( }); } - private async Task GenerateOpenApiOperationFromMetadataAsync(ApiDescription apiDescription, SchemaRepository schemaRepository) + private async Task GenerateOpenApiOperationFromMetadataAsync( + ApiDescription apiDescription, + SchemaRepository schemaRepository, + OpenApiDocument document) { var metadata = apiDescription.ActionDescriptor?.EndpointMetadata; var operation = metadata?.OfType().SingleOrDefault(); @@ -409,14 +424,19 @@ private async Task GenerateOpenApiOperationFromMetadataAsync(A } // Schemas will be generated via Swashbuckle by default. - foreach (var parameter in operation.Parameters) + foreach (var parameter in operation.Parameters ?? []) { var apiParameter = apiDescription.ParameterDescriptions.SingleOrDefault((p) => p.Name == parameter.Name && !p.IsFromBody() && !p.IsFromForm() && !p.IsIllegalHeaderParameter()); if (apiParameter is not null) { - var (parameterAndContext, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository); - parameter.Name = parameterAndContext.Name; - parameter.Schema = parameterAndContext.Schema; + var (parameterAndContext, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository, document); + + if (parameter is OpenApiParameter concrete) + { + concrete.Name = parameterAndContext.Name; + concrete.Schema = parameterAndContext.Schema; + } + parameter.Description ??= parameterAndContext.Description; foreach (var filter in _options.ParameterAsyncFilters) @@ -469,7 +489,8 @@ private async Task GenerateOpenApiOperationFromMetadataAsync(A bodyParameterDescription: bodyParameterDescription, formParameterDescriptions: bodyParameterDescription is null ? fromFormParameters : null, schemaGenerator: _schemaGenerator, - schemaRepository: schemaRepository); + schemaRepository: schemaRepository, + document); foreach (var filter in _options.RequestBodyAsyncFilters) { @@ -484,18 +505,21 @@ private async Task GenerateOpenApiOperationFromMetadataAsync(A } } - foreach (var kvp in operation.Responses) + if (operation.Responses is { Count: > 0 } responses) { - var response = kvp.Value; - var responseModel = apiDescription.SupportedResponseTypes.SingleOrDefault((p) => p.StatusCode.ToString() == kvp.Key); - if (responseModel is not null) + foreach (var kvp in responses) { - var responseContentTypes = response?.Content?.Values; - if (responseContentTypes is not null) + var response = kvp.Value; + var responseModel = apiDescription.SupportedResponseTypes.SingleOrDefault((p) => p.StatusCode.ToString() == kvp.Key); + if (responseModel is not null) { - foreach (var content in responseContentTypes) + var responseContentTypes = response?.Content?.Values; + if (responseContentTypes is not null) { - content.Schema = GenerateSchema(responseModel.Type, schemaRepository); + foreach (var content in responseContentTypes) + { + content.Schema = GenerateSchema(responseModel.Type, schemaRepository); + } } } } @@ -504,13 +528,31 @@ private async Task GenerateOpenApiOperationFromMetadataAsync(A return operation; } - private List GenerateOperationTags(OpenApiDocument document, ApiDescription apiDescription) - => [.. _options.TagsSelector(apiDescription).Select(tagName => CreateTag(tagName, document))]; + private HashSet GenerateOperationTags(OpenApiDocument document, ApiDescription apiDescription) + { + // The tags must be present at the document level for the tag references + // to be serialized correctly at the operation level, so we need to add + // them to the document before adding the references to the operation. + // See https://github.com/microsoft/OpenAPI.NET/issues/2319. + string[] names = [.. _options.TagsSelector(apiDescription)]; + + if (names.Length > 0) + { + document.Tags ??= new HashSet(); + foreach (var name in names) + { + document.Tags.Add(new OpenApiTag { Name = name }); + } + } + + return [.. names.Select((name) => new OpenApiTagReference(name, document))]; + } - private static async Task> GenerateParametersAsync( + private static async Task> GenerateParametersAsync( ApiDescription apiDescription, SchemaRepository schemaRepository, - Func> parameterGenerator) + OpenApiDocument document, + Func> parameterGenerator) { if (apiDescription.ParameterDescriptions.Any(IsFromFormAttributeUsedWithIFormFile)) { @@ -530,31 +572,37 @@ private static async Task> GenerateParametersAsync( && !apiParam.IsIllegalHeaderParameter(); }); - var parameters = new List(); + var parameters = new List(); foreach (var parameter in applicableApiParameters) { - parameters.Add(await parameterGenerator(parameter, schemaRepository)); + parameters.Add(await parameterGenerator(parameter, schemaRepository, document)); } return parameters; } - private List GenerateParameters(ApiDescription apiDescription, SchemaRepository schemaRepository) + private List GenerateParameters( + ApiDescription apiDescription, + SchemaRepository schemaRepository, + OpenApiDocument document) { return GenerateParametersAsync( apiDescription, schemaRepository, - (parameter, schemaRepository) => Task.FromResult(GenerateParameter(parameter, schemaRepository))).Result; + document, + (parameter, schemaRepository, document) => Task.FromResult(GenerateParameter(parameter, schemaRepository, document))).Result; } - private async Task> GenerateParametersAsync( + private async Task> GenerateParametersAsync( ApiDescription apiDescription, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { return await GenerateParametersAsync( apiDescription, schemaRepository, + document, GenerateParameterAsync); } @@ -595,8 +643,9 @@ apiParameter.Type is not null && var description = schema.Description; if (string.IsNullOrEmpty(description) && - !string.IsNullOrEmpty(schema?.Reference?.Id) && - schemaRepository.Schemas.TryGetValue(schema.Reference.Id, out var openApiSchema)) + schema is OpenApiSchemaReference reference && + !string.IsNullOrEmpty(reference.Reference.Id) && + schemaRepository.Schemas.TryGetValue(reference.Reference.Id, out var openApiSchema)) { description = openApiSchema.Description; } @@ -624,7 +673,8 @@ apiParameter.Type is not null && private (OpenApiParameter, ParameterFilterContext) GenerateParameterAndContext( ApiParameterDescription apiParameter, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { var parameter = GenerateParameterWithoutFilter(apiParameter, schemaRepository); @@ -632,6 +682,7 @@ apiParameter.Type is not null && apiParameter, _schemaGenerator, schemaRepository, + document, apiParameter.PropertyInfo(), apiParameter.ParameterInfo()); @@ -640,9 +691,10 @@ apiParameter.Type is not null && private OpenApiParameter GenerateParameter( ApiParameterDescription apiParameter, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { - var (parameter, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository); + var (parameter, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository, document); foreach (var filter in _options.ParameterFilters) { @@ -654,9 +706,10 @@ private OpenApiParameter GenerateParameter( private async Task GenerateParameterAsync( ApiParameterDescription apiParameter, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { - var (parameter, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository); + var (parameter, filterContext) = GenerateParameterAndContext(apiParameter, schemaRepository, document); foreach (var filter in _options.ParameterAsyncFilters) { @@ -671,7 +724,7 @@ private async Task GenerateParameterAsync( return parameter; } - private OpenApiSchema GenerateSchema( + private IOpenApiSchema GenerateSchema( Type type, SchemaRepository schemaRepository, PropertyInfo propertyInfo = null, @@ -690,9 +743,10 @@ private OpenApiSchema GenerateSchema( } } - private (OpenApiRequestBody RequestBody, RequestBodyFilterContext FilterContext) GenerateRequestBodyAndFilterContext( + private (IOpenApiRequestBody RequestBody, RequestBodyFilterContext FilterContext) GenerateRequestBodyAndFilterContext( ApiDescription apiDescription, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { OpenApiRequestBody requestBody = null; RequestBodyFilterContext filterContext = null; @@ -712,7 +766,8 @@ private OpenApiSchema GenerateSchema( bodyParameterDescription: bodyParameter, formParameterDescriptions: null, schemaGenerator: _schemaGenerator, - schemaRepository: schemaRepository); + schemaRepository: schemaRepository, + document); } else if (formParameters.Count > 0) { @@ -722,17 +777,19 @@ private OpenApiSchema GenerateSchema( bodyParameterDescription: null, formParameterDescriptions: formParameters, schemaGenerator: _schemaGenerator, - schemaRepository: schemaRepository); + schemaRepository: schemaRepository, + document); } return (requestBody, filterContext); } - private OpenApiRequestBody GenerateRequestBody( + private IOpenApiRequestBody GenerateRequestBody( ApiDescription apiDescription, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { - var (requestBody, filterContext) = GenerateRequestBodyAndFilterContext(apiDescription, schemaRepository); + var (requestBody, filterContext) = GenerateRequestBodyAndFilterContext(apiDescription, schemaRepository, document); if (requestBody != null) { @@ -745,11 +802,12 @@ private OpenApiRequestBody GenerateRequestBody( return requestBody; } - private async Task GenerateRequestBodyAsync( + private async Task GenerateRequestBodyAsync( ApiDescription apiDescription, - SchemaRepository schemaRepository) + SchemaRepository schemaRepository, + OpenApiDocument document) { - var (requestBody, filterContext) = GenerateRequestBodyAndFilterContext(apiDescription, schemaRepository); + var (requestBody, filterContext) = GenerateRequestBodyAndFilterContext(apiDescription, schemaRepository, document); if (requestBody != null) { @@ -848,13 +906,13 @@ private OpenApiRequestBody GenerateRequestBodyFromFormParameters( }; } - private OpenApiSchema GenerateSchemaFromFormParameters( + private IOpenApiSchema GenerateSchemaFromFormParameters( IEnumerable formParameters, SchemaRepository schemaRepository) { - var properties = new Dictionary(); + var properties = new Dictionary(); var requiredPropertyNames = new List(); - var ownSchemas = new List(); + var ownSchemas = new List(); foreach (var formParameter in formParameters) { @@ -870,7 +928,7 @@ private OpenApiSchema GenerateSchemaFromFormParameters( formParameter.ParameterInfo()) : new OpenApiSchema { Type = JsonSchemaTypes.String }; - if (schema.Reference is null || + if (schema is not OpenApiSchemaReference || (formParameter.ModelMetadata?.ModelType is not null && (Nullable.GetUnderlyingType(formParameter.ModelMetadata.ModelType) ?? formParameter.ModelMetadata.ModelType).IsEnum)) { var name = _options.DescribeAllParametersInCamelCase @@ -917,12 +975,12 @@ private OpenApiSchema GenerateSchemaFromFormParameters( return GenerateSchemaForProperties(properties, requiredPropertyNames); - static OpenApiSchema GenerateSchemaForProperties(Dictionary properties, List requiredPropertyNames) => + static OpenApiSchema GenerateSchemaForProperties(Dictionary properties, List requiredPropertyNames) => new() { Type = JsonSchemaTypes.Object, Properties = properties, - Required = new SortedSet(requiredPropertyNames) + Required = new SortedSet(requiredPropertyNames), }; } @@ -948,9 +1006,18 @@ private OpenApiResponse GenerateResponse( string statusCode, ApiResponseType apiResponseType) { - var description = ResponseDescriptionMap - .FirstOrDefault((entry) => Regex.IsMatch(statusCode, entry.Key)) - .Value; + string description = null; + +#if NET10_0_OR_GREATER + description = apiResponseType.Description; +#endif + + if (string.IsNullOrEmpty(description)) + { + description = ResponseDescriptionMap + .FirstOrDefault((entry) => Regex.IsMatch(statusCode, entry.Key)) + .Value; + } var responseContentTypes = InferResponseContentTypes(apiDescription, apiResponseType); @@ -1005,16 +1072,16 @@ private static bool IsFromFormAttributeUsedWithIFormFile(ApiParameterDescription return fromFormAttribute != null && parameterInfo?.ParameterType == typeof(IFormFile); } - private static readonly Dictionary OperationTypeMap = new(StringComparer.OrdinalIgnoreCase) + private static readonly Dictionary OperationTypeMap = new(StringComparer.OrdinalIgnoreCase) { - ["GET"] = OperationType.Get, - ["PUT"] = OperationType.Put, - ["POST"] = OperationType.Post, - ["DELETE"] = OperationType.Delete, - ["OPTIONS"] = OperationType.Options, - ["HEAD"] = OperationType.Head, - ["PATCH"] = OperationType.Patch, - ["TRACE"] = OperationType.Trace, + ["GET"] = HttpMethod.Get, + ["PUT"] = HttpMethod.Put, + ["POST"] = HttpMethod.Post, + ["DELETE"] = HttpMethod.Delete, + ["OPTIONS"] = HttpMethod.Options, + ["HEAD"] = HttpMethod.Head, + ["PATCH"] = HttpMethod.Patch, + ["TRACE"] = HttpMethod.Trace, }; private static readonly Dictionary ParameterLocationMap = new() @@ -1117,7 +1184,4 @@ private static string GenerateDescription(ApiDescription apiDescription) => ?.OfType() .Select((p) => p.Description) .LastOrDefault(); - - private static OpenApiTag CreateTag(string name, OpenApiDocument _) => - new() { Name = name }; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGeneratorOptions.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGeneratorOptions.cs index 38da942d54..ba659c64ae 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGeneratorOptions.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGeneratorOptions.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Routing; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; @@ -19,7 +19,7 @@ public SwaggerGeneratorOptions() SecuritySchemesSelector = null; SchemaComparer = StringComparer.Ordinal; Servers = []; - SecuritySchemes = new Dictionary(); + SecuritySchemes = new Dictionary(); SecurityRequirements = []; ParameterFilters = []; ParameterAsyncFilters = []; @@ -49,15 +49,15 @@ public SwaggerGeneratorOptions() public bool InferSecuritySchemes { get; set; } - public Func, IDictionary> SecuritySchemesSelector { get; set; } + public Func, IDictionary> SecuritySchemesSelector { get; set; } public bool DescribeAllParametersInCamelCase { get; set; } public List Servers { get; set; } - public IDictionary SecuritySchemes { get; set; } + public IDictionary SecuritySchemes { get; set; } - public IList SecurityRequirements { get; set; } + public IList> SecurityRequirements { get; set; } public IComparer SchemaComparer { get; set; } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs index a7f5c17a13..8fcd3540c5 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsDocumentFilter.cs @@ -1,31 +1,15 @@ using System.Xml.XPath; using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; -public class XmlCommentsDocumentFilter : IDocumentFilter +public class XmlCommentsDocumentFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) : IDocumentFilter { private const string SummaryTag = "summary"; - private readonly IReadOnlyDictionary _xmlDocMembers; - private readonly SwaggerGeneratorOptions _options; - - public XmlCommentsDocumentFilter(XPathDocument xmlDoc) - : this(xmlDoc, null) - { - } - - public XmlCommentsDocumentFilter(XPathDocument xmlDoc, SwaggerGeneratorOptions options) - : this(XmlCommentsDocumentHelper.CreateMemberDictionary(xmlDoc), options) - { - } - - internal XmlCommentsDocumentFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) - { - _xmlDocMembers = xmlDocMembers; - _options = options; - } + private readonly IReadOnlyDictionary _xmlDocMembers = xmlDocMembers; + private readonly SwaggerGeneratorOptions _options = options; public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { @@ -48,13 +32,18 @@ public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) var summaryNode = typeNode.SelectFirstChild(SummaryTag); if (summaryNode != null) { - swaggerDoc.Tags ??= []; + swaggerDoc.Tags ??= new HashSet(); - swaggerDoc.Tags.Add(new OpenApiTag + var name = nameAndType.Key; + var tag = swaggerDoc.Tags.FirstOrDefault((p) => p?.Name == name); + + if (tag is null) { - Name = nameAndType.Key, - Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml, _options?.XmlCommentEndOfLine) - }); + tag = new() { Name = name }; + swaggerDoc.Tags.Add(tag); + } + + tag.Description ??= XmlCommentsTextHelper.Humanize(summaryNode.InnerXml, _options?.XmlCommentEndOfLine); } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs index 759988f1b0..e85b6cdab1 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsExampleHelper.cs @@ -1,23 +1,37 @@ using System.Text.Json; -using Microsoft.OpenApi.Models; +using System.Text.Json.Nodes; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; internal static class XmlCommentsExampleHelper { - public static Microsoft.OpenApi.Any.IOpenApiAny Create( + public static JsonNode Create( SchemaRepository schemaRepository, - OpenApiSchema schema, + IOpenApiSchema schema, string exampleString) { - var isStringType = - schema?.ResolveType(schemaRepository) == JsonSchemaTypes.String && - !string.Equals(exampleString, "null"); + var type = schema?.ResolveType(schemaRepository); - var exampleAsJson = isStringType - ? JsonSerializer.Serialize(exampleString) - : exampleString; + var isStringType = type is { } value && + value.HasFlag(JsonSchemaType.String) && + !value.HasFlag(JsonSchemaType.Null); - return JsonModelFactory.CreateFromJson(exampleAsJson); + if (isStringType) + { + return JsonValue.Create(exampleString); + } + + // HACK If the value is a string, but we can't detect it as one, then + // if parsing it fails, assume it is a string that isn't quoted. There's + // probably a much better way to deal with this scenario. + try + { + return JsonModelFactory.CreateFromJson(exampleString); + } + catch (JsonException) when (exampleString?.StartsWith('"') == false) + { + return JsonValue.Create(exampleString); + } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs index 4db7b72636..ceb13a22f3 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsOperationFilter.cs @@ -1,26 +1,13 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; -public class XmlCommentsOperationFilter : IOperationFilter +public class XmlCommentsOperationFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) : IOperationFilter { - private readonly IReadOnlyDictionary _xmlDocMembers; - private readonly SwaggerGeneratorOptions _options; - - public XmlCommentsOperationFilter(XPathDocument xmlDoc) - : this(XmlCommentsDocumentHelper.CreateMemberDictionary(xmlDoc), null) - { - } - - [ActivatorUtilitiesConstructor] - internal XmlCommentsOperationFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) - { - _xmlDocMembers = xmlDocMembers; - _options = options; - } + private readonly IReadOnlyDictionary _xmlDocMembers = xmlDocMembers; + private readonly SwaggerGeneratorOptions _options = options; public void Apply(OpenApiOperation operation, OperationFilterContext context) { diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs index 8139eae9dc..441b7e8e9d 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsParameterFilter.cs @@ -1,28 +1,15 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; -public class XmlCommentsParameterFilter : IParameterFilter +public class XmlCommentsParameterFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) : IParameterFilter { - private readonly IReadOnlyDictionary _xmlDocMembers; - private readonly SwaggerGeneratorOptions _options; + private readonly IReadOnlyDictionary _xmlDocMembers = xmlDocMembers; + private readonly SwaggerGeneratorOptions _options = options; - public XmlCommentsParameterFilter(XPathDocument xmlDoc) - : this(XmlCommentsDocumentHelper.CreateMemberDictionary(xmlDoc), null) - { - } - - [ActivatorUtilitiesConstructor] - internal XmlCommentsParameterFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) - { - _xmlDocMembers = xmlDocMembers; - _options = options; - } - - public void Apply(OpenApiParameter parameter, ParameterFilterContext context) + public void Apply(IOpenApiParameter parameter, ParameterFilterContext context) { if (context.PropertyInfo != null) { @@ -34,7 +21,7 @@ public void Apply(OpenApiParameter parameter, ParameterFilterContext context) } } - private void ApplyPropertyTags(OpenApiParameter parameter, ParameterFilterContext context) + private void ApplyPropertyTags(IOpenApiParameter parameter, ParameterFilterContext context) { var propertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.PropertyInfo); @@ -47,14 +34,17 @@ private void ApplyPropertyTags(OpenApiParameter parameter, ParameterFilterContex parameter.Schema.Description = null; // No need to duplicate } - var exampleNode = propertyNode.SelectFirstChild("example"); - if (exampleNode != null) + if (parameter is OpenApiParameter concrete) { - parameter.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, exampleNode.ToString()); + var exampleNode = propertyNode.SelectFirstChild("example"); + if (exampleNode != null) + { + concrete.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, exampleNode.ToString()); + } } } - private void ApplyParamTags(OpenApiParameter parameter, ParameterFilterContext context) + private void ApplyParamTags(IOpenApiParameter parameter, ParameterFilterContext context) { if (context.ParameterInfo.Member is not MethodInfo methodInfo) { @@ -81,10 +71,13 @@ private void ApplyParamTags(OpenApiParameter parameter, ParameterFilterContext c { parameter.Description = XmlCommentsTextHelper.Humanize(paramNode.InnerXml, _options?.XmlCommentEndOfLine); - var example = paramNode.GetAttribute("example"); - if (!string.IsNullOrEmpty(example)) + if (parameter is OpenApiParameter concrete) { - parameter.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, example); + var example = paramNode.GetAttribute("example"); + if (!string.IsNullOrEmpty(example)) + { + concrete.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, parameter.Schema, example); + } } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs index ea15f3e128..4eccf74285 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs @@ -1,28 +1,15 @@ using System.Reflection; using System.Xml.XPath; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; -public class XmlCommentsRequestBodyFilter : IRequestBodyFilter +public class XmlCommentsRequestBodyFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) : IRequestBodyFilter { - private readonly IReadOnlyDictionary _xmlDocMembers; - private readonly SwaggerGeneratorOptions _options; + private readonly IReadOnlyDictionary _xmlDocMembers = xmlDocMembers; + private readonly SwaggerGeneratorOptions _options = options; - public XmlCommentsRequestBodyFilter(XPathDocument xmlDoc) - : this(XmlCommentsDocumentHelper.CreateMemberDictionary(xmlDoc), null) - { - } - - [ActivatorUtilitiesConstructor] - internal XmlCommentsRequestBodyFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) - { - _xmlDocMembers = xmlDocMembers; - _options = options; - } - - public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context) + public void Apply(IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { var bodyParameterDescription = context.BodyParameterDescription; @@ -70,9 +57,10 @@ public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext conte { var (summary, example) = GetParamTags(parameterFromForm); value.Description ??= summary; - if (!string.IsNullOrEmpty(example)) + + if (value is OpenApiSchema concrete && !string.IsNullOrEmpty(example)) { - value.Example ??= XmlCommentsExampleHelper.Create(context.SchemaRepository, value, example); + concrete.Example ??= XmlCommentsExampleHelper.Create(context.SchemaRepository, value, example); } } } @@ -100,7 +88,7 @@ public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext conte return (summary, exampleNode?.ToString()); } - private void ApplyPropertyTagsForBody(OpenApiRequestBody requestBody, RequestBodyFilterContext context, PropertyInfo propertyInfo) + private void ApplyPropertyTagsForBody(IOpenApiRequestBody requestBody, RequestBodyFilterContext context, PropertyInfo propertyInfo) { var (summary, example) = GetPropertyTags(propertyInfo); @@ -156,7 +144,7 @@ private void ApplyPropertyTagsForBody(OpenApiRequestBody requestBody, RequestBod return (summary, example); } - private void ApplyParamTagsForBody(OpenApiRequestBody requestBody, RequestBodyFilterContext context, ParameterInfo parameterInfo) + private void ApplyParamTagsForBody(IOpenApiRequestBody requestBody, RequestBodyFilterContext context, ParameterInfo parameterInfo) { var (summary, example) = GetParamTags(parameterInfo); diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs index b564444df5..55b334f843 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsSchemaFilter.cs @@ -1,27 +1,14 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; -using System.Xml.XPath; +using System.Xml.XPath; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen; -public class XmlCommentsSchemaFilter : ISchemaFilter +public class XmlCommentsSchemaFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) : ISchemaFilter { - private readonly IReadOnlyDictionary _xmlDocMembers; - private readonly SwaggerGeneratorOptions _options; + private readonly IReadOnlyDictionary _xmlDocMembers = xmlDocMembers; + private readonly SwaggerGeneratorOptions _options = options; - public XmlCommentsSchemaFilter(XPathDocument xmlDoc) - : this(XmlCommentsDocumentHelper.CreateMemberDictionary(xmlDoc), null) - { - } - - [ActivatorUtilitiesConstructor] - internal XmlCommentsSchemaFilter(IReadOnlyDictionary xmlDocMembers, SwaggerGeneratorOptions options) - { - _xmlDocMembers = xmlDocMembers; - _options = options; - } - - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { ApplyTypeTags(schema, context.Type); @@ -31,7 +18,7 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context) } } - private void ApplyTypeTags(OpenApiSchema schema, Type type) + private void ApplyTypeTags(IOpenApiSchema schema, Type type) { var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(type); @@ -45,7 +32,7 @@ private void ApplyTypeTags(OpenApiSchema schema, Type type) } } - private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) + private void ApplyMemberTags(IOpenApiSchema schema, SchemaFilterContext context) { var fieldOrPropertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.MemberInfo); @@ -63,10 +50,13 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) schema.Description = XmlCommentsTextHelper.Humanize(summaryNode, _options?.XmlCommentEndOfLine); } - var example = recordDefaultConstructorProperty.GetAttribute("example"); - if (!string.IsNullOrEmpty(example)) + if (schema is OpenApiSchema concrete) { - TrySetExample(schema, context, example); + var example = recordDefaultConstructorProperty.GetAttribute("example"); + if (!string.IsNullOrEmpty(example)) + { + TrySetExample(concrete, context, example); + } } } } @@ -79,8 +69,11 @@ private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context) schema.Description = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml, _options?.XmlCommentEndOfLine); } - var exampleNode = fieldOrPropertyNode.SelectFirstChild("example"); - TrySetExample(schema, context, exampleNode?.Value); + if (schema is OpenApiSchema concrete) + { + var exampleNode = fieldOrPropertyNode.SelectFirstChild("example"); + TrySetExample(concrete, context, exampleNode?.Value); + } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj index e22b217a77..954e748e21 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/Swashbuckle.AspNetCore.SwaggerUI.csproj @@ -71,7 +71,7 @@ - <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net9.0 + <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net10.0 <_SdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472 diff --git a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec index 98553901cc..7afe4d237d 100644 --- a/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec +++ b/src/Swashbuckle.AspNetCore/Swashbuckle.AspNetCore.nuspec @@ -25,6 +25,12 @@ + + + + + + diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs index 41369ca8cf..642a3c04c8 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsDocumentFilterTests.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.TestSupport; using Xunit; diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs index 3478d7b284..55e6009d63 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Xunit; @@ -20,6 +20,7 @@ public void Apply_EnrichesOperationMetadata_IfActionDecoratedWithSwaggerOperatio apiDescription: null, schemaRegistry: null, schemaRepository: null, + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -27,7 +28,7 @@ public void Apply_EnrichesOperationMetadata_IfActionDecoratedWithSwaggerOperatio Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); - Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); + Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Reference.Id)]); } [Fact] @@ -47,6 +48,7 @@ public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseA apiDescription: null, schemaRegistry: null, schemaRepository: null, + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -76,6 +78,7 @@ public void Apply_EnrichesResponseMetadata_IfActionDecoratedWithSwaggerResponseC apiDescription: null, schemaRegistry: new SchemaGenerator(new SchemaGeneratorOptions(), new JsonSerializerDataContractResolver(new JsonSerializerOptions())), schemaRepository: new SchemaRepository(), + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -102,6 +105,7 @@ public void Apply_DelegatesToSpecifiedFilter_IfControllerDecoratedWithSwaggerOpe apiDescription: null, schemaRegistry: null, schemaRepository: null, + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -118,6 +122,7 @@ public void Apply_DelegatesToSpecifiedFilter_IfActionDecoratedWithSwaggerOperati apiDescription: null, schemaRegistry: null, schemaRepository: null, + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -152,6 +157,7 @@ public void Apply_EnrichesOperationMetadata_IfMinimalActionDecoratedWithSwaggerO apiDescription: apiDescription, schemaRegistry: null, schemaRepository: null, + document: null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); @@ -159,7 +165,7 @@ public void Apply_EnrichesOperationMetadata_IfMinimalActionDecoratedWithSwaggerO Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); - Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Name)]); + Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Reference.Id)]); } private static AnnotationsOperationFilter Subject() diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs index 6fa6a8ae4f..c722a8638e 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsParameterFilterTests.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Xunit; using Swashbuckle.AspNetCore.SwaggerGen; @@ -18,6 +18,7 @@ public void Apply_EnrichesParameterMetadata_IfParameterDecoratedWithSwaggerParam apiParameterDescription: null, schemaGenerator: null, schemaRepository: null, + document: null, parameterInfo: parameterInfo); Subject().Apply(parameter, filterContext); @@ -35,6 +36,7 @@ public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerParame apiParameterDescription: new ApiParameterDescription(), schemaGenerator: null, schemaRepository: null, + null, propertyInfo: propertyInfo); Subject().Apply(parameter, filterContext); @@ -54,6 +56,7 @@ public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParamete apiParameterDescription: null, schemaGenerator: null, schemaRepository: null, + document: null, parameterInfo: parameterInfo); Subject().Apply(parameter, filterContext); diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs index 374f380391..b258115817 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsRequestBodyFilterTests.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.TestSupport; using Xunit; @@ -23,7 +23,7 @@ public void Apply_EnrichesRequestBodyMetadata_IfControllerParameterDecoratedWith { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, context); @@ -42,7 +42,7 @@ public void Apply_EnrichesRequestBodyMetadata_IfEndpointParameterDecoratedWithSw { ParameterDescriptor = new CustomParameterDescriptor { ParameterInfo = parameterInfo } }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, context); @@ -58,7 +58,7 @@ public void Apply_EnrichesParameterMetadata_IfPropertyDecoratedWithSwaggerReques { ModelMetadata = ModelMetadataFactory.CreateForProperty(typeof(SwaggerAnnotatedType), nameof(SwaggerAnnotatedType.StringWithSwaggerRequestBodyAttribute)) }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, context); @@ -78,7 +78,7 @@ public void Apply_DoesNotModifyTheRequiredFlag_IfNotSpecifiedWithSwaggerParamete { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var context = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, context); diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs index f812a0d70f..0f8a7932db 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsSchemaFilterTests.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; using Xunit; @@ -51,7 +51,7 @@ public void Apply_EnrichesSchemaMetadata_IfPropertyDecoratedWithSwaggerSchemaAtt bool expectedWriteOnly, bool expectedNullable) { - var schema = new OpenApiSchema { Nullable = true }; + var schema = new OpenApiSchema { Type = JsonSchemaType.Null }; var propertyInfo = declaringType .GetProperty(propertyName); var context = new SchemaFilterContext( @@ -66,13 +66,13 @@ public void Apply_EnrichesSchemaMetadata_IfPropertyDecoratedWithSwaggerSchemaAtt Assert.Equal("date", schema.Format); Assert.Equal(expectedReadOnly, schema.ReadOnly); Assert.Equal(expectedWriteOnly, schema.WriteOnly); - Assert.Equal(expectedNullable, schema.Nullable); + Assert.Equal(expectedNullable, schema.Type.Value.HasFlag(JsonSchemaType.Null)); } [Fact] public void Apply_DoesNotModifyFlags_IfNotSpecifiedWithSwaggerSchemaAttribute() { - var schema = new OpenApiSchema { ReadOnly = true, WriteOnly = true, Nullable = true }; + var schema = new OpenApiSchema { ReadOnly = true, WriteOnly = true, Type = JsonSchemaType.Null }; var propertyInfo = typeof(SwaggerAnnotatedType) .GetProperty(nameof(SwaggerAnnotatedType.StringWithSwaggerSchemaAttributeDescriptionOnly)); var context = new SchemaFilterContext( @@ -85,7 +85,7 @@ public void Apply_DoesNotModifyFlags_IfNotSpecifiedWithSwaggerSchemaAttribute() Assert.True(schema.ReadOnly); Assert.True(schema.WriteOnly); - Assert.True(schema.Nullable); + Assert.True(schema.Type.Value.HasFlag(JsonSchemaType.Null)); } [Theory] diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs index 245281c4f4..d6dc05005c 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsOperationFilter.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations.Test; @@ -8,6 +7,7 @@ public class VendorExtensionsOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { - operation.Extensions.Add("X-property1", new OpenApiString("value")); + operation.Extensions ??= new Dictionary(); + operation.Extensions.Add("X-property1", new JsonNodeExtension("value")); } } diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs index 778e553035..492fe9d4db 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/VendorExtensionsSchemaFilter.cs @@ -1,13 +1,16 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Annotations.Test; public class VendorExtensionsSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Extensions.Add("X-property1", new OpenApiString("value")); + if (schema is OpenApiSchema openApiSchema) + { + openApiSchema.Extensions ??= new Dictionary(); + openApiSchema.Extensions.Add("X-property1", new JsonNodeExtension("value")); + } } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs index 85c3a812d5..288883144b 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ApiTestRunnerBaseTests.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Xunit; namespace Swashbuckle.AspNetCore.ApiTesting.Test; @@ -54,9 +54,9 @@ public async Task TestAsync_ThrowsException_IfExpectedStatusCodeIs2xxAndRequestD { ["/api/products"] = new OpenApiPathItem { - Operations = new Dictionary + Operations = new Dictionary { - [OperationType.Get] = new OpenApiOperation + [HttpMethod.Get] = new OpenApiOperation { OperationId = "GetProducts", Parameters = @@ -110,9 +110,9 @@ public async Task TestAsync_ThrowsException_IfResponseDoesNotMatchSpec( { ["/api/products"] = new OpenApiPathItem { - Operations = new Dictionary + Operations = new Dictionary { - [OperationType.Get] = new OpenApiOperation + [HttpMethod.Get] = new OpenApiOperation { OperationId = "GetProducts", Responses = new OpenApiResponses diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs index 2eed054370..9f78ec43fb 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/JsonValidatorTests.cs @@ -1,9 +1,8 @@ -using Microsoft.OpenApi.Models; +using System.Globalization; +using Microsoft.OpenApi; using Newtonsoft.Json.Linq; using Xunit; -using JsonSchemaType = string; - namespace Swashbuckle.AspNetCore.ApiTesting.Test; public class JsonValidatorTests @@ -77,7 +76,7 @@ public void Validate_ReturnsError_IfNumberGreaterThanMaximum( bool expectedReturnValue, string expectedErrorMessage) { - var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Maximum = schemaMaximum }; + var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Maximum = schemaMaximum.ToString(CultureInfo.InvariantCulture) }; var instance = JToken.Parse(instanceText); var returnValue = Subject().Validate( @@ -102,8 +101,8 @@ public void Validate_ReturnsError_IfNumberGreaterThanOrEqualToMaximumAndExclusiv var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, - Maximum = schemaMaximum, - ExclusiveMaximum = true + Maximum = schemaMaximum.ToString(CultureInfo.InvariantCulture), + ExclusiveMaximum = schemaMaximum.ToString(CultureInfo.InvariantCulture), }; var instance = JToken.Parse(instanceText); @@ -126,7 +125,7 @@ public void Validate_ReturnsError_IfNumberLessThanMinimum( bool expectedReturnValue, string expectedErrorMessage) { - var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Minimum = schemaMinimum }; + var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, Minimum = schemaMinimum.ToString(CultureInfo.InvariantCulture) }; var instance = JToken.Parse(instanceText); var returnValue = Subject().Validate( @@ -151,8 +150,8 @@ public void Validate_ReturnsError_IfNumberLessThanOrEqualToMinimumAndExclusiveMi var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Number, - Minimum = schemaMinimum, - ExclusiveMinimum = true + Minimum = schemaMinimum.ToString(CultureInfo.InvariantCulture), + ExclusiveMinimum = schemaMinimum.ToString(CultureInfo.InvariantCulture), }; var instance = JToken.Parse(instanceText); @@ -451,7 +450,7 @@ public void Validate_ReturnsError_IfKnownPropertyDoesNotMatchPropertySchema( var openApiSchema = new OpenApiSchema { Type = JsonSchemaTypes.Object, - Properties = new Dictionary + Properties = new Dictionary { ["id"] = new OpenApiSchema { Type = propertySchemaType } } @@ -622,15 +621,12 @@ public void Validate_SupportsReferencedSchemas_IfDefinedInProvidedOpenApiDocumen string referenceId, string expectedExceptionMessage) { - var openApiSchema = new OpenApiSchema - { - Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = referenceId } - }; + var openApiSchema = new OpenApiSchemaReference(referenceId); var openApiDocument = new OpenApiDocument { Components = new OpenApiComponents { - Schemas = new Dictionary + Schemas = new Dictionary { ["ref"] = new OpenApiSchema { Type = JsonSchemaTypes.Number } } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs index dc0bc1d1ae..ccc498e114 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/RequestValidatorTests.cs @@ -1,9 +1,7 @@ using System.Text; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Xunit; -using JsonSchemaType = string; - namespace Swashbuckle.AspNetCore.ApiTesting.Test; public class RequestValidatorTests @@ -16,7 +14,7 @@ public void Validate_ThrowsException_IfUriDoesNotMatchPathTemplate( string pathTemplate, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation(pathTemplate, OperationType.Get, new OpenApiOperation()); + var openApiDocument = DocumentWithOperation(pathTemplate, HttpMethod.Get, new OpenApiOperation()); var request = new HttpRequestMessage { RequestUri = new Uri(uriString, UriKind.Relative), @@ -24,21 +22,21 @@ public void Validate_ThrowsException_IfUriDoesNotMatchPathTemplate( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, pathTemplate, OperationType.Get); + Subject().Validate(request, openApiDocument, pathTemplate, HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); } [Theory] - [InlineData("POST", OperationType.Get, "Request method 'POST' does not match specified operation type 'Get'")] - [InlineData("GET", OperationType.Get, null)] + [InlineData("POST", "GET", "Request method 'POST' does not match specified operation type 'GET'")] + [InlineData("GET", "GET", null)] public void Validate_ThrowsException_IfMethodDoesNotMatchOperationType( string methodString, - OperationType operationType, + string operationType, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", operationType, new OpenApiOperation()); + var openApiDocument = DocumentWithOperation("/api/products", new(operationType), new OpenApiOperation()); var request = new HttpRequestMessage { RequestUri = new Uri("/api/products", UriKind.Relative), @@ -47,7 +45,7 @@ public void Validate_ThrowsException_IfMethodDoesNotMatchOperationType( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", operationType); + Subject().Validate(request, openApiDocument, "/api/products", new(operationType)); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -60,7 +58,7 @@ public void Validate_ThrowsException_IfRequiredQueryParameterIsNotPresent( string uriString, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Get, new OpenApiOperation { Parameters = [ @@ -81,7 +79,7 @@ public void Validate_ThrowsException_IfRequiredQueryParameterIsNotPresent( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -94,7 +92,7 @@ public void Validate_ThrowsException_IfRequiredHeaderParameterIsNotPresent( string parameterValue, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Get, new OpenApiOperation { Parameters = [ @@ -116,7 +114,7 @@ public void Validate_ThrowsException_IfRequiredHeaderParameterIsNotPresent( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -138,7 +136,7 @@ public void Validate_ThrowsException_IfPathParameterIsNotOfSpecifiedType( JsonSchemaType specifiedType, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products/{param}", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products/{param}", HttpMethod.Get, new OpenApiOperation { Parameters = [ @@ -158,7 +156,7 @@ public void Validate_ThrowsException_IfPathParameterIsNotOfSpecifiedType( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products/{param}", OperationType.Get); + Subject().Validate(request, openApiDocument, "/api/products/{param}", HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -173,7 +171,7 @@ public void Validate_ThrowsException_IfPathParameterIsNotOfSpecifiedType( { "/api/products?param=1", JsonSchemaTypes.Number, null, null }, { "/api/products?param=foo", JsonSchemaTypes.String, null, null }, { "/api/products?param=1¶m=2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "/api/products?param=1¶m=foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'param' is not of type 'array[number]'" }, + { "/api/products?param=1¶m=foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'param' is not of type 'array[Number]'" }, }; [Theory] @@ -184,7 +182,7 @@ public void Validate_ThrowsException_IfQueryParameterIsNotOfSpecifiedType( JsonSchemaType? specifiedItemsType, string? expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Get, new OpenApiOperation { Parameters = [ @@ -208,7 +206,7 @@ public void Validate_ThrowsException_IfQueryParameterIsNotOfSpecifiedType( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -222,7 +220,7 @@ public void Validate_ThrowsException_IfQueryParameterIsNotOfSpecifiedType( { "1", JsonSchemaTypes.Number, null, null }, { "foo", JsonSchemaTypes.String, null, null }, { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'test-header' is not of type 'array[number]'" }, + { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Parameter 'test-header' is not of type 'array[Number]'" }, }; [Theory] @@ -233,7 +231,7 @@ public void Validate_ThrowsException_IfHeaderParameterIsNotOfSpecifiedType( JsonSchemaType? specifiedItemsType, string? expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Get, new OpenApiOperation { Parameters = [ @@ -258,7 +256,7 @@ public void Validate_ThrowsException_IfHeaderParameterIsNotOfSpecifiedType( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Get); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Get); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -272,7 +270,7 @@ public void Validate_ThrowsException_IfRequiredContentIsNotPresent( string contentString, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Post, new OpenApiOperation { RequestBody = new OpenApiRequestBody { @@ -292,7 +290,7 @@ public void Validate_ThrowsException_IfRequiredContentIsNotPresent( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Post); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -305,7 +303,7 @@ public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( string mediaType, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Post, new OpenApiOperation { RequestBody = new OpenApiRequestBody { @@ -324,7 +322,7 @@ public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( var exception = Record.Exception(() => { - Subject().Validate(request, openApiDocument, "/api/products", OperationType.Post); + Subject().Validate(request, openApiDocument, "/api/products", HttpMethod.Post); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -337,7 +335,7 @@ public void Validate_DelegatesContentValidationToInjectedContentValidators( string jsonString, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Post, new OpenApiOperation { RequestBody = new OpenApiRequestBody { @@ -363,13 +361,13 @@ public void Validate_DelegatesContentValidationToInjectedContentValidators( var exception = Record.Exception(() => { - Subject([new JsonContentValidator()]).Validate(request, openApiDocument, "/api/products", OperationType.Post); + Subject([new JsonContentValidator()]).Validate(request, openApiDocument, "/api/products", HttpMethod.Post); }); Assert.Equal(expectedErrorMessage, exception?.Message); } - private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + private static OpenApiDocument DocumentWithOperation(string pathTemplate, HttpMethod operationType, OpenApiOperation operationSpec) { return new OpenApiDocument { @@ -377,7 +375,7 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat { [pathTemplate] = new OpenApiPathItem { - Operations = new Dictionary + Operations = new Dictionary { [operationType] = operationSpec } @@ -385,7 +383,7 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat }, Components = new OpenApiComponents { - Schemas = new Dictionary() + Schemas = new Dictionary(), } }; } diff --git a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs index bb5eafe01e..1c4c31db8e 100644 --- a/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs +++ b/test/Swashbuckle.AspNetCore.ApiTesting.Test/ResponseValidatorTests.cs @@ -1,10 +1,8 @@ using System.Net; using System.Text; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Xunit; -using JsonSchemaType = string; - namespace Swashbuckle.AspNetCore.ApiTesting.Test; public class ResponseValidatorTests @@ -16,7 +14,7 @@ public void Validate_ThrowsException_IfStatusCodeDifferentToExpectedResponseCode HttpStatusCode statusCode, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Get, new OpenApiOperation { Responses = new OpenApiResponses { @@ -31,7 +29,7 @@ public void Validate_ThrowsException_IfStatusCodeDifferentToExpectedResponseCode var exception = Record.Exception(() => { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Get, "200"); + Subject().Validate(response, openApiDocument, "/api/products", HttpMethod.Get, "200"); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -44,13 +42,13 @@ public void Validate_ThrowsException_IfRequiredHeaderIsNotPresent( string headerValue, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Post, new OpenApiOperation { Responses = new OpenApiResponses { ["201"] = new OpenApiResponse { - Headers = new Dictionary + Headers = new Dictionary { ["test-header"] = new OpenApiHeader { @@ -68,7 +66,7 @@ public void Validate_ThrowsException_IfRequiredHeaderIsNotPresent( var exception = Record.Exception(() => { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); + Subject().Validate(response, openApiDocument, "/api/products", HttpMethod.Post, "201"); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -83,7 +81,7 @@ public void Validate_ThrowsException_IfRequiredHeaderIsNotPresent( { "1", JsonSchemaTypes.Number, null, null }, { "foo", JsonSchemaTypes.String, null, null }, { "1,2", JsonSchemaTypes.Array, JsonSchemaTypes.Number, null }, - { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Header 'test-header' is not of type 'array[number]'" }, + { "1,foo", JsonSchemaTypes.Array, JsonSchemaTypes.Number, "Header 'test-header' is not of type 'array[Number]'" }, }; [Theory] @@ -94,13 +92,13 @@ public void Validate_ThrowsException_IfHeaderIsNotOfSpecifiedType( JsonSchemaType? specifiedItemsType, string? expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products", OperationType.Post, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products", HttpMethod.Post, new OpenApiOperation { Responses = new OpenApiResponses { ["201"] = new OpenApiResponse { - Headers = new Dictionary + Headers = new Dictionary { ["test-header"] = new OpenApiHeader { @@ -122,7 +120,7 @@ public void Validate_ThrowsException_IfHeaderIsNotOfSpecifiedType( var exception = Record.Exception(() => { - Subject().Validate(response, openApiDocument, "/api/products", OperationType.Post, "201"); + Subject().Validate(response, openApiDocument, "/api/products", HttpMethod.Post, "201"); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -136,7 +134,7 @@ public void Validate_ThrowsException_IfExpectedContentIsNotPresent( string contentString, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products/1", HttpMethod.Get, new OpenApiOperation { Responses = new OpenApiResponses { @@ -156,7 +154,7 @@ public void Validate_ThrowsException_IfExpectedContentIsNotPresent( var exception = Record.Exception(() => { - Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + Subject().Validate(response, openApiDocument, "/api/products/1", HttpMethod.Get, "200"); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -169,7 +167,7 @@ public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( string mediaType, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products/1", HttpMethod.Get, new OpenApiOperation { Responses = new OpenApiResponses { @@ -189,7 +187,7 @@ public void Validate_ThrowsException_IfContentMediaTypeIsNotSpecified( var exception = Record.Exception(() => { - Subject().Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + Subject().Validate(response, openApiDocument, "/api/products/1", HttpMethod.Get, "200"); }); Assert.Equal(expectedErrorMessage, exception?.Message); @@ -202,7 +200,7 @@ public void Validate_DelegatesContentValidationToInjectedContentValidators( string jsonString, string expectedErrorMessage) { - var openApiDocument = DocumentWithOperation("/api/products/1", OperationType.Get, new OpenApiOperation + var openApiDocument = DocumentWithOperation("/api/products/1", HttpMethod.Get, new OpenApiOperation { Responses = new OpenApiResponses { @@ -229,14 +227,14 @@ public void Validate_DelegatesContentValidationToInjectedContentValidators( var exception = Record.Exception(() => { - Subject([new JsonContentValidator()]).Validate(response, openApiDocument, "/api/products/1", OperationType.Get, "200"); + Subject([new JsonContentValidator()]).Validate(response, openApiDocument, "/api/products/1", HttpMethod.Get, "200"); }); Assert.Equal(expectedErrorMessage, exception?.Message); } - private static OpenApiDocument DocumentWithOperation(string pathTemplate, OperationType operationType, OpenApiOperation operationSpec) + private static OpenApiDocument DocumentWithOperation(string pathTemplate, HttpMethod operationType, OpenApiOperation operationSpec) { return new OpenApiDocument { @@ -244,7 +242,7 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat { [pathTemplate] = new OpenApiPathItem { - Operations = new Dictionary + Operations = new Dictionary { [operationType] = operationSpec } @@ -252,7 +250,7 @@ private static OpenApiDocument DocumentWithOperation(string pathTemplate, Operat }, Components = new OpenApiComponents { - Schemas = new Dictionary() + Schemas = new Dictionary(), } }; } diff --git a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs index a57cb33235..facefaddc3 100644 --- a/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs +++ b/test/Swashbuckle.AspNetCore.Cli.Test/ToolTests.cs @@ -89,6 +89,28 @@ public static void Can_Generate_Swagger_Json_v3() Assert.True(productsPath.TryGetProperty("post", out _)); } + [Fact] + public static void Can_Generate_Swagger_Json_v3_1() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "3.1", + Path.Combine(Directory.GetCurrentDirectory(), "Basic.dll"), + "v1" + ]); + + Assert.StartsWith("3.1.", document.RootElement.GetProperty("openapi").GetString()); + + // verify one of the endpoints + var paths = document.RootElement.GetProperty("paths"); + var productsPath = paths.GetProperty("/products"); + Assert.True(productsPath.TryGetProperty("post", out _)); + } + [Fact] public static void Can_Generate_Swagger_Json_v3_OpenApiVersion() { @@ -173,6 +195,26 @@ public static void CustomDocumentSerializer_Writes_Custom_V3_Document() Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); } + [Fact] + public static void CustomDocumentSerializer_Writes_Custom_V3_1_Document() + { + using var document = RunToJsonCommand((outputPath) => + [ + "tofile", + "--output", + outputPath, + "--openapiversion", + "3.1", + Path.Combine(Directory.GetCurrentDirectory(), + "CustomDocumentSerializer.dll"), + "v1" + ]); + + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.1", swaggerInfo); + } + [Fact] public static void Can_Generate_Swagger_Json_ForTopLevelApp() { diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs index cdf8f9c6b4..e6d674972b 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/CustomDocumentSerializerTests.cs @@ -10,6 +10,23 @@ namespace Swashbuckle.AspNetCore.IntegrationTests; [Collection("TestSite")] public class CustomDocumentSerializerTests(ITestOutputHelper outputHelper) { + [Fact] + public async Task TestSite_Writes_Custom_V3_1_Document() + { + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup), outputHelper); + var client = testSite.BuildClient(); + + var swaggerResponse = await client.GetAsync($"/swagger/v1/swaggerv3_1.json", TestContext.Current.CancellationToken); + + swaggerResponse.EnsureSuccessStatusCode(); + var contentStream = await swaggerResponse.Content.ReadAsStreamAsync(TestContext.Current.CancellationToken); + using var document = JsonDocument.Parse(contentStream); + + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.1", swaggerInfo); + } + [Fact] public async Task TestSite_Writes_Custom_V3_Document() { @@ -69,6 +86,34 @@ public async Task DocumentProvider_Writes_Custom_V3_Document() Assert.Equal("DocumentSerializerTest3.0", swaggerInfo); } + [Fact] + public async Task DocumentProvider_Writes_Custom_V3_1_Document() + { + var testSite = new TestSite(typeof(CustomDocumentSerializer.Startup), outputHelper); + var server = testSite.BuildServer(); + var services = server.Services; + + var documentProvider = services.GetService(); + var options = services.GetService>(); + options.Value.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1; + + using var stream = new MemoryStream(); + + using (var writer = new StreamWriter(stream, Encoding.UTF8, bufferSize: 2048, leaveOpen: true)) + { + await documentProvider.GenerateAsync("v1", writer); + await writer.FlushAsync(TestContext.Current.CancellationToken); + } + + stream.Position = 0L; + + using var document = JsonDocument.Parse(stream); + + // verify that the custom serializer wrote the swagger info + var swaggerInfo = document.RootElement.GetProperty("swagger").GetString(); + Assert.Equal("DocumentSerializerTest3.1", swaggerInfo); + } + [Fact] public async Task DocumentProvider_Writes_Custom_V2_Document() { diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/HttpServerFixture.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/HttpServerFixture.cs new file mode 100644 index 0000000000..a752427cd4 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/HttpServerFixture.cs @@ -0,0 +1,20 @@ +#if NET10_0_OR_GREATER +using Microsoft.AspNetCore.Mvc.Testing; + +namespace Swashbuckle.AspNetCore.IntegrationTests; + +public class HttpApplicationFixture : WebApplicationFactory + where TEntryPoint : class +{ + public HttpApplicationFixture() => UseKestrel(0); + + public string ServerUrl + { + get + { + StartServer(); + return ClientOptions.BaseAddress.ToString(); + } + } +} +#endif diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/OpenApiDocumentLoader.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/OpenApiDocumentLoader.cs index 07ec16d375..cdcd53554a 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/OpenApiDocumentLoader.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/OpenApiDocumentLoader.cs @@ -1,5 +1,5 @@ -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; +using Microsoft.OpenApi; +using Microsoft.OpenApi.Reader; namespace Swashbuckle.AspNetCore; @@ -7,15 +7,23 @@ internal static class OpenApiDocumentLoader { public static async Task LoadAsync(Stream stream) { - var reader = new OpenApiStreamReader(); - var document = reader.Read(stream, out OpenApiDiagnostic diagnostic); - return await Task.FromResult(document); + var result = await OpenApiDocument.LoadAsync(stream); + + Assert.NotNull(result); + Assert.NotNull(result.Document); + Assert.NotNull(result.Diagnostic); + Assert.Empty(result.Diagnostic.Errors); + Assert.Empty(result.Diagnostic.Warnings); + + return result.Document; } public static async Task<(OpenApiDocument Document, OpenApiDiagnostic Diagnostic)> LoadWithDiagnosticsAsync(Stream stream) { - var reader = new OpenApiStreamReader(); - var document = reader.Read(stream, out OpenApiDiagnostic diagnostic); - return await Task.FromResult((document, diagnostic)); + var settings = new OpenApiReaderSettings(); + settings.AddYamlReader(); + + var result = await OpenApiDocument.LoadAsync(stream, settings: settings); + return (result.Document, result.Diagnostic); } } diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/PlaywrightFixture.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/PlaywrightFixture.cs new file mode 100644 index 0000000000..f0e4764fb5 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/PlaywrightFixture.cs @@ -0,0 +1,65 @@ +#if NET10_0_OR_GREATER +using Microsoft.Playwright; + +namespace Swashbuckle.AspNetCore.IntegrationTests; + +public sealed class PlaywrightFixture : IAsyncLifetime +{ + private bool _installed; + + public ValueTask InitializeAsync() + { + EnsureInstalled(); + return ValueTask.CompletedTask; + } + + public ValueTask DisposeAsync() + { + GC.SuppressFinalize(this); + return ValueTask.CompletedTask; + } + + public async Task VerifyPage(string url, Func test) + { + EnsureInstalled(); + + using var playwright = await Playwright.CreateAsync(); + var browserType = playwright[BrowserType.Chromium]; + + var options = new BrowserTypeLaunchOptions(); + + if (System.Diagnostics.Debugger.IsAttached) + { +#pragma warning disable CS0612 + options.Devtools = true; +#pragma warning restore CS0612 + options.Headless = false; + options.SlowMo = 100; + } + + await using var browser = await playwright[BrowserType.Chromium].LaunchAsync(options); + await using var context = await browser.NewContextAsync(); + + var page = await context.NewPageAsync(); + + await page.GotoAsync(url, new() { WaitUntil = WaitUntilState.NetworkIdle }); + + await test(page); + } + + private void EnsureInstalled() + { + if (!_installed) + { + int result = Microsoft.Playwright.Program.Main(["install", "chromium", "--only-shell", "--with-deps"]); + + if (result != 0) + { + throw new InvalidOperationException($"Failed to install Playwright dependencies: {result}."); + } + + _installed = true; + } + } +} +#endif diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs index 55ee5c5f94..c798078c97 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/SwaggerIntegrationTests.cs @@ -2,7 +2,6 @@ using System.Reflection; using System.Text; using System.Text.Json; -using Microsoft.OpenApi.Any; using ReDocApp = ReDoc; namespace Swashbuckle.AspNetCore.IntegrationTests; @@ -85,8 +84,7 @@ public async Task SwaggerEndpoint_ReturnsCorrectPriceExample_ForDifferentCulture { var openApiDocument = await OpenApiDocumentLoader.LoadAsync(contentStream); var example = openApiDocument.Components.Schemas["Product"].Example; - var exampleObject = Assert.IsType(example); - double price = Assert.IsType(exampleObject["price"]).Value; + double price = example["price"].GetValue(); Assert.Equal(14.37, price); } finally @@ -98,6 +96,7 @@ public async Task SwaggerEndpoint_ReturnsCorrectPriceExample_ForDifferentCulture [Theory] [InlineData("/swagger/v1/swagger.json", "openapi", "3.0.4")] [InlineData("/swagger/v1/swaggerv2.json", "swagger", "2.0")] + [InlineData("/swagger/v1/swaggerv3_1.json", "openapi", "3.1.1")] public async Task SwaggerMiddleware_CanBeConfiguredMultipleTimes( string swaggerUrl, string expectedVersionProperty, diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj index 3d3a84a83e..20f124b347 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/Swashbuckle.AspNetCore.IntegrationTests.csproj @@ -18,7 +18,8 @@ - + + @@ -30,13 +31,18 @@ - - + + - - + + + + + + + diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/UITests.cs b/test/Swashbuckle.AspNetCore.IntegrationTests/UITests.cs new file mode 100644 index 0000000000..fc5293784b --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/UITests.cs @@ -0,0 +1,48 @@ +#if NET10_0_OR_GREATER +using ReDocApp = ReDoc; + +namespace Swashbuckle.AspNetCore.IntegrationTests; + +public class UITests(PlaywrightFixture fixture) : IClassFixture +{ + [Fact] + public async Task Can_Load_SwaggerUI_Page_And_Make_Request() + { + // Arrange + await using var application = new SwaggerUIFixture(); + + // Act and Assert + await fixture.VerifyPage(application.ServerUrl, async (page) => + { + var operation = await page.WaitForSelectorAsync("text=Searches the collection of products by description key words"); + await operation.ClickAsync(); + + var button = await page.WaitForSelectorAsync("text=Try it out"); + await button.ClickAsync(); + + button = await page.WaitForSelectorAsync("text=Execute"); + await button.ClickAsync(); + + var response = await page.WaitForSelectorAsync(".live-responses-table"); + await response.WaitForSelectorAsync("text=200"); + }); + } + + [Fact] + public async Task Can_Load_Redoc_Page() + { + // Arrange + await using var application = new RedocFixture(); + + // Act and Assert + await fixture.VerifyPage(application.ServerUrl, async (page) => + { + await page.QuerySelectorAsync("text=/products"); + }); + } + + private sealed class RedocFixture : HttpApplicationFixture; + + private sealed class SwaggerUIFixture : HttpApplicationFixture; +} +#endif diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet10_0.verified.txt new file mode 100644 index 0000000000..f0b5be055c --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet10_0.verified.txt @@ -0,0 +1,59 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CliExampleWithFactory", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:57556/" + } + ], + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet8_0.verified.txt index 13e135e2cf..f0b5be055c 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet8_0.verified.txt @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet9_0.verified.txt index 13e135e2cf..f0b5be055c 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_ForAutofaq.DotNet9_0.verified.txt @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..97a6f4f1bb --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,1718 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API V1", + "description": "A sample API for testing Swashbuckle", + "termsOfService": "http://tempuri.org/terms", + "version": "v1" + }, + "paths": { + "/products": { + "post": { + "tags": [ + "CrudActions" + ], + "summary": "Creates a product", + "description": "## Heading 1\r\n\r\n POST /products\r\n {\r\n \"id\": \"123\",\r\n \"description\": \"Some product\"\r\n }", + "operationId": "CreateProduct", + "requestBody": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + }, + "x-purpose": "test" + }, + "get": { + "tags": [ + "CrudActions" + ], + "summary": "Searches the collection of products by description key words", + "operationId": "SearchProducts", + "parameters": [ + { + "name": "kw", + "in": "query", + "description": "A list of search terms", + "schema": { + "type": "string", + "default": "foobar" + }, + "example": "hello" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/products/all": { + "get": { + "tags": [ + "CrudActions" + ], + "summary": "Get all products", + "description": "```\r\n{\r\n \"Id\":1,\r\n \"Description\":\"\",\r\n \"Status\": 0,\r\n \"Status2\": 1\r\n}\r\n \r\n```", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/products/{id}": { + "get": { + "tags": [ + "CrudActions" + ], + "summary": "Returns a specific product", + "operationId": "GetProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The product id", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 111 + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + }, + "x-purpose": "test" + }, + "put": { + "tags": [ + "CrudActions" + ], + "summary": "Updates all properties of a specific product", + "operationId": "UpdateProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 222 + } + ], + "requestBody": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + }, + "patch": { + "tags": [ + "CrudActions" + ], + "summary": "Updates some properties of a specific product", + "description": "\r\nOnly provided properties will be updated,\r\n other remain unchanged.\r\n\r\nIdentifier must be non-default value\r\n\r\nBody must be specified", + "operationId": "PatchProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 333 + } + ], + "requestBody": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { } + } + }, + "text/json": { + "schema": { + "type": "object", + "additionalProperties": { } + } + }, + "application/*+json": { + "schema": { + "type": "object", + "additionalProperties": { } + } + } + }, + "required": true, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + }, + "delete": { + "tags": [ + "CrudActions" + ], + "summary": "Deletes a specific product", + "operationId": "DeleteProduct", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 444 + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/payments/authorize": { + "post": { + "tags": [ + "DataAnnotations" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentRequest" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/PaymentRequest" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/PaymentRequest" + } + } + }, + "required": true, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/payments/{paymentId}/cancel": { + "put": { + "tags": [ + "DataAnnotations" + ], + "parameters": [ + { + "name": "paymentId", + "in": "path", + "required": true, + "schema": { + "minLength": 6, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/kittens": { + "post": { + "tags": [ + "DynamicTypes" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { } + }, + "text/json": { + "schema": { } + }, + "application/*+json": { + "schema": { } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/unicorns": { + "get": { + "tags": [ + "DynamicTypes" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { } + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/dragons": { + "post": { + "tags": [ + "DynamicTypes" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { } + }, + "text/json": { + "schema": { } + }, + "application/*+json": { + "schema": { } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/files/single": { + "post": { + "tags": [ + "Files" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "file": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/files/multiple": { + "post": { + "tags": [ + "Files" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "files": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + } + }, + "encoding": { + "files": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/files/form-with-file": { + "post": { + "tags": [ + "Files" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "file": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "name": { + "style": "form" + }, + "file": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/files/{name}": { + "get": { + "tags": [ + "Files" + ], + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "oneOf": [ + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + } + ] + } + }, + "application/zip": { + "schema": { + "oneOf": [ + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + }, + { + "type": "string", + "format": "binary" + } + ] + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/registrations": { + "post": { + "tags": [ + "FromFormParams" + ], + "summary": "Form parameters with description", + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Summary for Name", + "example": "MyName" + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "description": "Summary for PhoneNumbers" + }, + "formFile": { + "type": "string", + "description": "Description for file", + "format": "binary" + }, + "text": { + "type": "string", + "description": "Description for Text" + } + } + }, + "encoding": { + "name": { + "style": "form" + }, + "phoneNumbers": { + "style": "form" + }, + "formFile": { + "style": "form" + }, + "text": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/registrationsWithIgnoreProperties": { + "post": { + "tags": [ + "FromFormParams" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "phoneNumbers": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + } + } + }, + "encoding": { + "phoneNumbers": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/registrationsWithEnumParameter": { + "post": { + "tags": [ + "FromFormParams" + ], + "summary": "Form parameters with description", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Summary for Name", + "example": "MyName" + }, + "phoneNumbers": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + }, + "description": "Summary for PhoneNumbers" + }, + "logLevel": { + "$ref": "#/components/schemas/LogLevel" + }, + "formFile": { + "type": "string", + "description": "Description for file", + "format": "binary" + }, + "dateTimeKind": { + "$ref": "#/components/schemas/DateTimeKind" + } + } + }, + "encoding": { + "name": { + "style": "form" + }, + "phoneNumbers": { + "style": "form" + }, + "logLevel": { + "style": "form" + }, + "formFile": { + "style": "form" + }, + "dateTimeKind": { + "style": "form" + } + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/country/validate": { + "get": { + "tags": [ + "FromHeaderParams" + ], + "parameters": [ + { + "name": "country", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/addresses/validate": { + "get": { + "tags": [ + "FromQueryParams" + ], + "parameters": [ + { + "name": "country", + "in": "query", + "description": "3-letter ISO country code", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "city", + "in": "query", + "description": "Name of city", + "schema": { + "type": "string", + "default": "Seattle" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/zip-codes/validate": { + "get": { + "tags": [ + "FromQueryParams" + ], + "parameters": [ + { + "name": "zipCodes", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "search", + "in": "query", + "required": true, + "style": "deepObject", + "schema": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + }, + "/Issue3013/Get": { + "get": { + "tags": [ + "Issue3013" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/TestResponse" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestResponse" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/TestResponse" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/promotions": { + "get": { + "tags": [ + "JsonAnnotations" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Promotion" + } + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/shapes": { + "post": { + "tags": [ + "PolymorphicTypes" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Rectangle" + }, + { + "$ref": "#/components/schemas/Circle" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Rectangle" + }, + { + "$ref": "#/components/schemas/Circle" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Rectangle" + }, + { + "$ref": "#/components/schemas/Circle" + } + ] + } + } + }, + "x-purpose": "test" + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/orders": { + "post": { + "tags": [ + "ResponseTypeAnnotations" + ], + "summary": "Creates an order", + "requestBody": { + "description": "", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "required": true, + "x-purpose": "test" + }, + "responses": { + "201": { + "description": "Order created", + "content": { + "application/xml": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "Order invalid", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/ValidationProblemDetails" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/carts": { + "post": { + "tags": [ + "SwaggerAnnotations" + ], + "operationId": "CreateCart", + "requestBody": { + "description": "The cart request body", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + } + }, + "x-purpose": "test" + }, + "responses": { + "201": { + "description": "The cart was created", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + } + } + }, + "400": { + "description": "The cart data is invalid" + } + }, + "x-purpose": "test" + } + }, + "/carts/{id}": { + "get": { + "tags": [ + "SwaggerAnnotations" + ], + "externalDocs": { + "description": "External docs for CartsByIdGet", + "url": "https://tempuri.org/carts-by-id-get" + }, + "operationId": "GetCart", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The cart identifier", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + } + } + } + }, + "x-purpose": "test" + }, + "delete": { + "tags": [ + "SwaggerAnnotations" + ], + "summary": "Deletes a specific cart", + "description": "Requires admin privileges", + "operationId": "DeleteCart", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The cart identifier", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Cart" + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/stores": { + "post": { + "tags": [ + "UnboundParams" + ], + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "location", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + }, + "x-purpose": "test" + }, + "get": { + "tags": [ + "UnboundParams" + ], + "parameters": [ + { + "name": "locations", + "in": "query", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Store" + } + } + } + } + } + }, + "x-purpose": "test" + } + }, + "/stores/{id}": { + "get": { + "tags": [ + "UnboundParams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Store" + } + } + } + } + }, + "x-purpose": "test" + }, + "put": { + "tags": [ + "UnboundParams" + ], + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "location", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + }, + "delete": { + "tags": [ + "UnboundParams" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "x-purpose": "test" + } + } + }, + "components": { + "schemas": { + "Cart": { + "required": [ + "Id" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The cart identifier", + "format": "int32", + "readOnly": true + }, + "cartType": { + "$ref": "#/components/schemas/CartType" + } + }, + "additionalProperties": false + }, + "CartType": { + "enum": [ + 0, + 1 + ], + "type": "integer", + "description": "The cart type", + "format": "int32" + }, + "Circle": { + "allOf": [ + { + "$ref": "#/components/schemas/Shape" + }, + { + "type": "object", + "properties": { + "radius": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + } + ] + }, + "CreditCard": { + "required": [ + "cardNumber", + "expMonth", + "expYear" + ], + "type": "object", + "properties": { + "cardNumber": { + "minLength": 1, + "pattern": "^[3-6]?\\d{12,15}$", + "type": "string" + }, + "expMonth": { + "maximum": 12, + "minimum": 1, + "type": "integer", + "format": "int32" + }, + "expYear": { + "maximum": 99, + "minimum": 14, + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "DateTimeKind": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "format": "int32" + }, + "DiscountType": { + "enum": [ + "Percentage", + "Amount" + ], + "type": "string" + }, + "LogLevel": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "type": "integer", + "format": "int32" + }, + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + }, + "total": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "PaymentRequest": { + "required": [ + "creditCard", + "transaction" + ], + "type": "object", + "properties": { + "transaction": { + "$ref": "#/components/schemas/Transaction" + }, + "creditCard": { + "$ref": "#/components/schemas/CreditCard" + } + }, + "additionalProperties": false + }, + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Uniquely identifies the product", + "format": "int32" + }, + "description": { + "type": "string", + "description": "Describes the product", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ProductStatus" + }, + "status2": { + "$ref": "#/components/schemas/ProductStatus" + }, + "price": { + "maximum": 122.9, + "exclusiveMaximum": true, + "minimum": 0.1, + "exclusiveMinimum": true, + "type": "number", + "format": "double", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Represents a product", + "example": { + "id": 123, + "description": "foobar", + "price": 14.37 + } + }, + "ProductStatus": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "format": "int32" + }, + "Promotion": { + "type": "object", + "properties": { + "promo-code": { + "type": "string", + "nullable": true + }, + "discountType": { + "$ref": "#/components/schemas/DiscountType" + } + }, + "additionalProperties": false + }, + "Rectangle": { + "allOf": [ + { + "$ref": "#/components/schemas/Shape" + }, + { + "type": "object", + "properties": { + "height": { + "type": "integer", + "format": "int32" + }, + "width": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + } + ] + }, + "Shape": { + "required": [ + "TypeName" + ], + "type": "object", + "properties": { + "TypeName": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "TypeName", + "mapping": { + "Rectangle": "#/components/schemas/Rectangle", + "Circle": "#/components/schemas/Circle" + } + } + }, + "Store": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "location": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "TestResponse": { + "type": "object", + "properties": { + "foo": { + "$ref": "#/components/schemas/TestStruct" + } + }, + "additionalProperties": false + }, + "TestStruct": { + "type": "object", + "properties": { + "a": { + "type": "integer", + "format": "int32" + }, + "b": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false + }, + "Transaction": { + "required": [ + "amount" + ], + "type": "object", + "properties": { + "amount": { + "type": "number", + "format": "double" + }, + "note": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ValidationProblemDetails": { + "type": "object", + "properties": { + "type": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "status": { + "type": "integer", + "format": "int32", + "nullable": true + }, + "detail": { + "type": "string", + "nullable": true + }, + "instance": { + "type": "string", + "nullable": true + }, + "errors": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "nullable": true + } + }, + "additionalProperties": { } + } + } + }, + "tags": [ + { + "name": "CrudActions" + }, + { + "name": "DataAnnotations" + }, + { + "name": "DynamicTypes" + }, + { + "name": "Files" + }, + { + "name": "FromFormParams" + }, + { + "name": "FromHeaderParams" + }, + { + "name": "FromQueryParams" + }, + { + "name": "Issue3013" + }, + { + "name": "JsonAnnotations" + }, + { + "name": "PolymorphicTypes" + }, + { + "name": "ResponseTypeAnnotations" + }, + { + "name": "SwaggerAnnotations", + "description": "Manipulate Carts to your heart's content", + "externalDocs": { + "url": "http://www.tempuri.org" + } + }, + { + "name": "UnboundParams" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index fdafa1de38..97a6f4f1bb 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -1671,12 +1671,48 @@ } }, "tags": [ + { + "name": "CrudActions" + }, + { + "name": "DataAnnotations" + }, + { + "name": "DynamicTypes" + }, + { + "name": "Files" + }, + { + "name": "FromFormParams" + }, + { + "name": "FromHeaderParams" + }, + { + "name": "FromQueryParams" + }, + { + "name": "Issue3013" + }, + { + "name": "JsonAnnotations" + }, + { + "name": "PolymorphicTypes" + }, + { + "name": "ResponseTypeAnnotations" + }, { "name": "SwaggerAnnotations", "description": "Manipulate Carts to your heart's content", "externalDocs": { "url": "http://www.tempuri.org" } + }, + { + "name": "UnboundParams" } ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index fdafa1de38..97a6f4f1bb 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=Basic.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -1671,12 +1671,48 @@ } }, "tags": [ + { + "name": "CrudActions" + }, + { + "name": "DataAnnotations" + }, + { + "name": "DynamicTypes" + }, + { + "name": "Files" + }, + { + "name": "FromFormParams" + }, + { + "name": "FromHeaderParams" + }, + { + "name": "FromQueryParams" + }, + { + "name": "Issue3013" + }, + { + "name": "JsonAnnotations" + }, + { + "name": "PolymorphicTypes" + }, + { + "name": "ResponseTypeAnnotations" + }, { "name": "SwaggerAnnotations", "description": "Manipulate Carts to your heart's content", "externalDocs": { "url": "http://www.tempuri.org" } + }, + { + "name": "UnboundParams" } ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..84b5ecf08c --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,59 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CliExample", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:51071" + } + ], + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 1d1afacba0..84b5ecf08c 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 1d1afacba0..84b5ecf08c 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CliExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..26f06ef714 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,73 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "ConfigFromFile", + "version": "1.0" + }, + "paths": { + "/api/Products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "foo": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer", + "format": "int32" + } + }, + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 1bc73a344e..26f06ef714 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -64,5 +64,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 1bc73a344e..26f06ef714 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ConfigFromFile.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -64,5 +64,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomDocumentSerializer.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomDocumentSerializer.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..fb35c79785 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomDocumentSerializer.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,3 @@ +{ + "swagger": "DocumentSerializerTest3.0" +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..72beb1d9f1 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,54 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CustomUIConfig", + "version": "1.0" + }, + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index f6c887c54d..72beb1d9f1 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -45,5 +45,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index f6c887c54d..72beb1d9f1 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIConfig.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -45,5 +45,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..db2a6f33fe --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,54 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CustomUIIndex", + "version": "1.0" + }, + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 5ade11f2a2..db2a6f33fe 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -45,5 +45,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 5ade11f2a2..db2a6f33fe 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=CustomUIIndex.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -45,5 +45,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..66da63fb97 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,591 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "1" + }, + "paths": { + "/{tenantId}/orders": { + "post": { + "tags": [ + "Orders" + ], + "summary": "Creates a resource", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Orders" + ], + "summary": "Delete by Ids", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Ids", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + }, + "/{tenantId}/orders/DeleteById": { + "delete": { + "tags": [ + "Orders" + ], + "summary": "Delete by Id", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Id", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + }, + "/{tenantId}/orders/Delete/List": { + "delete": { + "tags": [ + "Orders" + ], + "summary": "Delete by Id List", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Ids", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + }, + "/{tenantId}/products": { + "post": { + "tags": [ + "Products" + ], + "summary": "Creates a resource", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "The resource", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Products" + ], + "summary": "Delete by Ids", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Ids", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + }, + "/{tenantId}/products/DeleteById": { + "delete": { + "tags": [ + "Products" + ], + "summary": "Delete by Id", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Id", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + }, + "/{tenantId}/products/Delete/List": { + "delete": { + "tags": [ + "Products" + ], + "summary": "Delete by Id List", + "parameters": [ + { + "name": "tenantId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "deleting Ids", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/*+json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Deleted", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "404": { + "description": "Failed" + } + } + } + } + }, + "components": { + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "subtotal": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Orders" + }, + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 307f1d98ba..66da63fb97 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -579,5 +579,13 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Orders" + }, + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 307f1d98ba..66da63fb97 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=GenericControllers.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -579,5 +579,13 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Orders" + }, + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet10_0.verified.txt new file mode 100644 index 0000000000..545254baed --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet10_0.verified.txt @@ -0,0 +1,80 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Sample API 1.0", + "version": "1.0" + }, + "paths": { + "/Products": { + "get": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet8_0.verified.txt index 7876caa8e1..545254baed 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet8_0.verified.txt @@ -71,5 +71,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet9_0.verified.txt index 7876caa8e1..545254baed 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=1.0.DotNet9_0.verified.txt @@ -71,5 +71,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet10_0.verified.txt new file mode 100644 index 0000000000..fa7ee19c72 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet10_0.verified.txt @@ -0,0 +1,220 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Sample API 2.0", + "version": "2.0" + }, + "paths": { + "/Products": { + "post": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + }, + "text/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "get": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "api-version", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + }, + "/Products/{id}": { + "put": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "api-version", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "api-version", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet8_0.verified.txt index 2bd3758720..fa7ee19c72 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet8_0.verified.txt @@ -211,5 +211,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet9_0.verified.txt index 2bd3758720..fa7ee19c72 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=MultipleVersions.Startup_swaggerRequestUri=2.0.DotNet9_0.verified.txt @@ -211,5 +211,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..fe357e0e13 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,474 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "NswagClientExample", + "version": "1.0" + }, + "paths": { + "/Animals": { + "post": { + "tags": [ + "Animals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/SecondLevel": { + "post": { + "tags": [ + "SecondLevel" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + }, + "/SystemTextJsonAnimals": { + "post": { + "tags": [ + "SystemTextJsonAnimals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/SystemTextJsonDefaultDiscriminatorAnimals": { + "post": { + "tags": [ + "SystemTextJsonDefaultDiscriminatorAnimals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "required": [ + "animalType" + ], + "type": "object", + "properties": { + "animalType": { + "$ref": "#/components/schemas/AnimalType" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "animalType", + "mapping": { + "Cat": "#/components/schemas/Cat", + "Dog": "#/components/schemas/Dog" + } + } + }, + "AnimalType": { + "enum": [ + "Cat", + "Dog" + ], + "type": "string" + }, + "BaseType": { + "required": [ + "discriminator" + ], + "type": "object", + "properties": { + "discriminator": { + "type": "string" + }, + "property": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "discriminator", + "mapping": { + "SubSubType": "#/components/schemas/SubSubType" + } + } + }, + "Cat": { + "allOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "Dog": { + "allOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SubSubType": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseType" + }, + { + "type": "object", + "properties": { + "property2": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonAnimal": { + "required": [ + "animalType" + ], + "type": "object", + "properties": { + "animalType": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "animalType", + "mapping": { + "Cat": "#/components/schemas/SystemTextJsonCat", + "Dog": "#/components/schemas/SystemTextJsonDog" + } + } + }, + "SystemTextJsonCat": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDefaultDiscriminatorAnimal": { + "required": [ + "$type" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "animalType": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "Cat": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat", + "Dog": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + } + }, + "SystemTextJsonDefaultDiscriminatorCat": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDefaultDiscriminatorDog": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDog": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + } + } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 022f86beda..fe357e0e13 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -456,5 +456,19 @@ ] } } - } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 022f86beda..fe357e0e13 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=NSwagClientExample.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -456,5 +456,19 @@ ] } } - } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..fcb1f1f2bf --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,252 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API V1", + "version": "v1" + }, + "servers": [ + { + "url": "/resource-server" + } + ], + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "oauth2": [ + "readAccess" + ] + } + ] + }, + "post": { + "tags": [ + "Products" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "oauth2": [ + "writeAccess" + ] + } + ] + } + }, + "/products/{id}": { + "get": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "oauth2": [ + "readAccess" + ] + } + ] + }, + "delete": { + "tags": [ + "Products" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + }, + "403": { + "description": "Forbidden" + } + }, + "security": [ + { + "oauth2": [ + "writeAccess" + ] + } + ] + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "serialNo": { + "type": "string", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/ProductStatus" + } + }, + "additionalProperties": false + }, + "ProductStatus": { + "enum": [ + 0, + 1 + ], + "type": "integer", + "format": "int32" + } + }, + "securitySchemes": { + "oauth2": { + "type": "oauth2", + "flows": { + "authorizationCode": { + "authorizationUrl": "/auth-server/connect/authorize", + "tokenUrl": "/auth-server/connect/token", + "scopes": { + "readAccess": "Access read operations", + "writeAccess": "Access write operations" + } + } + } + } + } + }, + "security": [ + { + "oauth2": [ + "readAccess", + "writeAccess" + ] + } + ], + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 433d6bf6ff..fcb1f1f2bf 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -243,5 +243,10 @@ "writeAccess" ] } + ], + "tags": [ + { + "name": "Products" + } ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 433d6bf6ff..fcb1f1f2bf 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=OAuth2Integration.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -243,5 +243,10 @@ "writeAccess" ] } + ], + "tags": [ + { + "name": "Products" + } ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..4a46d91284 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,194 @@ +{ + "swagger": "2.0", + "info": { + "title": "ReDoc", + "version": "1.0" + }, + "paths": { + "/products": { + "post": { + "tags": [ + "Products" + ], + "consumes": [ + "application/json", + "text/json", + "application/*+json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/Product" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "get": { + "tags": [ + "Products" + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Product" + } + } + } + } + } + }, + "/products/{id}": { + "get": { + "tags": [ + "Products" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/Product" + } + } + } + }, + "put": { + "tags": [ + "Products" + ], + "consumes": [ + "application/json", + "text/json", + "application/*+json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "type": "integer", + "format": "int32" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "$ref": "#/definitions/Product" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "patch": { + "tags": [ + "Products" + ], + "consumes": [ + "application/json", + "text/json", + "application/*+json" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "type": "integer", + "format": "int32" + }, + { + "in": "body", + "name": "body", + "required": true, + "schema": { + "type": "object", + "additionalProperties": { } + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "delete": { + "tags": [ + "Products" + ], + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "type": "integer", + "format": "int32" + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "definitions": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "x-nullable": true + } + }, + "additionalProperties": false + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt index 27a5d6cfb0..4a46d91284 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -32,8 +32,8 @@ "200": { "description": "OK", "schema": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" } } } @@ -175,14 +175,20 @@ "type": "object", "properties": { "id": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "description": { - "type": "string" + "type": "string", + "x-nullable": true } }, "additionalProperties": false } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt index 27a5d6cfb0..4a46d91284 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=ReDoc.Startup_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -32,8 +32,8 @@ "200": { "description": "OK", "schema": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" } } } @@ -175,14 +175,20 @@ "type": "object", "properties": { "id": { - "format": "int32", - "type": "integer" + "type": "integer", + "format": "int32" }, "description": { - "type": "string" + "type": "string", + "x-nullable": true } }, "additionalProperties": false } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=TestFirst.Startup_swaggerRequestUri=v1-generated.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=TestFirst.Startup_swaggerRequestUri=v1-generated.DotNet10_0.verified.txt new file mode 100644 index 0000000000..5dc02708a7 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.SwaggerEndpoint_ReturnsValidSwaggerJson_startupType=TestFirst.Startup_swaggerRequestUri=v1-generated.DotNet10_0.verified.txt @@ -0,0 +1,52 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test-first Example API (Generated)", + "version": "v1" + }, + "paths": { + "/api/users": { + "post": { + "operationId": "CreateUser", + "requestBody": { + "content": { + "application/json": { + "schema": { + "required": [ + "email", + "password" + ], + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "User created", + "headers": { + "Location": { + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid request" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..14e9ac594f --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,79 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "MinimalApp", + "version": "v1" + }, + "paths": { + "/WeatherForecast": { + "get": { + "tags": [ + "WeatherForecast" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "WeatherForecast": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "temperatureC": { + "type": "integer", + "format": "int32" + }, + "temperatureF": { + "type": "integer", + "format": "int32", + "readOnly": true + }, + "summary": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "WeatherForecast" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt index 7c2cec82e4..14e9ac594f 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -70,5 +70,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "WeatherForecast" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt index 7c2cec82e4..14e9ac594f 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MinimalApp.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -70,5 +70,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "WeatherForecast" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..c02949b557 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,87 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "MvcWithNullable", + "version": "1.0" + }, + "paths": { + "/api/Enum": { + "get": { + "tags": [ + "Enum" + ], + "parameters": [ + { + "name": "logLevel", + "in": "query", + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/LogLevel" + } + ], + "default": 4 + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/RequiredEnum": { + "get": { + "tags": [ + "RequiredEnum" + ], + "parameters": [ + { + "name": "logLevel", + "in": "query", + "required": true, + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/LogLevel" + } + ], + "default": 4 + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "LogLevel": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6 + ], + "type": "integer", + "format": "int32" + } + } + }, + "tags": [ + { + "name": "Enum" + }, + { + "name": "RequiredEnum" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt index ac23e23151..c02949b557 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -75,5 +75,13 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Enum" + }, + { + "name": "RequiredEnum" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt index ac23e23151..c02949b557 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=MvcWithNullable.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -75,5 +75,13 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Enum" + }, + { + "name": "RequiredEnum" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=TopLevelSwaggerDoc.Program_swaggerRequestUri=.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=TopLevelSwaggerDoc.Program_swaggerRequestUri=.DotNet10_0.verified.txt new file mode 100644 index 0000000000..7f373c459d --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=TopLevelSwaggerDoc.Program_swaggerRequestUri=.DotNet10_0.verified.txt @@ -0,0 +1,9 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "1" + }, + "paths": { }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..48d906e2bc --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,87 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Native AoT API V1", + "description": "A sample API for testing Swashbuckle with native AoT", + "termsOfService": "http://tempuri.org/terms", + "version": "v1" + }, + "paths": { + "/todos": { + "get": { + "tags": [ + "WebApi.Aot" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Todo" + } + } + } + } + } + } + } + }, + "/todos/{id}": { + "get": { + "tags": [ + "WebApi.Aot" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "Todo": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "title": { + "type": "string", + "nullable": true + }, + "dueBy": { + "type": "string", + "format": "date", + "nullable": true + }, + "isComplete": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "WebApi.Aot" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt index 579ea114e7..48d906e2bc 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -78,5 +78,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "WebApi.Aot" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt index 579ea114e7..48d906e2bc 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Aot.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -78,5 +78,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "WebApi.Aot" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt new file mode 100644 index 0000000000..048170ae70 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet10_0.verified.txt @@ -0,0 +1,1220 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "WebApi", + "version": "v1" + }, + "paths": { + "/annotations/fruit/{id}": { + "post": { + "tags": [ + "Annotations" + ], + "summary": "CreateFruit", + "description": "Create a fruit", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The id of the fruit that will be created", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Description for Body", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Fruit" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Description for response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Fruit" + } + } + } + } + } + } + }, + "/annotations/singleForm": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PersonAnnotated" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PersonAnnotated" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/multipleForms": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "$ref": "#/components/schemas/AddressAnnotated" + } + ] + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "$ref": "#/components/schemas/AddressAnnotated" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromFileAndString": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file", + "tags" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "Description for File", + "format": "binary" + }, + "tags": { + "type": "string" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromFileAndEnum": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "dateTimeKind", + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "dateTimeKind": { + "$ref": "#/components/schemas/DateTimeKind" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "dateTimeKind": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromObjectAndString": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/AsParameters": { + "get": { + "tags": [ + "Annotations" + ], + "parameters": [ + { + "name": "paramOne", + "in": "query", + "description": "Description", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "paramTwo", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "paramThree", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "paramFour", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "paramFive", + "in": "query", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "paramSix", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "paramSeven", + "in": "query", + "schema": { + "type": "string", + "format": "time" + } + }, + { + "name": "paramEight", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "time" + } + }, + { + "name": "paramNine", + "in": "query", + "schema": { + "$ref": "#/components/schemas/DateTimeKind" + } + }, + { + "name": "paramTen", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/DateTimeKind" + } + }, + { + "name": "paramEleven", + "in": "query", + "schema": { + "type": "number", + "format": "double" + } + }, + { + "name": "paramTwelve", + "in": "query", + "required": true, + "schema": { + "type": "number", + "format": "double" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsParametersRecord" + } + } + } + } + } + } + }, + "/TypeWithTryParse/{tryParse}": { + "get": { + "tags": [ + "WebApi" + ], + "parameters": [ + { + "name": "tryParse", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/weatherforecast": { + "get": { + "tags": [ + "WithOpenApi" + ], + "operationId": "GetWeatherForecast", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + } + } + } + } + } + }, + "/WithOpenApi/multipleForms": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "$ref": "#/components/schemas/Address" + } + ] + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "$ref": "#/components/schemas/Address" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFile": { + "post": { + "tags": [ + "WithOpenApi" + ], + "parameters": [ + { + "name": "queryParameter", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "file": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileCollection": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "collection" + ], + "type": "object", + "properties": { + "collection": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + } + }, + "encoding": { + "collection": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromBody": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationCustomExchangeRatesDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileAndString": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file", + "tags" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "tags": { + "type": "string" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileAndEnum": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "dateTimeKind", + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "dateTimeKind": { + "$ref": "#/components/schemas/DateTimeKind" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "dateTimeKind": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromObjectAndString": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/XmlComments/Car/{id}": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The product id", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 111 + } + ], + "responses": { + "200": { + "description": "A Product Id", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/Car": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product using asParameters record", + "parameters": [ + { + "name": "Id", + "in": "query", + "description": "Uniquely identifies the product", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "Description", + "in": "query", + "description": "Describes the product", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/CarWithProduces": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product With Produces attribute", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/CarWithProducesDefaultResponseType": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product With ProducesDefaultResponseType attribute", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "default": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "state": { + "type": "string", + "nullable": true + }, + "zipCode": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "AddressAnnotated": { + "type": "object", + "properties": { + "street": { + "type": "string", + "description": "Description for Street", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "state": { + "type": "string", + "nullable": true + }, + "zipCode": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "AsParametersRecord": { + "type": "object", + "properties": { + "paramOne": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "paramTwo": { + "type": "string", + "format": "uuid" + }, + "paramThree": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "paramFour": { + "type": "string", + "format": "date-time" + }, + "paramFive": { + "type": "string", + "format": "date", + "nullable": true + }, + "paramSix": { + "type": "string", + "format": "date" + }, + "paramSeven": { + "type": "string", + "format": "time", + "nullable": true + }, + "paramEight": { + "type": "string", + "format": "time" + }, + "paramNine": { + "$ref": "#/components/schemas/DateTimeKind" + }, + "paramTen": { + "$ref": "#/components/schemas/DateTimeKind" + }, + "paramEleven": { + "type": "number", + "format": "double", + "nullable": true + }, + "paramTwelve": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "CurrenciesRate": { + "required": [ + "currencyFrom", + "currencyTo" + ], + "type": "object", + "properties": { + "currencyFrom": { + "type": "string", + "description": "Currency From", + "nullable": true + }, + "currencyTo": { + "type": "string", + "nullable": true + }, + "rate": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "DateTimeKind": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "format": "int32" + }, + "Fruit": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Description for Schema" + }, + "OrganizationCustomExchangeRatesDto": { + "required": [ + "currenciesRates" + ], + "type": "object", + "properties": { + "currenciesRates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CurrenciesRate" + }, + "nullable": true + }, + "isUpdated": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "Person": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "PersonAnnotated": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "description": "Description for FirstName", + "nullable": true + }, + "lastName": { + "type": "string", + "description": "Description for LastName", + "nullable": true + } + }, + "additionalProperties": false + }, + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Uniquely identifies the product", + "format": "int32" + }, + "description": { + "type": "string", + "description": "Describes the product", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Represents a product" + }, + "WeatherForecast": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "temperatureC": { + "type": "integer", + "format": "int32" + }, + "summary": { + "type": "string", + "nullable": true + }, + "temperatureF": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt index d437eea1cf..6e9b065305 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet8_0.verified.txt @@ -505,8 +505,7 @@ ] } } - }, - "required": true + } }, "responses": { "200": { @@ -531,7 +530,6 @@ { "name": "queryParameter", "in": "query", - "description": "queryParameter Description", "required": true, "schema": { "type": "string" @@ -559,8 +557,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -605,8 +602,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -684,8 +680,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -734,8 +729,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -808,8 +802,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -1097,5 +1090,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt index d437eea1cf..6e9b065305 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.Swagger_IsValidJson_No_Startup_entryPointType=WebApi.Program_swaggerRequestUri=v1.DotNet9_0.verified.txt @@ -505,8 +505,7 @@ ] } } - }, - "required": true + } }, "responses": { "200": { @@ -531,7 +530,6 @@ { "name": "queryParameter", "in": "query", - "description": "queryParameter Description", "required": true, "schema": { "type": "string" @@ -559,8 +557,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -605,8 +602,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -684,8 +680,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -734,8 +729,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -808,8 +802,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -1097,5 +1090,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000000..048170ae70 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,1220 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "WebApi", + "version": "v1" + }, + "paths": { + "/annotations/fruit/{id}": { + "post": { + "tags": [ + "Annotations" + ], + "summary": "CreateFruit", + "description": "Create a fruit", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The id of the fruit that will be created", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Description for Body", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Fruit" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Description for response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Fruit" + } + } + } + } + } + } + }, + "/annotations/singleForm": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PersonAnnotated" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PersonAnnotated" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/multipleForms": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "$ref": "#/components/schemas/AddressAnnotated" + } + ] + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "$ref": "#/components/schemas/AddressAnnotated" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromFileAndString": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file", + "tags" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "description": "Description for File", + "format": "binary" + }, + "tags": { + "type": "string" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromFileAndEnum": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "dateTimeKind", + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "dateTimeKind": { + "$ref": "#/components/schemas/DateTimeKind" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "dateTimeKind": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/IFromObjectAndString": { + "post": { + "tags": [ + "Annotations" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonAnnotated" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/annotations/AsParameters": { + "get": { + "tags": [ + "Annotations" + ], + "parameters": [ + { + "name": "paramOne", + "in": "query", + "description": "Description", + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "paramTwo", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + }, + { + "name": "paramThree", + "in": "query", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "paramFour", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "paramFive", + "in": "query", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "paramSix", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "date" + } + }, + { + "name": "paramSeven", + "in": "query", + "schema": { + "type": "string", + "format": "time" + } + }, + { + "name": "paramEight", + "in": "query", + "required": true, + "schema": { + "type": "string", + "format": "time" + } + }, + { + "name": "paramNine", + "in": "query", + "schema": { + "$ref": "#/components/schemas/DateTimeKind" + } + }, + { + "name": "paramTen", + "in": "query", + "required": true, + "schema": { + "$ref": "#/components/schemas/DateTimeKind" + } + }, + { + "name": "paramEleven", + "in": "query", + "schema": { + "type": "number", + "format": "double" + } + }, + { + "name": "paramTwelve", + "in": "query", + "required": true, + "schema": { + "type": "number", + "format": "double" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsParametersRecord" + } + } + } + } + } + } + }, + "/TypeWithTryParse/{tryParse}": { + "get": { + "tags": [ + "WebApi" + ], + "parameters": [ + { + "name": "tryParse", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/weatherforecast": { + "get": { + "tags": [ + "WithOpenApi" + ], + "operationId": "GetWeatherForecast", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + } + } + } + } + } + }, + "/WithOpenApi/multipleForms": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "$ref": "#/components/schemas/Address" + } + ] + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "$ref": "#/components/schemas/Address" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFile": { + "post": { + "tags": [ + "WithOpenApi" + ], + "parameters": [ + { + "name": "queryParameter", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "file": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileCollection": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "collection" + ], + "type": "object", + "properties": { + "collection": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + } + }, + "encoding": { + "collection": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromBody": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationCustomExchangeRatesDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileAndString": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file", + "tags" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "tags": { + "type": "string" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromFileAndEnum": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "dateTimeKind", + "file" + ], + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "binary" + }, + "dateTimeKind": { + "$ref": "#/components/schemas/DateTimeKind" + } + } + }, + "encoding": { + "file": { + "style": "form" + }, + "dateTimeKind": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/WithOpenApi/IFromObjectAndString": { + "post": { + "tags": [ + "WithOpenApi" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/Person" + }, + { + "required": [ + "tags" + ], + "type": "object", + "properties": { + "tags": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "tags": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/XmlComments/Car/{id}": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The product id", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + }, + "example": 111 + } + ], + "responses": { + "200": { + "description": "A Product Id", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/Car": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product using asParameters record", + "parameters": [ + { + "name": "Id", + "in": "query", + "description": "Uniquely identifies the product", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + }, + { + "name": "Description", + "in": "query", + "description": "Describes the product", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/CarWithProduces": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product With Produces attribute", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + }, + "/XmlComments/CarWithProducesDefaultResponseType": { + "get": { + "tags": [ + "Xml" + ], + "summary": "Returns a specific product With ProducesDefaultResponseType attribute", + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + }, + "default": { + "description": "A Product", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "state": { + "type": "string", + "nullable": true + }, + "zipCode": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "AddressAnnotated": { + "type": "object", + "properties": { + "street": { + "type": "string", + "description": "Description for Street", + "nullable": true + }, + "city": { + "type": "string", + "nullable": true + }, + "state": { + "type": "string", + "nullable": true + }, + "zipCode": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "AsParametersRecord": { + "type": "object", + "properties": { + "paramOne": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "paramTwo": { + "type": "string", + "format": "uuid" + }, + "paramThree": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "paramFour": { + "type": "string", + "format": "date-time" + }, + "paramFive": { + "type": "string", + "format": "date", + "nullable": true + }, + "paramSix": { + "type": "string", + "format": "date" + }, + "paramSeven": { + "type": "string", + "format": "time", + "nullable": true + }, + "paramEight": { + "type": "string", + "format": "time" + }, + "paramNine": { + "$ref": "#/components/schemas/DateTimeKind" + }, + "paramTen": { + "$ref": "#/components/schemas/DateTimeKind" + }, + "paramEleven": { + "type": "number", + "format": "double", + "nullable": true + }, + "paramTwelve": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "CurrenciesRate": { + "required": [ + "currencyFrom", + "currencyTo" + ], + "type": "object", + "properties": { + "currencyFrom": { + "type": "string", + "description": "Currency From", + "nullable": true + }, + "currencyTo": { + "type": "string", + "nullable": true + }, + "rate": { + "type": "number", + "format": "double" + } + }, + "additionalProperties": false + }, + "DateTimeKind": { + "enum": [ + 0, + 1, + 2 + ], + "type": "integer", + "format": "int32" + }, + "Fruit": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Description for Schema" + }, + "OrganizationCustomExchangeRatesDto": { + "required": [ + "currenciesRates" + ], + "type": "object", + "properties": { + "currenciesRates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CurrenciesRate" + }, + "nullable": true + }, + "isUpdated": { + "type": "boolean", + "readOnly": true + } + }, + "additionalProperties": false + }, + "Person": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "PersonAnnotated": { + "type": "object", + "properties": { + "firstName": { + "type": "string", + "description": "Description for FirstName", + "nullable": true + }, + "lastName": { + "type": "string", + "description": "Description for LastName", + "nullable": true + } + }, + "additionalProperties": false + }, + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Uniquely identifies the product", + "format": "int32" + }, + "description": { + "type": "string", + "description": "Describes the product", + "nullable": true + } + }, + "additionalProperties": false, + "description": "Represents a product" + }, + "WeatherForecast": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "temperatureC": { + "type": "integer", + "format": "int32" + }, + "summary": { + "type": "string", + "nullable": true + }, + "temperatureF": { + "type": "integer", + "format": "int32", + "readOnly": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet8_0.verified.txt index d437eea1cf..6e9b065305 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet8_0.verified.txt @@ -505,8 +505,7 @@ ] } } - }, - "required": true + } }, "responses": { "200": { @@ -531,7 +530,6 @@ { "name": "queryParameter", "in": "query", - "description": "queryParameter Description", "required": true, "schema": { "type": "string" @@ -559,8 +557,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -605,8 +602,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -684,8 +680,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -734,8 +729,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -808,8 +802,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -1097,5 +1090,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet9_0.verified.txt index d437eea1cf..6e9b065305 100644 --- a/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.IntegrationTests/snapshots/VerifyTests.TypesAreRenderedCorrectly.DotNet9_0.verified.txt @@ -505,8 +505,7 @@ ] } } - }, - "required": true + } }, "responses": { "200": { @@ -531,7 +530,6 @@ { "name": "queryParameter", "in": "query", - "description": "queryParameter Description", "required": true, "schema": { "type": "string" @@ -559,8 +557,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -605,8 +602,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -684,8 +680,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -734,8 +729,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -808,8 +802,7 @@ } } } - }, - "required": true + } }, "responses": { "200": { @@ -1097,5 +1090,19 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Annotations" + }, + { + "name": "WebApi" + }, + { + "name": "WithOpenApi" + }, + { + "name": "Xml" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/RecursiveCallSchemaFilter.cs b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/RecursiveCallSchemaFilter.cs index 763bc346da..5551799107 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/RecursiveCallSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/RecursiveCallSchemaFilter.cs @@ -1,11 +1,11 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Newtonsoft.Test; public class RecursiveCallSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema model, SchemaFilterContext context) + public void Apply(IOpenApiSchema model, SchemaFilterContext context) { if (model.Type == JsonSchemaTypes.Object) { diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/VendorExtensionsSchemaFilter.cs b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/VendorExtensionsSchemaFilter.cs index 02a8ea7311..d9ca102ba0 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/VendorExtensionsSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Fixtures/VendorExtensionsSchemaFilter.cs @@ -1,13 +1,16 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Swashbuckle.AspNetCore.Newtonsoft.Test; public class VendorExtensionsSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Extensions.Add("X-foo", new OpenApiString("bar")); + if (schema is OpenApiSchema openApiSchema) + { + openApiSchema.Extensions ??= new Dictionary(); + openApiSchema.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + } } } diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs index dbe50b446b..1e3a76f7b4 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/SchemaGenerator/NewtonsoftSchemaGeneratorTests.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.NewtonsoftJson; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; @@ -14,8 +14,6 @@ using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures; using Swashbuckle.AspNetCore.TestSupport; -using JsonSchemaType = string; - namespace Swashbuckle.AspNetCore.Newtonsoft.Test; public class NewtonsoftSchemaGeneratorTests @@ -57,16 +55,16 @@ public void GenerateSchema_GeneratesFileSchema_BinaryStringResultType(Type type) { typeof(Version), JsonSchemaTypes.String, null }, { typeof(DateOnly), JsonSchemaTypes.String, "date" }, { typeof(TimeOnly), JsonSchemaTypes.String, "time" }, - { typeof(bool?), JsonSchemaTypes.Boolean, null }, - { typeof(int?), JsonSchemaTypes.Integer, "int32" }, - { typeof(DateTime?), JsonSchemaTypes.String, "date-time" }, - { typeof(Guid?), JsonSchemaTypes.String, "uuid" }, - { typeof(DateOnly?), JsonSchemaTypes.String, "date" }, - { typeof(TimeOnly?), JsonSchemaTypes.String, "time" }, + { typeof(bool?), JsonSchemaTypes.Boolean | JsonSchemaType.Null, null }, + { typeof(int?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int32" }, + { typeof(DateTime?), JsonSchemaTypes.String | JsonSchemaType.Null, "date-time" }, + { typeof(Guid?), JsonSchemaTypes.String | JsonSchemaType.Null, "uuid" }, + { typeof(DateOnly?), JsonSchemaTypes.String | JsonSchemaType.Null, "date" }, + { typeof(TimeOnly?), JsonSchemaTypes.String | JsonSchemaType.Null, "time" }, { typeof(Int128), JsonSchemaTypes.Integer, "int128" }, - { typeof(Int128?), JsonSchemaTypes.Integer, "int128" }, + { typeof(Int128?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int128" }, { typeof(UInt128), JsonSchemaTypes.Integer, "int128" }, - { typeof(UInt128?), JsonSchemaTypes.Integer, "int128" }, + { typeof(UInt128?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int128" }, }; [Theory] @@ -100,7 +98,7 @@ public void GenerateSchema_GeneratesReferencedEnumSchema_IfEnumOrNullableEnumTyp { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(type, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(type, schemaRepository)); Assert.NotNull(referenceSchema.Reference); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; @@ -116,7 +114,7 @@ public void GenerateSchema_DedupsEnumValues_IfEnumTypeHasDuplicateValues() var enumType = typeof(HttpStatusCode); var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(enumType, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(enumType, schemaRepository)); Assert.NotNull(referenceSchema.Reference); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; @@ -161,24 +159,28 @@ public void GenerateSchema_GeneratesReferencedDictionarySchema_IfDictionaryTypeI { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(DictionaryOfSelf), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(DictionaryOfSelf), schemaRepository)); Assert.NotNull(referenceSchema.Reference); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.True(schema.AdditionalPropertiesAllowed); Assert.NotNull(schema.AdditionalProperties); - Assert.Equal(schema.AdditionalProperties.Reference.Id, referenceSchema.Reference.Id); // ref to self + Assert.Equal(Assert.IsType(schema.AdditionalProperties).Reference.Id, referenceSchema.Reference.Id); // ref to self } #nullable enable public static TheoryData EnumerableTypesData => new() { { typeof(int[]), JsonSchemaTypes.Integer, "int32" }, + { typeof(int?[]), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int32" }, + { typeof(double[]), JsonSchemaTypes.Number, "double" }, + { typeof(double?[]), JsonSchemaTypes.Number | JsonSchemaType.Null, "double" }, + { typeof(DateTime[]), JsonSchemaTypes.String, "date-time" }, + { typeof(DateTime?[]), JsonSchemaTypes.String | JsonSchemaType.Null, "date-time" }, { typeof(IEnumerable), JsonSchemaTypes.String, null }, - { typeof(DateTime?[]), JsonSchemaTypes.String, "date-time" }, { typeof(int[][]), JsonSchemaTypes.Array, null }, - { typeof(IList), null, null } + { typeof(IList), null, null }, }; [Theory] @@ -214,12 +216,12 @@ public void GenerateSchema_GeneratesReferencedArraySchema_IfEnumerableTypeIsSelf { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(ListOfSelf), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(ListOfSelf), schemaRepository)); Assert.NotNull(referenceSchema.Reference); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Array, schema.Type); - Assert.Equal(schema.Items.Reference.Id, referenceSchema.Reference.Id); // ref to self + Assert.Equal(Assert.IsType(schema.Items).Reference.Id, referenceSchema.Reference.Id); // ref to self } [Theory] @@ -234,7 +236,7 @@ public void GenerateSchema_GeneratesReferencedObjectSchema_IfComplexType( { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(type, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(type, schemaRepository)); Assert.NotNull(referenceSchema.Reference); Assert.Equal(expectedSchemaId, referenceSchema.Reference.Id); @@ -249,7 +251,7 @@ public void GenerateSchema_IncludesInheritedProperties_IfComplexTypeIsDerived() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(SubType1), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(SubType1), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); @@ -261,7 +263,7 @@ public void GenerateSchema_ExcludesIndexerProperties_IfComplexTypeIsIndexed() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(IndexedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(IndexedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); @@ -279,10 +281,10 @@ public void GenerateSchema_SetsNullableFlag_IfPropertyIsReferenceOrNullableType( { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(declaringType, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(declaringType, schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal(expectedNullable, schema.Properties[propertyName].Nullable); + Assert.Equal(expectedNullable, schema.Properties[propertyName].Type.Value.HasFlag(JsonSchemaType.Null)); } [Fact] @@ -292,10 +294,14 @@ public void GenerateSchema_DoesNotSetNullableFlag_IfReferencedEnum() var referenceSchema = Subject().GenerateSchema(typeof(TypeWithNullableProperties), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + const string propertyName = nameof(TypeWithNullableProperties.NullableIntEnumProperty); - Assert.False(schema.Properties[propertyName].Nullable); - Assert.Equal("IntEnum", schema.Properties[propertyName].Reference.Id); + Assert.False(schema.Properties[propertyName].Type.HasValue); + + reference = Assert.IsType(schema.Properties[propertyName]); + Assert.Equal("IntEnum", reference.Reference.Id); } [Fact] @@ -305,8 +311,9 @@ public void GenerateSchema_SetNullableFlag_IfInlineEnum() var referenceSchema = Subject(o => o.UseInlineDefinitionsForEnums = true).GenerateSchema(typeof(TypeWithNullableProperties), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.True(schema.Properties[nameof(TypeWithNullableProperties.NullableIntEnumProperty)].Nullable); + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + Assert.True(schema.Properties[nameof(TypeWithNullableProperties.NullableIntEnumProperty)].Type.Value.HasFlag(JsonSchemaType.Null)); } [Theory] @@ -320,7 +327,7 @@ public void GenerateSchema_SetNullableFlag_IfInlineEnum() [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.IntArrayWithDefault), "[\n 1,\n 2,\n 3\n]")] [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.StringArrayWithDefault), "[\n \"foo\",\n \"bar\"\n]")] [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultValue), "2147483647")] - [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultNullValue), "null")] + [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultNullValue), null)] public void GenerateSchema_SetsDefault_IfPropertyHasDefaultValueAttribute( Type declaringType, string propertyName, @@ -332,7 +339,7 @@ public void GenerateSchema_SetsDefault_IfPropertyHasDefaultValueAttribute( var schemaRepository = new SchemaRepository(); // Act - var referenceSchema = Subject().GenerateSchema(declaringType, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(declaringType, schemaRepository)); // Assert var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; @@ -345,7 +352,7 @@ public void GenerateSchema_SetsDeprecatedFlag_IfPropertyHasObsoleteAttribute() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.True(schema.Properties["ObsoleteProperty"].Deprecated); @@ -358,7 +365,7 @@ public void GenerateSchema_SetsValidationProperties_IfComplexTypeHasValidationAt { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(type, schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(type, schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal("credit-card", schema.Properties["StringWithDataTypeCreditCard"].Format); @@ -370,26 +377,28 @@ public void GenerateSchema_SetsValidationProperties_IfComplexTypeHasValidationAt Assert.Equal(3, schema.Properties["StringWithLength"].MaxLength); Assert.Equal(1, schema.Properties["ArrayWithLength"].MinItems); Assert.Equal(3, schema.Properties["ArrayWithLength"].MaxItems); - Assert.Equal(true, schema.Properties["IntWithExclusiveRange"].ExclusiveMinimum); - Assert.Equal(true, schema.Properties["IntWithExclusiveRange"].ExclusiveMaximum); + Assert.NotNull(schema.Properties["IntWithExclusiveRange"].ExclusiveMinimum); + Assert.NotNull(schema.Properties["IntWithExclusiveRange"].ExclusiveMaximum); Assert.Equal("byte", schema.Properties["StringWithBase64"].Format); - Assert.Equal(JsonSchemaTypes.String, schema.Properties["StringWithBase64"].Type); + Assert.Equal(JsonSchemaTypes.String | JsonSchemaType.Null, schema.Properties["StringWithBase64"].Type); Assert.Null(schema.Properties["IntWithRange"].ExclusiveMinimum); Assert.Null(schema.Properties["IntWithRange"].ExclusiveMaximum); - Assert.Equal(1, schema.Properties["IntWithRange"].Minimum); - Assert.Equal(10, schema.Properties["IntWithRange"].Maximum); + Assert.Equal("1", schema.Properties["IntWithRange"].Minimum); + Assert.Equal("10", schema.Properties["IntWithRange"].Maximum); Assert.Equal("^[3-6]?\\d{12,15}$", schema.Properties["StringWithRegularExpression"].Pattern); Assert.Equal(5, schema.Properties["StringWithStringLength"].MinLength); Assert.Equal(10, schema.Properties["StringWithStringLength"].MaxLength); Assert.Equal(1, schema.Properties["StringWithRequired"].MinLength); - Assert.False(schema.Properties["StringWithRequired"].Nullable); - Assert.False(schema.Properties["StringWithRequiredAllowEmptyTrue"].Nullable); + Assert.False(schema.Properties["StringWithRequired"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithRequiredAllowEmptyTrue"].Type.Value.HasFlag(JsonSchemaType.Null)); Assert.Null(schema.Properties["StringWithRequiredAllowEmptyTrue"].MinLength); Assert.Equal(["NullableIntEnumWithRequired", "StringWithRequired", "StringWithRequiredAllowEmptyTrue"], schema.Required); Assert.Equal("Description", schema.Properties[nameof(TypeWithValidationAttributes.StringWithDescription)].Description); Assert.True(schema.Properties[nameof(TypeWithValidationAttributes.StringWithReadOnly)].ReadOnly); - Assert.False(schema.Properties[nameof(TypeWithValidationAttributes.NullableIntEnumWithRequired)].Nullable); - Assert.Equal(nameof(IntEnum), schema.Properties[nameof(TypeWithValidationAttributes.NullableIntEnumWithRequired)].Reference.Id); + + var reference = Assert.IsType(schema.Properties[nameof(TypeWithValidationAttributes.NullableIntEnumWithRequired)]); + Assert.False(reference.Type.HasValue); + Assert.Equal(nameof(IntEnum), reference.Reference.Id); } [Fact] @@ -397,7 +406,7 @@ public void GenerateSchema_SetsReadOnlyAndWriteOnlyFlags_IfPropertyIsRestricted( { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRestrictedProperties), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(TypeWithRestrictedProperties), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.False(schema.Properties["ReadWriteProperty"].ReadOnly); @@ -413,7 +422,7 @@ public void GenerateSchema_DoesNotSetReadOnlyFlag_IfPropertyIsReadOnlyButCanBeSe { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(TypeWithPropertiesSetViaConstructor), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(TypeWithPropertiesSetViaConstructor), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.False(schema.Properties["Id"].ReadOnly); @@ -434,7 +443,7 @@ public void GenerateSchema_SupportsOption_CustomTypeMappings( var schema = subject.GenerateSchema(type, new SchemaRepository()); Assert.Equal(JsonSchemaTypes.String, schema.Type); - Assert.Empty(schema.Properties); + Assert.Null(schema.Properties); } [Theory] @@ -451,10 +460,14 @@ public void GenerateSchema_SupportsOption_SchemaFilters(Type type) var schema = subject.GenerateSchema(type, schemaRepository); - if (schema.Reference == null) - Assert.Contains("X-foo", schema.Extensions.Keys); + if (schema is OpenApiSchemaReference reference) + { + Assert.Contains("X-foo", schemaRepository.Schemas[reference.Reference.Id].Extensions.Keys); + } else - Assert.Contains("X-foo", schemaRepository.Schemas[schema.Reference.Id].Extensions.Keys); + { + Assert.Contains("X-foo", schema.Extensions.Keys); + } } [Fact] @@ -465,7 +478,7 @@ public void GenerateSchema_SupportsOption_IgnoreObsoleteProperties() ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.DoesNotContain("ObsoleteProperty", schema.Properties.Keys); @@ -479,7 +492,7 @@ public void GenerateSchema_SupportsOption_SchemaIdSelector() ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(ComplexType), schemaRepository)); Assert.Equal("Swashbuckle.AspNetCore.TestSupport.ComplexType", referenceSchema.Reference.Id); Assert.Contains(referenceSchema.Reference.Id, schemaRepository.Schemas.Keys); @@ -493,14 +506,13 @@ public void GenerateSchema_SupportsOption_UseAllOfForInheritance() ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(SubType1), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(SubType1), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.NotNull(schema.AllOf); Assert.Equal(2, schema.AllOf.Count); - var baseSchema = schema.AllOf[0]; + var baseSchema = Assert.IsType(schema.AllOf[0]); Assert.Equal("BaseType", baseSchema.Reference.Id); - Assert.NotNull(baseSchema.Reference); var subSchema = schema.AllOf[1]; Assert.Equal(["Property1"], subSchema.Properties.Keys); // The base type schema @@ -536,7 +548,7 @@ public void GenerateSchema_SupportsOption_DiscriminatorNameSelector() var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(BaseType), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(BaseType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Contains("TypeName", schema.Properties.Keys); @@ -554,34 +566,20 @@ public void GenerateSchema_SupportsOption_UseAllOfForPolymorphism() }); var schemaRepository = new SchemaRepository(); - var schema = subject.GenerateSchema(typeof(BaseType), schemaRepository); + var schema = Assert.IsType(subject.GenerateSchema(typeof(BaseType), schemaRepository)); // The polymorphic schema Assert.NotNull(schema.OneOf); Assert.Equal(3, schema.OneOf.Count); // The base type schema - Assert.NotNull(schema.OneOf[0].Reference); - var baseSchema = schemaRepository.Schemas[schema.OneOf[0].Reference.Id]; - Assert.Equal(JsonSchemaTypes.Object, baseSchema.Type); - Assert.Equal(["BaseProperty"], baseSchema.Properties.Keys); + var baseSchema = Assert.IsType(schema.OneOf[0]); + Assert.Equal("BaseType", baseSchema.Reference.Id); // The first sub type schema - Assert.NotNull(schema.OneOf[1].Reference); - var subType1Schema = schemaRepository.Schemas[schema.OneOf[1].Reference.Id]; - Assert.Equal(JsonSchemaTypes.Object, subType1Schema.Type); - Assert.NotNull(subType1Schema.AllOf); - var allOf = Assert.Single(subType1Schema.AllOf); - Assert.NotNull(allOf.Reference); - Assert.Equal("BaseType", allOf.Reference.Id); - Assert.Equal(["Property1"], subType1Schema.Properties.Keys); + baseSchema = Assert.IsType(schema.OneOf[1]); + Assert.Equal("SubType1", baseSchema.Reference.Id); // The second sub type schema - Assert.NotNull(schema.OneOf[2].Reference); - var subType2Schema = schemaRepository.Schemas[schema.OneOf[2].Reference.Id]; - Assert.Equal(JsonSchemaTypes.Object, subType2Schema.Type); - Assert.NotNull(subType2Schema.AllOf); - allOf = Assert.Single(subType2Schema.AllOf); - Assert.NotNull(allOf.Reference); - Assert.Equal("BaseType", allOf.Reference.Id); - Assert.Equal(["Property2"], subType2Schema.Properties.Keys); + baseSchema = Assert.IsType(schema.OneOf[2]); + Assert.Equal("SubType2", baseSchema.Reference.Id); } [Fact] @@ -594,7 +592,7 @@ public void GenerateSchema_SupportsOption_UseAllOfToExtendReferenceSchemas() var schema = subject.GenerateSchema(propertyInfo.PropertyType, new SchemaRepository(), memberInfo: propertyInfo); - Assert.Null(schema.Reference); + Assert.IsNotType(schema); Assert.NotNull(schema.AllOf); Assert.Single(schema.AllOf); } @@ -617,11 +615,11 @@ public void GenerateSchema_HandlesTypesWithNestedTypes() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(ContainingType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(ContainingType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); - Assert.Equal("NestedType", schema.Properties["Property1"].Reference.Id); + Assert.Equal("NestedType", Assert.IsType(schema.Properties["Property1"]).Reference.Id); } [Fact] @@ -641,11 +639,11 @@ public void GenerateSchema_HandlesTypesWithOverriddenProperties() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(TypeWithOverriddenProperty), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(TypeWithOverriddenProperty), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); - Assert.Equal(JsonSchemaTypes.String, schema.Properties["Property1"].Type); + Assert.Equal(JsonSchemaTypes.String | JsonSchemaType.Null, schema.Properties["Property1"].Type); } [Fact] @@ -656,10 +654,10 @@ public void GenerateSchema_HandlesRecursion_IfCalledAgainWithinAFilter() ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(ComplexType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal("ComplexType", schema.Properties["Self"].Reference.Id); + Assert.Equal("ComplexType", Assert.IsType(schema.Properties["Self"]).Reference.Id); } [Fact] @@ -693,7 +691,7 @@ public void GenerateSchema_HonorsSerializerSetting_StringEnumConverter( ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(TypeWithDefaultAttributeOnEnum), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(TypeWithDefaultAttributeOnEnum), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; var propertySchema = schema.Properties[nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault)]; @@ -732,7 +730,7 @@ public void GenerateSchema_HonorsSerializerSetting_TypeNameHandling( ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(BaseType), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(BaseType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; if (expectedDiscriminatorPresent) @@ -744,11 +742,11 @@ public void GenerateSchema_HonorsSerializerSetting_TypeNameHandling( Assert.Equal( expected: new Dictionary { - [string.Format(expectedDiscriminatorMappingKeyFormat, "BaseType")] = "#/components/schemas/BaseType", - [string.Format(expectedDiscriminatorMappingKeyFormat, "SubType1")] = "#/components/schemas/SubType1", - [string.Format(expectedDiscriminatorMappingKeyFormat, "SubType2")] = "#/components/schemas/SubType2" + [string.Format(expectedDiscriminatorMappingKeyFormat, "BaseType")] = "BaseType", + [string.Format(expectedDiscriminatorMappingKeyFormat, "SubType1")] = "SubType1", + [string.Format(expectedDiscriminatorMappingKeyFormat, "SubType2")] = "SubType2", }, - actual: schema.Discriminator.Mapping); + actual: schema.Discriminator.Mapping.ToDictionary((k) => k.Key, (v) => v.Value.Reference.Id)); } else { @@ -769,7 +767,7 @@ public void GenerateSchema_HonorsSerializeSetting_ContractResolver() ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(typeof(BaseType), schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(typeof(BaseType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; @@ -781,7 +779,7 @@ public void GenerateSchema_HonorsSerializerAttribute_StringEnumConverter() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonConverterAnnotatedEnum), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonConverterAnnotatedEnum), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(JsonSchemaTypes.String, schema.Type); @@ -793,7 +791,7 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonIgnore() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonIgnoreAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonIgnoreAnnotatedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(["StringWithNoAnnotation"], schema.Properties.Keys); @@ -804,9 +802,11 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonProperty() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonPropertyAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonPropertyAnnotatedType), schemaRepository)); + + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal( [ "string-with-json-property-name", @@ -834,22 +834,19 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonProperty() ], schema.Required ); - Assert.True(schema.Properties["string-with-json-property-name"].Nullable); - Assert.False(schema.Properties["IntWithRequiredDefault"].Nullable); - Assert.True(schema.Properties["StringWithRequiredDefault"].Nullable); - Assert.False(schema.Properties["StringWithRequiredDisallowNull"].Nullable); - Assert.False(schema.Properties["StringWithRequiredAlways"].Nullable); - Assert.True(schema.Properties["StringWithRequiredAllowNull"].Nullable); - Assert.False(schema.Properties["StringWithRequiredAlwaysButConflictingDataMember"].Nullable); - Assert.True(schema.Properties["StringWithRequiredDefaultButConflictingDataMember"].Nullable); - Assert.False(schema.Properties["IntEnumWithRequiredDefault"].Nullable); - Assert.False(schema.Properties["IntEnumWithRequiredAllowNull"].Nullable); - Assert.False(schema.Properties["IntEnumWithRequiredAlways"].Nullable); - Assert.False(schema.Properties["IntEnumWithRequiredDisallowNull"].Nullable); - Assert.Equal("IntEnum", schema.Properties["IntEnumWithRequiredDefault"].Reference.Id); - Assert.Equal("IntEnum", schema.Properties["IntEnumWithRequiredAllowNull"].Reference.Id); - Assert.Equal(nameof(IntEnum), schema.Properties["IntEnumWithRequiredAlways"].Reference.Id); - Assert.Equal(nameof(IntEnum), schema.Properties["IntEnumWithRequiredDisallowNull"].Reference.Id); + + Assert.True(schema.Properties["string-with-json-property-name"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["IntWithRequiredDefault"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["StringWithRequiredDefault"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithRequiredDisallowNull"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithRequiredAlways"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["StringWithRequiredAllowNull"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithRequiredAlwaysButConflictingDataMember"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["StringWithRequiredDefaultButConflictingDataMember"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["IntEnumWithRequiredDefault"].Type.HasValue); + Assert.False(schema.Properties["IntEnumWithRequiredAllowNull"].Type.HasValue); + Assert.False(schema.Properties["IntEnumWithRequiredAlways"].Type.HasValue); + Assert.False(schema.Properties["IntEnumWithRequiredDisallowNull"].Type.HasValue); } [Fact] @@ -857,13 +854,16 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonRequired() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonRequiredAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonRequiredAnnotatedType), schemaRepository)); + + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(["IntEnumWithRequired", "NullableIntEnumWithRequired", "StringWithConflictingRequired", "StringWithJsonRequired"], schema.Required); - Assert.False(schema.Properties["StringWithJsonRequired"].Nullable); - Assert.False(schema.Properties["IntEnumWithRequired"].Nullable); - Assert.Equal(nameof(IntEnum), schema.Properties["IntEnumWithRequired"].Reference.Id); + Assert.False(schema.Properties["StringWithJsonRequired"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["IntEnumWithRequired"].Type.HasValue); + reference = Assert.IsType(schema.Properties["IntEnumWithRequired"]); + Assert.Equal(nameof(IntEnum), reference.Reference.Id); Assert.True(schemaRepository.TryLookupByType(typeof(IntEnum), out _)); Assert.False(schemaRepository.TryLookupByType(typeof(IntEnum?), out _)); } @@ -873,7 +873,7 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonObject() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonObjectAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonObjectAnnotatedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal( @@ -885,10 +885,10 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonObject() ], schema.Required ); - Assert.False(schema.Properties["StringWithNoAnnotation"].Nullable); - Assert.False(schema.Properties["StringWithRequiredUnspecified"].Nullable); - Assert.True(schema.Properties["StringWithRequiredAllowNull"].Nullable); - Assert.False(schema.Properties["StringWithDataMemberRequiredFalse"].Nullable); + Assert.False(schema.Properties["StringWithNoAnnotation"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithRequiredUnspecified"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["StringWithRequiredAllowNull"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.False(schema.Properties["StringWithDataMemberRequiredFalse"].Type.Value.HasFlag(JsonSchemaType.Null)); } [Fact] @@ -896,7 +896,7 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonExtensionData() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(JsonExtensionDataAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(JsonExtensionDataAnnotatedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.True(schema.AdditionalPropertiesAllowed); @@ -909,7 +909,7 @@ public void GenerateSchema_HonorsAttribute_SwaggerIgnore() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(SwaggerIngoreAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(SwaggerIngoreAnnotatedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; @@ -925,15 +925,14 @@ public void GenerateSchema_HonorsDataMemberAttribute() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(DataMemberAnnotatedType), schemaRepository); + var referenceSchema = Assert.IsType(Subject().GenerateSchema(typeof(DataMemberAnnotatedType), schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - - Assert.True(schema.Properties["StringWithDataMemberRequired"].Nullable); - Assert.True(schema.Properties["StringWithDataMemberNonRequired"].Nullable); - Assert.True(schema.Properties["RequiredWithCustomNameFromDataMember"].Nullable); - Assert.True(schema.Properties["NonRequiredWithCustomNameFromDataMember"].Nullable); + Assert.True(schema.Properties["StringWithDataMemberRequired"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["StringWithDataMemberNonRequired"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["RequiredWithCustomNameFromDataMember"].Type.Value.HasFlag(JsonSchemaType.Null)); + Assert.True(schema.Properties["NonRequiredWithCustomNameFromDataMember"].Type.Value.HasFlag(JsonSchemaType.Null)); Assert.Equal( [ @@ -969,7 +968,7 @@ public void GenerateSchema_HonorsSerializerSetting_ProblemDetailsConverter(Type ); var schemaRepository = new SchemaRepository(); - var referenceSchema = subject.GenerateSchema(type, schemaRepository); + var referenceSchema = Assert.IsType(subject.GenerateSchema(type, schemaRepository)); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.DoesNotContain("Extensions", schema.Properties.Keys); @@ -985,9 +984,8 @@ public void GenerateSchema_HonorsSerializerSetting_ProblemDetailsConverter(Type [InlineData(typeof(JArray))] public void GenerateSchema_GeneratesOpenSchema_IfDynamicJsonType(Type type) { - var schema = Subject().GenerateSchema(type, new SchemaRepository()); + var schema = Assert.IsType(Subject().GenerateSchema(type, new SchemaRepository())); - Assert.Null(schema.Reference); Assert.Null(schema.Type); } diff --git a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj index 2cece83730..935262b978 100644 --- a/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj +++ b/test/Swashbuckle.AspNetCore.Newtonsoft.Test/Swashbuckle.AspNetCore.Newtonsoft.Test.csproj @@ -6,6 +6,7 @@ + diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/RecursiveCallSchemaFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/RecursiveCallSchemaFilter.cs index 14080c3790..f1999b228a 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/RecursiveCallSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/RecursiveCallSchemaFilter.cs @@ -1,10 +1,10 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; public class RecursiveCallSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema model, SchemaFilterContext context) + public void Apply(IOpenApiSchema model, SchemaFilterContext context) { if (model.Type == JsonSchemaTypes.Object) { diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestDocumentFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestDocumentFilter.cs index d5a212e935..24b455e342 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestDocumentFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestDocumentFilter.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.TestSupport; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -8,8 +7,9 @@ public class TestDocumentFilter : IDocumentFilter, IDocumentAsyncFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Extensions.Add("X-foo", new OpenApiString("bar")); - swaggerDoc.Extensions.Add("X-docName", new OpenApiString(context.DocumentName)); + swaggerDoc.Extensions ??= new Dictionary(); + swaggerDoc.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + swaggerDoc.Extensions.Add("X-docName", new JsonNodeExtension(context.DocumentName)); context.SchemaGenerator.GenerateSchema(typeof(ComplexType), context.SchemaRepository); } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestEnumSchemaFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestEnumSchemaFilter.cs index 0d791449e3..49a5246e80 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestEnumSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestEnumSchemaFilter.cs @@ -1,11 +1,11 @@ using System.Text; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures; internal class TestEnumSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { bool isEnumArgument = (context.Type?.GenericTypeArguments?.Length ?? 0) == 1 && context.Type.GenericTypeArguments.All(b => b.IsEnum); var isEnumArray = context.Type.IsArray && context.Type.GetElementType().IsEnum; diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestOperationFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestOperationFilter.cs index b3e13e2cb7..58c2536819 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestOperationFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestOperationFilter.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -7,8 +6,9 @@ public class TestOperationFilter : IOperationFilter, IOperationAsyncFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { - operation.Extensions.Add("X-foo", new OpenApiString("bar")); - operation.Extensions.Add("X-docName", new OpenApiString(context.DocumentName)); + operation.Extensions ??= new Dictionary(); + operation.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + operation.Extensions.Add("X-docName", new JsonNodeExtension(context.DocumentName)); } public Task ApplyAsync(OpenApiOperation operation, OperationFilterContext context, CancellationToken cancellationToken) diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestParameterFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestParameterFilter.cs index 28caa151ee..ab3bfae958 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestParameterFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestParameterFilter.cs @@ -1,17 +1,20 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; public class TestParameterFilter : IParameterFilter, IParameterAsyncFilter { - public void Apply(OpenApiParameter parameter, ParameterFilterContext context) + public void Apply(IOpenApiParameter parameter, ParameterFilterContext context) { - parameter.Extensions.Add("X-foo", new OpenApiString("bar")); - parameter.Extensions.Add("X-docName", new OpenApiString(context.DocumentName)); + if (parameter is OpenApiParameter openApiParameter) + { + openApiParameter.Extensions ??= new Dictionary(); + openApiParameter.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + openApiParameter.Extensions.Add("X-docName", new JsonNodeExtension(context.DocumentName)); + } } - public Task ApplyAsync(OpenApiParameter parameter, ParameterFilterContext context, CancellationToken cancellationToken) + public Task ApplyAsync(IOpenApiParameter parameter, ParameterFilterContext context, CancellationToken cancellationToken) { Apply(parameter, context); return Task.CompletedTask; diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestRequestBodyFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestRequestBodyFilter.cs index 0221466156..6ced63807d 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestRequestBodyFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestRequestBodyFilter.cs @@ -1,17 +1,20 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; public class TestRequestBodyFilter : IRequestBodyFilter, IRequestBodyAsyncFilter { - public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context) + public void Apply(IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { - requestBody.Extensions.Add("X-foo", new OpenApiString("bar")); - requestBody.Extensions.Add("X-docName", new OpenApiString(context.DocumentName)); + if (requestBody is OpenApiRequestBody body) + { + body.Extensions ??= new Dictionary(); + body.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + body.Extensions.Add("X-docName", new JsonNodeExtension(context.DocumentName)); + } } - public Task ApplyAsync(OpenApiRequestBody requestBody, RequestBodyFilterContext context, CancellationToken cancellationToken) + public Task ApplyAsync(IOpenApiRequestBody requestBody, RequestBodyFilterContext context, CancellationToken cancellationToken) { Apply(requestBody, context); return Task.CompletedTask; diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestSchemaFilter.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestSchemaFilter.cs index 247c04a99b..dfde121e84 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestSchemaFilter.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Fixtures/TestSchemaFilter.cs @@ -1,13 +1,16 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; public class TestSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Extensions.Add("X-foo", new OpenApiString("bar")); - schema.Extensions.Add("X-docName", new OpenApiString(context.DocumentName)); + if (schema is OpenApiSchema openApiSchema) + { + openApiSchema.Extensions ??= new Dictionary(); + openApiSchema.Extensions.Add("X-foo", new JsonNodeExtension("bar")); + openApiSchema.Extensions.Add("X-docName", new JsonNodeExtension(context.DocumentName)); + } } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index e5020c6fa3..f662064bf3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -11,13 +11,10 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Constraints; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures; using Swashbuckle.AspNetCore.TestSupport; -using JsonSchemaType = string; - namespace Swashbuckle.AspNetCore.SwaggerGen.Test; public class JsonSerializerSchemaGeneratorTests @@ -60,16 +57,16 @@ public void GenerateSchema_GeneratesFileSchema_BinaryStringResultType(Type type) { typeof(Version), JsonSchemaTypes.String, null }, { typeof(DateOnly), JsonSchemaTypes.String, "date" }, { typeof(TimeOnly), JsonSchemaTypes.String, "time" }, - { typeof(bool?), JsonSchemaTypes.Boolean, null }, - { typeof(int?), JsonSchemaTypes.Integer, "int32" }, - { typeof(DateTime?), JsonSchemaTypes.String, "date-time" }, - { typeof(Guid?), JsonSchemaTypes.String, "uuid" }, - { typeof(DateOnly?), JsonSchemaTypes.String, "date" }, - { typeof(TimeOnly?), JsonSchemaTypes.String, "time" }, + { typeof(bool?), JsonSchemaTypes.Boolean | JsonSchemaType.Null, null }, + { typeof(int?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int32" }, + { typeof(DateTime?), JsonSchemaTypes.String | JsonSchemaType.Null, "date-time" }, + { typeof(Guid?), JsonSchemaTypes.String | JsonSchemaType.Null, "uuid" }, + { typeof(DateOnly?), JsonSchemaTypes.String | JsonSchemaType.Null, "date" }, + { typeof(TimeOnly?), JsonSchemaTypes.String | JsonSchemaType.Null, "time" }, { typeof(Int128), JsonSchemaTypes.Integer, "int128" }, - { typeof(Int128?), JsonSchemaTypes.Integer, "int128" }, + { typeof(Int128?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int128" }, { typeof(UInt128), JsonSchemaTypes.Integer, "int128" }, - { typeof(UInt128?), JsonSchemaTypes.Integer, "int128" }, + { typeof(UInt128?), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int128" }, }; [Theory] @@ -86,41 +83,43 @@ public void GenerateSchema_GeneratesPrimitiveSchema_IfPrimitiveOrNullablePrimiti } [Theory] - [InlineData(typeof(IntEnum), "int32", false, "2", "4", "8")] - [InlineData(typeof(LongEnum), "int64", false, "2", "4", "8")] + [InlineData(typeof(IntEnum), JsonSchemaType.Integer, "int32", "2", "4", "8")] + [InlineData(typeof(LongEnum), JsonSchemaType.Integer, "int64", "2", "4", "8")] + [InlineData(typeof(IntEnum?), JsonSchemaType.Integer, "int32", "2", "4", "8")] + [InlineData(typeof(LongEnum?), JsonSchemaType.Integer, "int64", "2", "4", "8")] public void GenerateSchema_GeneratesReferencedEnumSchema_IfEnumOrNullableEnumType( Type type, + JsonSchemaType expectedType, string expectedFormat, - bool expectedNullable, params string[] expectedEnumAsJson) { var schemaRepository = new SchemaRepository(); var referenceSchema = Subject().GenerateSchema(type, schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal(JsonSchemaTypes.Integer, schema.Type); + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + Assert.Equal(expectedType, schema.Type); Assert.Equal(expectedFormat, schema.Format); - Assert.Equal(expectedNullable, schema.Nullable); Assert.NotNull(schema.Enum); - Assert.Equal(expectedEnumAsJson, schema.Enum.Select(openApiAny => openApiAny.ToJson())); + Assert.Equal(expectedEnumAsJson, schema.Enum.Select(openApiAny => openApiAny?.ToJson()).ToArray()); } [Fact] - public void GenerateSchema_DedupsEnumValues_IfEnumTypeHasDuplicateValues() + public void GenerateSchema_DedupesEnumValues_IfEnumTypeHasDuplicateValues() { var enumType = typeof(HttpStatusCode); var schemaRepository = new SchemaRepository(); var referenceSchema = Subject().GenerateSchema(enumType, schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(enumType.GetEnumValues().Cast().Distinct().Count(), schema.Enum.Count); } - public static TheoryData CollectionTypeData => new() +#nullable enable + public static TheoryData CollectionTypeData => new() { { typeof(IDictionary), JsonSchemaTypes.Integer }, { typeof(IDictionary), JsonSchemaTypes.Integer }, @@ -133,7 +132,7 @@ public void GenerateSchema_DedupsEnumValues_IfEnumTypeHasDuplicateValues() [MemberData(nameof(CollectionTypeData))] public void GenerateSchema_GeneratesDictionarySchema_IfDictionaryType( Type type, - JsonSchemaType expectedAdditionalPropertiesType) + JsonSchemaType? expectedAdditionalPropertiesType) { var schema = Subject().GenerateSchema(type, new SchemaRepository()); @@ -142,6 +141,7 @@ public void GenerateSchema_GeneratesDictionarySchema_IfDictionaryType( Assert.NotNull(schema.AdditionalProperties); Assert.Equal(expectedAdditionalPropertiesType, schema.AdditionalProperties.Type); } +#nullable restore [Fact] public void GenerateSchema_GeneratesReferencedDictionarySchema_IfDictionaryTypeIsSelfReferencing() @@ -150,34 +150,35 @@ public void GenerateSchema_GeneratesReferencedDictionarySchema_IfDictionaryTypeI var referenceSchema = Subject().GenerateSchema(typeof(DictionaryOfSelf), schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.True(schema.AdditionalPropertiesAllowed); Assert.NotNull(schema.AdditionalProperties); - Assert.Equal(schema.AdditionalProperties.Reference.Id, referenceSchema.Reference.Id); // ref to self + var additionalReference = Assert.IsType(schema.AdditionalProperties); + Assert.Equal(additionalReference.Reference.Id, reference.Reference.Id); // ref to self } - public static TheoryData EnumerableTypeData => new() - { - { typeof(int[]), JsonSchemaTypes.Integer, "int32", false }, - { typeof(int?[]), JsonSchemaTypes.Integer, "int32", true }, - { typeof(double[]), JsonSchemaTypes.Number, "double", false }, - { typeof(double?[]), JsonSchemaTypes.Number, "double", true }, - { typeof(DateTime[]), JsonSchemaTypes.String, "date-time", false }, - { typeof(DateTime?[]), JsonSchemaTypes.String, "date-time", true }, - { typeof(IEnumerable), JsonSchemaTypes.String, null, false }, - { typeof(int[][]), JsonSchemaTypes.Array, null, false }, - { typeof(IList), null, null, false }, +#nullable enable + public static TheoryData EnumerableTypeData => new() + { + { typeof(int[]), JsonSchemaTypes.Integer, "int32" }, + { typeof(int?[]), JsonSchemaTypes.Integer | JsonSchemaType.Null, "int32" }, + { typeof(double[]), JsonSchemaTypes.Number, "double" }, + { typeof(double?[]), JsonSchemaTypes.Number | JsonSchemaType.Null, "double" }, + { typeof(DateTime[]), JsonSchemaTypes.String, "date-time" }, + { typeof(DateTime?[]), JsonSchemaTypes.String | JsonSchemaType.Null, "date-time" }, + { typeof(IEnumerable), JsonSchemaTypes.String, null }, + { typeof(int[][]), JsonSchemaTypes.Array, null }, + { typeof(IList), null, null }, }; [Theory] [MemberData(nameof(EnumerableTypeData))] public void GenerateSchema_GeneratesArraySchema_IfEnumerableType( Type type, - JsonSchemaType expectedItemsType, - string expectedItemsFormat, - bool expectedItemsNullable) + JsonSchemaType? expectedItemsType, + string? expectedItemsFormat) { var schema = Subject().GenerateSchema(type, new SchemaRepository()); @@ -185,8 +186,8 @@ public void GenerateSchema_GeneratesArraySchema_IfEnumerableType( Assert.NotNull(schema.Items); Assert.Equal(expectedItemsType, schema.Items.Type); Assert.Equal(expectedItemsFormat, schema.Items.Format); - Assert.Equal(expectedItemsNullable, schema.Items.Nullable); } +#nullable restore [Theory] [InlineData(typeof(ISet))] @@ -207,10 +208,11 @@ public void GenerateSchema_GeneratesReferencedArraySchema_IfEnumerableTypeIsSelf var referenceSchema = Subject().GenerateSchema(typeof(ListOfSelf), schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Array, schema.Type); - Assert.Equal(schema.Items.Reference.Id, referenceSchema.Reference.Id); // ref to self + var itemsReference = Assert.IsType(schema.Items); + Assert.Equal(itemsReference.Reference.Id, reference.Reference.Id); // ref to self } [Theory] @@ -227,8 +229,8 @@ public void GenerateSchema_GeneratesReferencedObjectSchema_IfComplexType( var referenceSchema = Subject().GenerateSchema(type, schemaRepository); - Assert.NotNull(referenceSchema.Reference); - Assert.Equal(expectedSchemaId, referenceSchema.Reference.Id); + var reference = Assert.IsType(referenceSchema); + Assert.Equal(expectedSchemaId, reference.Reference.Id); var schema = schemaRepository.Schemas[expectedSchemaId]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.Equal(expectedProperties, schema.Properties.Keys); @@ -242,7 +244,8 @@ public void GenerateSchema_IncludesInheritedProperties_IfComplexTypeIsDerived() var referenceSchema = Subject().GenerateSchema(typeof(SubType1), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.Equal(["BaseProperty", "Property1"], schema.Properties.Keys); } @@ -260,7 +263,8 @@ public void GenerateSchema_IncludesInheritedProperties_IfTypeIsAnInterfaceHierar var referenceSchema = Subject().GenerateSchema(type, schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.Equal(expectedPropertyNames.OrderBy(n => n), schema.Properties.Keys.OrderBy(k => k)); } @@ -272,7 +276,8 @@ public void GenerateSchema_KeepMostDerivedType_IfTypeIsAnInterface() var referenceSchema = Subject().GenerateSchema(typeof(INewBaseInterface), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Integer, schema.Properties["BaseProperty"].Type); } @@ -283,7 +288,8 @@ public void GenerateSchema_ExcludesIndexerProperties_IfComplexTypeIsIndexed() var referenceSchema = Subject().GenerateSchema(typeof(IndexedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); Assert.Equal(["Property1"], schema.Properties.Keys); } @@ -301,8 +307,9 @@ public void GenerateSchema_SetsNullableFlag_IfPropertyIsReferenceOrNullableType( var referenceSchema = Subject().GenerateSchema(declaringType, schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal(expectedNullable, schema.Properties[propertyName].Nullable); + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + Assert.Equal(expectedNullable, schema.Properties[propertyName].Type.Value.HasFlag(JsonSchemaType.Null)); } [Theory] @@ -315,7 +322,7 @@ public void GenerateSchema_SetsNullableFlag_IfPropertyIsReferenceOrNullableType( [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.StringWithDefault), "\"foobar\"")] [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.IntArrayWithDefault), "[\n 1,\n 2,\n 3\n]")] [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.StringArrayWithDefault), "[\n \"foo\",\n \"bar\"\n]")] - [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultNullValue), "null")] + [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultNullValue), null)] [InlineData(typeof(TypeWithDefaultAttributes), nameof(TypeWithDefaultAttributes.NullableIntWithDefaultValue), "2147483647")] public void GenerateSchema_SetsDefault_IfPropertyHasDefaultValueAttribute( Type declaringType, @@ -331,10 +338,10 @@ public void GenerateSchema_SetsDefault_IfPropertyHasDefaultValueAttribute( var referenceSchema = Subject().GenerateSchema(declaringType, schemaRepository); // Assert - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; var propertySchema = schema.Properties[propertyName]; - Assert.NotNull(propertySchema.Default); - Assert.Equal(expectedDefaultAsJson, propertySchema.Default.ToJson()); + Assert.Equal(expectedDefaultAsJson, propertySchema.Default?.ToJson()); } [Fact] @@ -344,7 +351,8 @@ public void GenerateSchema_SetsDeprecatedFlag_IfPropertyHasObsoleteAttribute() var referenceSchema = Subject().GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.True(schema.Properties["ObsoleteProperty"].Deprecated); } @@ -357,7 +365,8 @@ public void GenerateSchema_SetsValidationProperties_IfComplexTypeHasValidationAt var referenceSchema = Subject().GenerateSchema(type, schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal("credit-card", schema.Properties["StringWithDataTypeCreditCard"].Format); Assert.Equal(1, schema.Properties["StringWithMinMaxLength"].MinLength); Assert.Equal(3, schema.Properties["StringWithMinMaxLength"].MaxLength); @@ -367,20 +376,20 @@ public void GenerateSchema_SetsValidationProperties_IfComplexTypeHasValidationAt Assert.Equal(3, schema.Properties["StringWithLength"].MaxLength); Assert.Equal(1, schema.Properties["ArrayWithLength"].MinItems); Assert.Equal(3, schema.Properties["ArrayWithLength"].MaxItems); - Assert.Equal(true, schema.Properties["IntWithExclusiveRange"].ExclusiveMinimum); - Assert.Equal(true, schema.Properties["IntWithExclusiveRange"].ExclusiveMaximum); + Assert.NotNull(schema.Properties["IntWithExclusiveRange"].ExclusiveMinimum); + Assert.NotNull(schema.Properties["IntWithExclusiveRange"].ExclusiveMaximum); Assert.Equal("byte", schema.Properties["StringWithBase64"].Format); - Assert.Equal(JsonSchemaTypes.String, schema.Properties["StringWithBase64"].Type); + Assert.Equal(JsonSchemaTypes.String | JsonSchemaType.Null, schema.Properties["StringWithBase64"].Type); Assert.Null(schema.Properties["IntWithRange"].ExclusiveMinimum); Assert.Null(schema.Properties["IntWithRange"].ExclusiveMaximum); - Assert.Equal(1, schema.Properties["IntWithRange"].Minimum); - Assert.Equal(10, schema.Properties["IntWithRange"].Maximum); + Assert.Equal("1", schema.Properties["IntWithRange"].Minimum); + Assert.Equal("10", schema.Properties["IntWithRange"].Maximum); Assert.Equal("^[3-6]?\\d{12,15}$", schema.Properties["StringWithRegularExpression"].Pattern); Assert.Equal(5, schema.Properties["StringWithStringLength"].MinLength); Assert.Equal(10, schema.Properties["StringWithStringLength"].MaxLength); Assert.Equal(1, schema.Properties["StringWithRequired"].MinLength); - Assert.False(schema.Properties["StringWithRequired"].Nullable); - Assert.False(schema.Properties["StringWithRequiredAllowEmptyTrue"].Nullable); + AssertIsNullable(schema.Properties["StringWithRequired"], false); + AssertIsNullable(schema.Properties["StringWithRequiredAllowEmptyTrue"], false); Assert.Null(schema.Properties["StringWithRequiredAllowEmptyTrue"].MinLength); Assert.Equal(["NullableIntEnumWithRequired", "StringWithRequired", "StringWithRequiredAllowEmptyTrue"], schema.Required); Assert.Equal("Description", schema.Properties[nameof(TypeWithValidationAttributes.StringWithDescription)].Description); @@ -394,7 +403,8 @@ public void GenerateSchema_SetsReadOnlyAndWriteOnlyFlags_IfPropertyIsRestricted( var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRestrictedProperties), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.False(schema.Properties["ReadWriteProperty"].ReadOnly); Assert.False(schema.Properties["ReadWriteProperty"].WriteOnly); Assert.True(schema.Properties["ReadOnlyProperty"].ReadOnly); @@ -422,10 +432,11 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeyword() var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRequiredProperties), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.True(schema.Properties["RequiredString"].Nullable); + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + AssertIsNullable(schema.Properties["RequiredString"]); Assert.Contains("RequiredString", schema.Required.ToArray()); - Assert.False(schema.Properties["RequiredInt"].Nullable); + AssertIsNullable(schema.Properties["RequiredInt"], false); Assert.Contains("RequiredInt", schema.Required.ToArray()); } @@ -436,9 +447,10 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeywordAndValidatio var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRequiredPropertyAndValidationAttribute), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(1, schema.Properties["RequiredProperty"].MinLength); - Assert.True(schema.Properties["RequiredProperty"].Nullable); + AssertIsNullable(schema.Properties["RequiredProperty"]); Assert.Equal(["RequiredProperty"], schema.Required); } @@ -456,10 +468,18 @@ public void GenerateSchema_SetsRequiredAndNullable_IfPropertyHasRequiredKeywordA var referenceSchema = Subject(configureGenerator: (c) => c.SupportNonNullableReferenceTypes = true).GenerateSchema(typeof(TypeWithNullableReferenceTypes), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.True(schema.Properties["RequiredNullableString"].Nullable); + var reference = Assert.IsType(referenceSchema); + + Assert.NotNull(reference); + Assert.NotNull(reference.Reference); + Assert.NotNull(reference.Reference.Id); + + var schema = schemaRepository.Schemas[reference.Reference.Id]; + Assert.NotNull(schema.Properties); + AssertIsNullable(schema.Properties["RequiredNullableString"]); + Assert.NotNull(schema.Required); Assert.Contains("RequiredNullableString", schema.Required.ToArray()); - Assert.False(schema.Properties["RequiredNonNullableString"].Nullable); + AssertIsNullable(schema.Properties["RequiredNonNullableString"], false); Assert.Contains("RequiredNonNullableString", schema.Required.ToArray()); } #nullable disable @@ -478,7 +498,8 @@ bool expectedReadOnly var referenceSchema = Subject().GenerateSchema(type, schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(expectedReadOnly, schema.Properties[propertyName].ReadOnly); } @@ -512,7 +533,7 @@ public void GenerateSchema_SupportsOption_CustomTypeMappings( var schema = subject.GenerateSchema(type, new SchemaRepository()); Assert.Equal(JsonSchemaTypes.String, schema.Type); - Assert.Empty(schema.Properties); + Assert.Null(schema.Properties); } [Theory] @@ -529,10 +550,14 @@ public void GenerateSchema_SupportsOption_SchemaFilters(Type type) var schema = subject.GenerateSchema(type, schemaRepository); - var definitionSchema = schema.Reference == null ? schema : schemaRepository.Schemas[schema.Reference.Id]; - Assert.Contains("X-foo", definitionSchema.Extensions.Keys); + if (schema is OpenApiSchemaReference reference) + { + schema = schemaRepository.Schemas[reference.Reference.Id]; + } + + Assert.Contains("X-foo", schema.Extensions.Keys); - Assert.Equal("v1", ((OpenApiString)definitionSchema.Extensions["X-docName"]).Value); + Assert.Equal("v1", ((JsonNodeExtension)schema.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -545,7 +570,8 @@ public void GenerateSchema_SupportsOption_IgnoreObsoleteProperties() var referenceSchema = subject.GenerateSchema(typeof(TypeWithObsoleteAttribute), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.DoesNotContain("ObsoleteProperty", schema.Properties.Keys); } @@ -559,8 +585,9 @@ public void GenerateSchema_SupportsOption_SchemaIdSelector() var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); - Assert.Equal("Swashbuckle.AspNetCore.TestSupport.ComplexType", referenceSchema.Reference.Id); - Assert.Contains(referenceSchema.Reference.Id, schemaRepository.Schemas.Keys); + var reference = Assert.IsType(referenceSchema); + Assert.Equal("Swashbuckle.AspNetCore.TestSupport.ComplexType", reference.Reference.Id); + Assert.Contains(reference.Reference.Id, schemaRepository.Schemas.Keys); } [Fact] @@ -573,16 +600,17 @@ public void GenerateSchema_SupportsOption_UseAllOfForInheritance() var referenceSchema = subject.GenerateSchema(typeof(SubType1), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.NotNull(schema.AllOf); Assert.Equal(2, schema.AllOf.Count); var baseSchema = schema.AllOf[0]; - Assert.Equal("BaseType", baseSchema.Reference.Id); - Assert.NotNull(baseSchema.Reference); + reference = Assert.IsType(baseSchema); + Assert.Equal("BaseType", reference.Reference.Id); var subSchema = schema.AllOf[1]; Assert.Equal(["Property1"], subSchema.Properties.Keys); // The base type schema - var baseTypeSchema = schemaRepository.Schemas[baseSchema.Reference.Id]; + var baseTypeSchema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, baseTypeSchema.Type); Assert.Equal(["BaseProperty"], baseTypeSchema.Properties.Keys); } @@ -639,7 +667,8 @@ public void GenerateSchema_SupportsOption_DiscriminatorNameSelector() var referenceSchema = subject.GenerateSchema(typeof(BaseType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Contains("TypeName", schema.Properties.Keys); Assert.Contains("TypeName", schema.Required); Assert.NotNull(schema.Discriminator); @@ -660,28 +689,31 @@ public void GenerateSchema_SupportsOption_UseAllOfForPolymorphism() // The polymorphic schema Assert.NotNull(schema.OneOf); Assert.Equal(3, schema.OneOf.Count); + // The base type schema - Assert.NotNull(schema.OneOf[0].Reference); - var baseSchema = schemaRepository.Schemas[schema.OneOf[0].Reference.Id]; + var reference = Assert.IsType(schema.OneOf[0]); + var baseSchema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, baseSchema.Type); Assert.Equal(["BaseProperty"], baseSchema.Properties.Keys); + // The first sub type schema - Assert.NotNull(schema.OneOf[1].Reference); - var subType1Schema = schemaRepository.Schemas[schema.OneOf[1].Reference.Id]; + reference = Assert.IsType(schema.OneOf[1]); + var subType1Schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, subType1Schema.Type); Assert.NotNull(subType1Schema.AllOf); var allOf = Assert.Single(subType1Schema.AllOf); - Assert.NotNull(allOf.Reference); - Assert.Equal("BaseType", allOf.Reference.Id); + reference = Assert.IsType(allOf); + Assert.Equal("BaseType", reference.Reference.Id); Assert.Equal(["Property1"], subType1Schema.Properties.Keys); + // The second sub type schema - Assert.NotNull(schema.OneOf[2].Reference); - var subType2Schema = schemaRepository.Schemas[schema.OneOf[2].Reference.Id]; + reference = Assert.IsType(schema.OneOf[2]); + var subType2Schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, subType2Schema.Type); Assert.NotNull(subType2Schema.AllOf); allOf = Assert.Single(subType2Schema.AllOf); - Assert.NotNull(allOf.Reference); - Assert.Equal("BaseType", allOf.Reference.Id); + reference = Assert.IsType(allOf); + Assert.Equal("BaseType", reference.Reference.Id); Assert.Equal(["Property2"], subType2Schema.Properties.Keys); } @@ -695,7 +727,7 @@ public void GenerateSchema_SupportsOption_UseAllOfToExtendReferenceSchemas() var schema = subject.GenerateSchema(propertyInfo.PropertyType, new SchemaRepository(), memberInfo: propertyInfo); - Assert.Null(schema.Reference); + Assert.IsNotType(schema); Assert.NotNull(schema.AllOf); Assert.Single(schema.AllOf); } @@ -784,8 +816,9 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes( var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - Assert.Equal(expectedNullable, propertySchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + AssertIsNullable(propertySchema, expectedNullable); } [Theory] @@ -810,10 +843,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -838,10 +872,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -866,10 +901,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -894,10 +930,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -922,10 +959,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -950,10 +988,11 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla var referenceSchema = subject.GenerateSchema(declaringType, schemaRepository); - var propertySchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName]; - var contentSchema = schemaRepository.Schemas[referenceSchema.Reference.Id].Properties[propertyName].AdditionalProperties; - Assert.Equal(expectedNullableProperty, propertySchema.Nullable); - Assert.Equal(expectedNullableContent, contentSchema.Nullable); + var reference = Assert.IsType(referenceSchema); + var propertySchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName]; + var contentSchema = schemaRepository.Schemas[reference.Reference.Id].Properties[propertyName].AdditionalProperties; + AssertIsNullable(propertySchema, expectedNullableProperty); + AssertIsNullable(contentSchema, expectedNullableContent); } [Theory] @@ -975,7 +1014,7 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypesInDict subject.GenerateSchema(declaringType, schemaRepository); var propertySchema = schemaRepository.Schemas[subType].Properties[propertyName]; - Assert.Equal(expectedNullable, propertySchema.Nullable); + AssertIsNullable(propertySchema, expectedNullable); } [Theory] @@ -1036,7 +1075,7 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Neste subject.GenerateSchema(declaringType, schemaRepository); var propertySchema = schemaRepository.Schemas[subType].Properties[propertyName]; - Assert.Equal(expectedNullable, propertySchema.Nullable); + AssertIsNullable(propertySchema, expectedNullable); } [Theory] @@ -1069,9 +1108,11 @@ public void GenerateSchema_HandlesTypesWithNestedTypes() var referenceSchema = Subject().GenerateSchema(typeof(ContainingType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.Object, schema.Type); - Assert.Equal("NestedType", schema.Properties["Property1"].Reference.Id); + reference = Assert.IsType(schema.Properties["Property1"]); + Assert.Equal("NestedType", reference.Reference.Id); } [Fact] @@ -1096,8 +1137,10 @@ public void GenerateSchema_HandlesRecursion_IfCalledAgainWithinAFilter() var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal("ComplexType", schema.Properties["Self"].Reference.Id); + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; + reference = Assert.IsType(schema.Properties["Self"]); + Assert.Equal("ComplexType", reference.Reference.Id); } [Fact] @@ -1124,8 +1167,8 @@ public void GenerateSchema_HonorsSerializerOption_IgnoreReadonlyProperties() var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; } [Fact] @@ -1139,8 +1182,8 @@ public void GenerateSchema_HonorsSerializerOption_PropertyNamingPolicy() var referenceSchema = subject.GenerateSchema(typeof(ComplexType), schemaRepository); - Assert.NotNull(referenceSchema.Reference); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(["property1", "property2"], schema.Properties.Keys); } @@ -1160,7 +1203,8 @@ public void GenerateSchema_HonorsSerializerOption_StringEnumConverter( var referenceSchema = subject.GenerateSchema(typeof(TypeWithDefaultAttributeOnEnum), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; var propertySchema = schema.Properties[nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault)]; Assert.Equal(JsonSchemaTypes.String, propertySchema.Type); Assert.Equal(expectedEnumAsJson, propertySchema.Enum.Select(openApiAny => openApiAny.ToJson())); @@ -1185,7 +1229,8 @@ public void GenerateSchema_HonorsSerializerAttribute_StringEnumConverter() var referenceSchema = Subject().GenerateSchema(typeof(JsonConverterAnnotatedEnum), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(JsonSchemaTypes.String, schema.Type); Assert.Equal(["\"Value1\"", "\"Value2\"", "\"X\""], schema.Enum.Select(openApiAny => openApiAny.ToJson())); } @@ -1197,7 +1242,8 @@ public void GenerateSchema_HonorsSerializerAttributes_JsonIgnore() var referenceSchema = Subject().GenerateSchema(typeof(JsonIgnoreAnnotatedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; string[] expectedKeys = [ @@ -1217,7 +1263,8 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonPropertyName() var referenceSchema = Subject().GenerateSchema(typeof(JsonPropertyNameAnnotatedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(["string-with-json-property-name"], schema.Properties.Keys); } @@ -1228,9 +1275,10 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonRequired() var referenceSchema = Subject().GenerateSchema(typeof(JsonRequiredAnnotatedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.Equal(["StringWithJsonRequired"], schema.Required); - Assert.True(schema.Properties["StringWithJsonRequired"].Nullable); + AssertIsNullable(schema.Properties["StringWithJsonRequired"]); } [Fact] @@ -1240,7 +1288,8 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonExtensionData() var referenceSchema = Subject().GenerateSchema(typeof(JsonExtensionDataAnnotatedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.True(schema.AdditionalPropertiesAllowed); Assert.NotNull(schema.AdditionalProperties); Assert.Null(schema.AdditionalProperties.Type); @@ -1253,7 +1302,8 @@ public void GenerateSchema_HonorsAttribute_SwaggerIgnore() var referenceSchema = Subject().GenerateSchema(typeof(SwaggerIngoreAnnotatedType), schemaRepository); - var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; + var reference = Assert.IsType(referenceSchema); + var schema = schemaRepository.Schemas[reference.Reference.Id]; Assert.True(schema.Properties.ContainsKey(nameof(SwaggerIngoreAnnotatedType.NotIgnoredString))); Assert.False(schema.Properties.ContainsKey(nameof(SwaggerIngoreAnnotatedType.IgnoredString))); @@ -1270,7 +1320,7 @@ public void GenerateSchema_GeneratesOpenSchema_IfDynamicJsonType(Type type) { var schema = Subject().GenerateSchema(type, new SchemaRepository()); - Assert.Null(schema.Reference); + Assert.IsNotType(schema); Assert.Null(schema.Type); } @@ -1349,13 +1399,9 @@ private static SchemaGenerator Subject( return new SchemaGenerator(generatorOptions, new JsonSerializerDataContractResolver(serializerOptions)); } - private static SchemaGenerator Subject(Action configureGenerator) + private static void AssertIsNullable(IOpenApiSchema schema, bool expected = true) { - var generatorOptions = new SchemaGeneratorOptions(); - configureGenerator?.Invoke(generatorOptions); - - var serializerOptions = new JsonSerializerOptions(); - - return new SchemaGenerator(generatorOptions, new JsonSerializerDataContractResolver(serializerOptions)); + Assert.True(schema.Type.HasValue); + Assert.Equal(expected, schema.Type.Value.HasFlag(JsonSchemaType.Null)); } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiAnyFactoryTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiAnyFactoryTests.cs deleted file mode 100644 index 983e881882..0000000000 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiAnyFactoryTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -namespace Swashbuckle.AspNetCore.SwaggerGen.Test; - -using System; -using System.Text.Json; -using Microsoft.OpenApi.Any; -using Xunit; - -public class OpenApiAnyFactoryTests -{ - [Theory] - [InlineData("1", typeof(OpenApiInteger), 1)] - [InlineData("4294877294", typeof(OpenApiLong), 4294877294L)] - [InlineData("1.5", typeof(OpenApiFloat), 1.5f)] - [InlineData("1.5e308", typeof(OpenApiDouble), 1.5e308)] - [InlineData("\"abc\"", typeof(OpenApiString), "abc")] - [InlineData("true", typeof(OpenApiBoolean), true)] - [InlineData("false", typeof(OpenApiBoolean), false)] - public void CreateFromJson_SimpleType(string json, Type expectedType, object expectedValue) - { - var openApiAnyObject = OpenApiAnyFactory.CreateFromJson(json); - Assert.NotNull(openApiAnyObject); - Assert.Equal(expectedType, openApiAnyObject.GetType()); - Assert.Equal(AnyType.Primitive, openApiAnyObject.AnyType); - var valueProperty = expectedType.GetProperty("Value"); - var actualValue = valueProperty.GetValue(openApiAnyObject); - Assert.Equal(expectedValue, actualValue); - } - - [Fact] - public void CreateFromJson_NullType() - { - var expectedType = typeof(OpenApiNull); - - var openApiAnyObject = OpenApiAnyFactory.CreateFromJson("null"); - Assert.NotNull(openApiAnyObject); - Assert.Equal(expectedType, openApiAnyObject.GetType()); - Assert.Equal(AnyType.Null, openApiAnyObject.AnyType); - var valueProperty = expectedType.GetProperty("Value"); - Assert.Null(valueProperty); - } - - [Theory] - [InlineData("[1,2]", typeof(OpenApiInteger), 1, 2)] - [InlineData("[4294877294,4294877295]", typeof(OpenApiLong), 4294877294L, 4294877295L)] - [InlineData("[1.5,-1.5]", typeof(OpenApiFloat), 1.5f, -1.5f)] - [InlineData("[1.5e308,-1.5e308]", typeof(OpenApiDouble), 1.5e308, -1.5e308)] - [InlineData("[\"abc\",\"def\"]", typeof(OpenApiString), "abc", "def")] - [InlineData("[true,false]", typeof(OpenApiBoolean), true, false)] - [InlineData("[{\"a\":1,\"b\":2},{\"a\":3,\"b\":4}]", typeof(OpenApiObject))] - [InlineData("[[1,2],[3,4]]", typeof(OpenApiArray))] - public void CreateFromJson_Array(string json, Type expectedType, params object[] expectedValues) - { - var openApiAnyObject = OpenApiAnyFactory.CreateFromJson(json); - Assert.NotNull(openApiAnyObject); - Assert.Equal(typeof(OpenApiArray), openApiAnyObject.GetType()); - Assert.Equal(AnyType.Array, openApiAnyObject.AnyType); - var array = (OpenApiArray)openApiAnyObject; - for (var i = 0; i < array.Count; i++) - { - Assert.NotNull(array[i]); - Assert.Equal(expectedType, array[i].GetType()); - if (expectedValues.Length > 0) - { - var valueProperty = expectedType.GetProperty("Value"); - var expectedValue = expectedValues[i]; - var actualValue = valueProperty.GetValue(array[i]); - Assert.Equal(expectedValue, actualValue); - } - } - } - - [Fact] - public void CreateFromJson_Object() - { - var json = JsonSerializer.Serialize(new - { - int_value = 1, - long_value = 4294877294L, - float_value = 1.5f, - double_value = 1.5e308, - string_value = "abc", - true_value = true, - false_value = false, - array_value = new[] {1,2}, - object_value = new - { - a = 1, - b = 2 - } - }); - - var openApiAnyObject = OpenApiAnyFactory.CreateFromJson(json); - Assert.NotNull(openApiAnyObject); - Assert.Equal(typeof(OpenApiObject), openApiAnyObject.GetType()); - Assert.Equal(AnyType.Object, openApiAnyObject.AnyType); - var obj = (OpenApiObject)openApiAnyObject; - - Assert.NotNull(obj["int_value"]); - Assert.Equal(typeof(OpenApiInteger), obj["int_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["int_value"].AnyType); - Assert.Equal(1, ((OpenApiInteger) obj["int_value"]).Value); - - Assert.NotNull(obj["long_value"]); - Assert.Equal(typeof(OpenApiLong), obj["long_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["long_value"].AnyType); - Assert.Equal(4294877294L, ((OpenApiLong)obj["long_value"]).Value); - - Assert.NotNull(obj["float_value"]); - Assert.Equal(typeof(OpenApiFloat), obj["float_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["float_value"].AnyType); - Assert.Equal(1.5f, ((OpenApiFloat)obj["float_value"]).Value); - - Assert.NotNull(obj["double_value"]); - Assert.Equal(typeof(OpenApiDouble), obj["double_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["double_value"].AnyType); - Assert.Equal(1.5e308, ((OpenApiDouble)obj["double_value"]).Value); - - Assert.NotNull(obj["string_value"]); - Assert.Equal(typeof(OpenApiString), obj["string_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["string_value"].AnyType); - Assert.Equal("abc", ((OpenApiString)obj["string_value"]).Value); - - Assert.NotNull(obj["true_value"]); - Assert.Equal(typeof(OpenApiBoolean), obj["true_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["true_value"].AnyType); - Assert.True(((OpenApiBoolean)obj["true_value"]).Value); - - Assert.NotNull(obj["false_value"]); - Assert.Equal(typeof(OpenApiBoolean), obj["false_value"].GetType()); - Assert.Equal(AnyType.Primitive, obj["false_value"].AnyType); - Assert.False(((OpenApiBoolean)obj["false_value"]).Value); - - Assert.NotNull(obj["array_value"]); - Assert.Equal(typeof(OpenApiArray), obj["array_value"].GetType()); - Assert.Equal(AnyType.Array, obj["array_value"].AnyType); - - Assert.NotNull(obj["object_value"]); - Assert.Equal(typeof(OpenApiObject), obj["object_value"].GetType()); - Assert.Equal(AnyType.Object, obj["object_value"].AnyType); - } -} diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiSchemaExtensionsTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiSchemaExtensionsTests.cs index e75b8f65df..f01aab9e26 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiSchemaExtensionsTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/OpenApiSchemaExtensionsTests.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Globalization; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -92,9 +92,6 @@ public static void ApplyValidationAttributes_Handles_RangeAttribute_Correctly( string expectedMaximum) { // Arrange - var minimum = decimal.Parse(expectedMinimum, CultureInfo.InvariantCulture); - var maximum = decimal.Parse(expectedMaximum, CultureInfo.InvariantCulture); - var schema = new OpenApiSchema(); // Act @@ -104,10 +101,20 @@ public static void ApplyValidationAttributes_Handles_RangeAttribute_Correctly( } // Assert - Assert.Equal(isExclusive ? true : null, schema.ExclusiveMinimum); - Assert.Equal(isExclusive ? true : null, schema.ExclusiveMaximum); - Assert.Equal(minimum, schema.Minimum); - Assert.Equal(maximum, schema.Maximum); + if (isExclusive) + { + Assert.Equal(expectedMinimum, schema.ExclusiveMinimum); + Assert.Equal(expectedMaximum, schema.ExclusiveMaximum); + Assert.Null(schema.Minimum); + Assert.Null(schema.Maximum); + } + else + { + Assert.Equal(expectedMinimum, schema.Minimum); + Assert.Equal(expectedMaximum, schema.Maximum); + Assert.Null(schema.ExclusiveMinimum); + Assert.Null(schema.ExclusiveMaximum); + } } [Fact] diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs index ccde891856..a48e8afa91 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs @@ -9,8 +9,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Routing; -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures; using Swashbuckle.AspNetCore.TestSupport; @@ -53,14 +52,14 @@ public void GetSwagger_GeneratesSwaggerDocument_ForApiDescriptionsWithMatchingGr Assert.Equal("V1", document.Info.Version); Assert.Equal("Test API", document.Info.Title); Assert.Equal(["/resource"], [.. document.Paths.Keys]); - Assert.Equal([OperationType.Post, OperationType.Get], document.Paths["/resource"].Operations.Keys); + Assert.Equal([HttpMethod.Post, HttpMethod.Get], document.Paths["/resource"].Operations.Keys); Assert.Equal(2, document.Paths["/resource"].Operations.Count); var documentV2 = subject.GetSwagger("v2"); Assert.Equal("V2", documentV2.Info.Version); Assert.Equal("Test API 2", documentV2.Info.Title); Assert.Equal(["/resource"], [.. documentV2.Paths.Keys]); - Assert.Equal([OperationType.Post], documentV2.Paths["/resource"].Operations.Keys); + Assert.Equal([HttpMethod.Post], documentV2.Paths["/resource"].Operations.Keys); Assert.Single(documentV2.Paths["/resource"].Operations); } @@ -113,7 +112,7 @@ public void GetSwagger_SetsOperationIdToNull_ByDefault() var document = subject.GetSwagger("v1"); - Assert.Null(document.Paths["/resource"].Operations[OperationType.Post].OperationId); + Assert.Null(document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); } [Fact] @@ -129,7 +128,7 @@ public void GetSwagger_SetsOperationIdToRouteName_IfActionHasRouteNameMetadata() var document = subject.GetSwagger("v1"); - Assert.Equal("SomeRouteName", document.Paths["/resource"].Operations[OperationType.Post].OperationId); + Assert.Equal("SomeRouteName", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); } [Fact] @@ -153,7 +152,7 @@ public void GetSwagger_SetsOperationIdToEndpointName_IfActionHasEndpointNameMeta var document = subject.GetSwagger("v1"); - Assert.Equal("SomeEndpointName", document.Paths["/resource"].Operations[OperationType.Post].OperationId); + Assert.Equal("SomeEndpointName", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); } [Fact] @@ -190,8 +189,8 @@ public void GetSwagger_UseProvidedOpenApiOperation_IfExistsInMetadata() var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - Assert.Equal("ParameterInMetadata", document.Paths["/resource"].Operations[OperationType.Post].Parameters[0].Name); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + Assert.Equal("ParameterInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].Parameters[0].Name); } [Fact] @@ -244,12 +243,12 @@ public void GetSwagger_GenerateProducesSchemas_ForProvidedOpenApiOperation() var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - var content = Assert.Single(document.Paths["/resource"].Operations[OperationType.Post].Responses["200"].Content); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + var content = Assert.Single(document.Paths["/resource"].Operations[HttpMethod.Post].Responses["200"].Content); Assert.Equal("application/someMediaType", content.Key); Assert.Null(content.Value.Schema.Type); - Assert.NotNull(content.Value.Schema.Reference); - Assert.Equal("TestDto", content.Value.Schema.Reference.Id); + var reference = Assert.IsType(content.Value.Schema); + Assert.Equal("TestDto", reference.Reference.Id); } [Fact] @@ -324,24 +323,24 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationAndApp var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal("OperationIdSetInMetadata", operation.OperationId); var content = Assert.Single(operation.RequestBody.Content); Assert.Equal("application/someMediaType", content.Key); Assert.Null(content.Value.Schema.Type); - Assert.NotNull(content.Value.Schema.Reference); - Assert.Equal("TestDto", content.Value.Schema.Reference.Id); + var reference = Assert.IsType(content.Value.Schema); + Assert.Equal("TestDto", reference.Reference.Id); Assert.Equal(2, operation.RequestBody.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.RequestBody.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.RequestBody.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.RequestBody.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.RequestBody.Extensions["X-docName"]).Node.GetValue()); Assert.NotEmpty(operation.Parameters); Assert.Equal("paramQuery", operation.Parameters[0].Name); Assert.Equal(2, operation.Parameters[0].Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Parameters[0].Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Parameters[0].Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -392,10 +391,10 @@ public void GetSwagger_GenerateParametersSchemas_ForProvidedOpenApiOperation() var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - Assert.Equal("ParameterInMetadata", document.Paths["/resource"].Operations[OperationType.Post].Parameters[0].Name); - Assert.NotNull(document.Paths["/resource"].Operations[OperationType.Post].Parameters[0].Schema); - Assert.Equal(JsonSchemaTypes.String, document.Paths["/resource"].Operations[OperationType.Post].Parameters[0].Schema.Type); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + Assert.Equal("ParameterInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].Parameters[0].Name); + Assert.NotNull(document.Paths["/resource"].Operations[HttpMethod.Post].Parameters[0].Schema); + Assert.Equal(JsonSchemaTypes.String, document.Paths["/resource"].Operations[HttpMethod.Post].Parameters[0].Schema.Type); } [Fact] @@ -419,7 +418,7 @@ public void GetSwagger_SetsOperationIdToNull_IfActionHasNoEndpointMetadata() var document = subject.GetSwagger("v1"); - Assert.Null(document.Paths["/resource"].Operations[OperationType.Post].OperationId); + Assert.Null(document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); } [Fact] @@ -435,7 +434,7 @@ public void GetSwagger_SetsDeprecated_IfActionHasObsoleteAttribute() var document = subject.GetSwagger("v1"); - Assert.True(document.Paths["/resource"].Operations[OperationType.Post].Deprecated); + Assert.True(document.Paths["/resource"].Operations[HttpMethod.Post].Deprecated); } [Theory] @@ -468,7 +467,7 @@ public void GetSwagger_GeneratesParameters_ForApiParametersThatAreNotBoundToBody var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(expectedParameterLocation, parameter.In); } @@ -518,7 +517,7 @@ public void GetSwagger_IgnoresParameters_IfActionParameterHasBindNeverAttribute( var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Empty(operation.Parameters); } @@ -546,7 +545,7 @@ public void GetSwagger_IgnoresParameters_IfActionParameterHasSwaggerIgnoreAttrib var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Empty(operation.Parameters); } @@ -586,7 +585,7 @@ public void GetSwagger_IgnoresParameters_IfActionParameterIsIllegalHeaderParamet var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Get]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal("param", parameter.Name); } @@ -656,7 +655,7 @@ public void GetSwagger_GenerateParametersSchemas_IfActionParameterIsIllegalHeade var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Get]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Get]; Assert.Null(operation.Parameters.Single(p => p.Name == illegalParameterName).Schema); Assert.NotNull(operation.Parameters.Single(p => p.Name == "param").Schema); } @@ -685,7 +684,7 @@ public void GetSwagger_SetsParameterRequired_IfApiParameterIsBoundToPath() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.True(operation.Parameters.First().Required); } @@ -718,7 +717,7 @@ public void GetSwagger_SetsParameterRequired_IfActionParameterHasRequiredOrBindR var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(expectedRequired, parameter.Required); } @@ -748,7 +747,7 @@ public void GetSwagger_SetsParameterRequired_IfActionParameterHasRequiredMember( var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.True(parameter.Required); } @@ -788,7 +787,7 @@ static void Execute(object obj) { } var document = subject.GetSwagger("v1"); - Assert.Equal(isRequired, document.Paths["/resource"].Operations[OperationType.Post].RequestBody.Required); + Assert.Equal(isRequired, document.Paths["/resource"].Operations[HttpMethod.Post].RequestBody.Required); } [Fact] @@ -815,7 +814,7 @@ public void GetSwagger_SetsParameterTypeToString_IfApiParameterHasNoCorrespondin var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(JsonSchemaTypes.String, parameter.Schema.Type); } @@ -848,7 +847,7 @@ public void GetSwagger_GeneratesRequestBody_ForFirstApiParameterThatIsBoundToBod var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(["application/json"], operation.RequestBody.Content.Keys); var mediaType = operation.RequestBody.Content["application/json"]; @@ -888,7 +887,7 @@ public void GetSwagger_SetsRequestBodyRequired_IfActionParameterHasRequiredOrBin var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(expectedRequired, operation.RequestBody.Required); } @@ -923,7 +922,7 @@ public void GetSwagger_GeneratesRequestBody_ForApiParametersThatAreBoundToForm() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(["multipart/form-data"], operation.RequestBody.Content.Keys); var mediaType = operation.RequestBody.Content["multipart/form-data"]; @@ -959,7 +958,7 @@ public void GetSwagger_SetsRequestBodyContentTypesFromAttribute_IfActionHasConsu var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(["application/someMediaType"], operation.RequestBody.Content.Keys); } @@ -1004,7 +1003,7 @@ public void GetSwagger_GeneratesResponses_ForSupportedResponseTypes() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(["200", "400", "422", "default"], operation.Responses.Keys); var response200 = operation.Responses["200"]; Assert.Equal("OK", response200.Description); @@ -1047,7 +1046,7 @@ public void GetSwagger_SetsResponseContentType_WhenActionHasFileResult() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var content = operation.Responses["200"].Content.FirstOrDefault(); Assert.Equal("application/zip", content.Key); Assert.Equal("binary", content.Value.Schema.Format); @@ -1078,7 +1077,7 @@ public void GetSwagger_SetsResponseContentTypesFromAttribute_IfActionHasProduces var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(["application/someMediaType"], operation.Responses["200"].Content.Keys); } @@ -1195,7 +1194,7 @@ public void GetSwagger_SupportsOption_IgnoreObsoleteActions() var document = subject.GetSwagger("v1"); Assert.Equal(["/resource"], [.. document.Paths.Keys]); - Assert.Equal([OperationType.Post], document.Paths["/resource"].Operations.Keys); + Assert.Equal([HttpMethod.Post], document.Paths["/resource"].Operations.Keys); Assert.Single(document.Paths["/resource"].Operations); } @@ -1253,7 +1252,7 @@ public void GetSwagger_SupportsOption_TagSelector() var document = subject.GetSwagger("v1"); - Assert.Equal(["resource"], [.. document.Paths["/resource"].Operations[OperationType.Post].Tags?.Select(t => t.Name)]); + Assert.Equal(["resource"], [.. document.Paths["/resource"].Operations[HttpMethod.Post].Tags?.Select(t => t.Reference.Id)]); } [Fact] @@ -1277,7 +1276,7 @@ public void GetSwagger_CanReadTagsFromMetadata() var document = subject.GetSwagger("v1"); - Assert.Equal(["Some", "Tags", "Here"], [.. document.Paths["/resource"].Operations[OperationType.Post].Tags?.Select(t => t.Name)]); + Assert.Equal(["Some", "Tags", "Here"], [.. document.Paths["/resource"].Operations[HttpMethod.Post].Tags?.Select(t => t.Reference.Id)]); } [Fact] @@ -1301,7 +1300,7 @@ public void GetSwagger_CanReadEndpointSummaryFromMetadata() var document = subject.GetSwagger("v1"); - Assert.Equal("A Test Summary", document.Paths["/resource"].Operations[OperationType.Post].Summary); + Assert.Equal("A Test Summary", document.Paths["/resource"].Operations[HttpMethod.Post].Summary); } [Fact] @@ -1325,7 +1324,7 @@ public void GetSwagger_CanReadEndpointDescriptionFromMetadata() var document = subject.GetSwagger("v1"); - Assert.Equal("A Test Description", document.Paths["/resource"].Operations[OperationType.Post].Description); + Assert.Equal("A Test Description", document.Paths["/resource"].Operations[HttpMethod.Post].Description); } [Fact] @@ -1353,7 +1352,7 @@ public void GetSwagger_SupportsOption_ConflictingActionsResolver() var document = subject.GetSwagger("v1"); Assert.Equal(["/resource"], [.. document.Paths.Keys]); - Assert.Equal([OperationType.Post], document.Paths["/resource"].Operations.Keys); + Assert.Equal([HttpMethod.Post], document.Paths["/resource"].Operations.Keys); Assert.Single(document.Paths["/resource"].Operations); } @@ -1396,7 +1395,7 @@ public void GetSwagger_SupportsOption_DescribeAllParametersInCamelCase( var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(expectedOpenApiParameterName, parameter.Name); } @@ -1465,7 +1464,7 @@ public void GetSwagger_SupportsOption_DescribeAllParametersInCamelCase_ForParame var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(expectedOpenApiParameterName, parameter.Name); } @@ -1505,7 +1504,7 @@ public void GetSwagger_SupportsOption_SecuritySchemes() { ["v1"] = new OpenApiInfo { Version = "V1", Title = "Test API" } }, - SecuritySchemes = new Dictionary + SecuritySchemes = new Dictionary { ["basic"] = new OpenApiSecurityScheme { Type = SecuritySchemeType.Http, Scheme = "basic" } } @@ -1518,7 +1517,7 @@ public void GetSwagger_SupportsOption_SecuritySchemes() } [Theory] - [InlineData(false, new string[] { })] + [InlineData(false, null)] [InlineData(true, new string[] { "Bearer" })] public async Task GetSwagger_SupportsOption_InferSecuritySchemes( bool inferSecuritySchemes, @@ -1543,11 +1542,11 @@ public async Task GetSwagger_SupportsOption_InferSecuritySchemes( var document = await subject.GetSwaggerAsync("v1"); - Assert.Equal(expectedSecuritySchemeNames, document.Components.SecuritySchemes.Keys); + Assert.Equal(expectedSecuritySchemeNames, document.Components.SecuritySchemes?.Keys); } [Theory] - [InlineData(false, new string[] { })] + [InlineData(false, null)] [InlineData(true, new string[] { "Bearer", "Cookies" })] public async Task GetSwagger_SupportsOption_SecuritySchemesSelector( bool inferSecuritySchemes, @@ -1571,13 +1570,13 @@ public async Task GetSwagger_SupportsOption_SecuritySchemesSelector( authenticationSchemes .ToDictionary( (authScheme) => authScheme.Name, - (authScheme) => new OpenApiSecurityScheme()) + (authScheme) => new OpenApiSecurityScheme() as IOpenApiSecurityScheme) } ); var document = await subject.GetSwaggerAsync("v1"); - Assert.Equal(expectedSecuritySchemeNames, document.Components.SecuritySchemes.Keys); + Assert.Equal(expectedSecuritySchemeNames, document.Components.SecuritySchemes?.Keys); } [Fact] @@ -1611,11 +1610,10 @@ public void GetSwagger_SupportsOption_ParameterFilters() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Parameters[0].Extensions.Count); - - Assert.Equal("bar", ((OpenApiString)operation.Parameters[0].Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Parameters[0].Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1649,11 +1647,11 @@ public void GetSwagger_SupportsOption_RequestBodyFilters() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.RequestBody.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.RequestBody.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.RequestBody.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.RequestBody.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.RequestBody.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1680,11 +1678,11 @@ public void GetSwagger_SupportsOption_OperationFilters() var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1710,8 +1708,8 @@ public void GetSwagger_SupportsOption_DocumentFilters() Assert.Equal(2, document.Extensions.Count); Assert.Contains("ComplexType", document.Components.Schemas.Keys); - Assert.Equal("bar", ((OpenApiString)document.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)document.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)document.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)document.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1738,11 +1736,11 @@ public async Task GetSwaggerAsync_SupportsOption_OperationFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1769,11 +1767,11 @@ public async Task GetSwaggerAsync_SupportsOption_OperationAsyncFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1799,8 +1797,8 @@ public async Task GetSwaggerAsync_SupportsOption_DocumentAsyncFilters() Assert.Equal(2, document.Extensions.Count); Assert.Contains("ComplexType", document.Components.Schemas.Keys); - Assert.Equal("bar", ((OpenApiString)document.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)document.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)document.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)document.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1826,8 +1824,8 @@ public async Task GetSwaggerAsync_SupportsOption_DocumentFilters() Assert.Equal(2, document.Extensions.Count); Assert.Contains("ComplexType", document.Components.Schemas.Keys); - Assert.Equal("bar", ((OpenApiString)document.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)document.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)document.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)document.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1861,11 +1859,11 @@ public async Task GetSwaggerAsync_SupportsOption_RequestBodyAsyncFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.RequestBody.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.RequestBody.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.RequestBody.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.RequestBody.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.RequestBody.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1899,11 +1897,11 @@ public async Task GetSwaggerAsync_SupportsOption_RequestBodyFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.RequestBody.Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.RequestBody.Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.RequestBody.Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.RequestBody.Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.RequestBody.Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1937,11 +1935,11 @@ public async Task GetSwaggerAsync_SupportsOption_ParameterFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Parameters[0].Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Parameters[0].Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Parameters[0].Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-docName"]).Node.GetValue()); } [Fact] @@ -1975,11 +1973,11 @@ public async Task GetSwaggerAsync_SupportsOption_ParameterAsyncFilters() var document = await subject.GetSwaggerAsync("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal(2, operation.Parameters[0].Extensions.Count); - Assert.Equal("bar", ((OpenApiString)operation.Parameters[0].Extensions["X-foo"]).Value); - Assert.Equal("v1", ((OpenApiString)operation.Parameters[0].Extensions["X-docName"]).Value); + Assert.Equal("bar", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-foo"]).Node.GetValue()); + Assert.Equal("v1", ((JsonNodeExtension)operation.Parameters[0].Extensions["X-docName"]).Node.GetValue()); } [Theory] @@ -2079,7 +2077,7 @@ public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_Not_Used_With_I Assert.Equal("Test API", document.Info.Title); Assert.Equal(["/resource"], [.. document.Paths.Keys]); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.Parameters); Assert.Equal(2, operation.Parameters.Count); Assert.Equal("param1", operation.Parameters[0].Name); @@ -2129,7 +2127,7 @@ public void GetSwagger_Works_As_Expected_When_FromForm_Attribute_With_SwaggerIgn ); var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(["multipart/form-data"], operation.RequestBody.Content.Keys); var mediaType = operation.RequestBody.Content["multipart/form-data"]; @@ -2164,13 +2162,13 @@ public void GetSwagger_Works_As_Expected_When_FromFormObject() ); var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(["multipart/form-data"], operation.RequestBody.Content.Keys); var mediaType = operation.RequestBody.Content["multipart/form-data"]; Assert.NotNull(mediaType.Schema); - Assert.NotNull(mediaType.Schema.Reference); - Assert.Equal(nameof(SwaggerIngoreAnnotatedType), mediaType.Schema.Reference.Id); + var reference = Assert.IsType(mediaType.Schema); + Assert.Equal(nameof(SwaggerIngoreAnnotatedType), reference.Reference.Id); Assert.Empty(mediaType.Encoding); } @@ -2206,15 +2204,15 @@ public void GetSwagger_Works_As_Expected_When_FromFormObject_AndString() ); var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(["multipart/form-data"], operation.RequestBody.Content.Keys); var mediaType = operation.RequestBody.Content["multipart/form-data"]; Assert.NotNull(mediaType.Schema); Assert.NotEmpty(mediaType.Schema.AllOf); Assert.Equal(2, mediaType.Schema.AllOf.Count); - Assert.NotNull(mediaType.Schema.AllOf[0].Reference); - Assert.Equal(nameof(SwaggerIngoreAnnotatedType), mediaType.Schema.AllOf[0].Reference.Id); + var reference = Assert.IsType(mediaType.Schema.AllOf[0]); + Assert.Equal(nameof(SwaggerIngoreAnnotatedType), reference.Reference.Id); Assert.NotEmpty(mediaType.Schema.AllOf[1].Properties); Assert.Equal(["param2"], mediaType.Schema.AllOf[1].Properties.Keys); Assert.NotEmpty(mediaType.Encoding); @@ -2246,11 +2244,11 @@ public void GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsS ); var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.Equal("param1", operation.Parameters[0].Name); Assert.NotNull(operation.Parameters[0].Schema); - Assert.NotNull(operation.Parameters[0].Schema.Reference); - Assert.Equal(nameof(IntEnum), operation.Parameters[0].Schema.Reference.Id); + var reference = Assert.IsType(operation.Parameters[0].Schema); + Assert.Equal(nameof(IntEnum), reference.Reference.Id); } [Fact] @@ -2297,7 +2295,7 @@ public void GetSwagger_Copies_Description_From_GeneratedSchema() ); var document = subject.GetSwagger("v1"); - var operation = document.Paths["/resource"].Operations[OperationType.Post]; + var operation = document.Paths["/resource"].Operations[HttpMethod.Post]; Assert.NotEmpty(operation.Parameters); Assert.Equal(nameof(TypeWithDefaultAttributeOnEnum.EnumWithDefault), operation.Parameters[0].Name); Assert.Equal(document.Components.Schemas[nameof(IntEnum)].Description, operation.Parameters[0].Description); @@ -2359,13 +2357,15 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSe var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - var content = Assert.Single(document.Paths["/resource"].Operations[OperationType.Post].RequestBody.Content); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + var content = Assert.Single(document.Paths["/resource"].Operations[HttpMethod.Post].RequestBody.Content); Assert.Equal("application/someMediaType", content.Key); Assert.NotNull(content.Value.Schema); Assert.NotNull(content.Value.Schema.AllOf); - Assert.Equal("TestDto", content.Value.Schema.AllOf[0].Reference.Id); - Assert.Equal("TypeWithDefaultAttributeOnEnum", content.Value.Schema.AllOf[1].Reference.Id); + var reference = Assert.IsType(content.Value.Schema.AllOf[0]); + Assert.Equal("TestDto", reference.Reference.Id); + reference = Assert.IsType(content.Value.Schema.AllOf[1]); + Assert.Equal("TypeWithDefaultAttributeOnEnum", reference.Reference.Id); } [Fact] @@ -2416,8 +2416,8 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIF var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - var content = Assert.Single(document.Paths["/resource"].Operations[OperationType.Post].RequestBody.Content); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + var content = Assert.Single(document.Paths["/resource"].Operations[HttpMethod.Post].RequestBody.Content); Assert.Equal("application/someMediaType", content.Key); Assert.NotNull(content.Value.Schema); Assert.Equal(JsonSchemaTypes.Object, content.Value.Schema.Type); @@ -2478,8 +2478,8 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIF var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - var content = Assert.Single(document.Paths["/resource"].Operations[OperationType.Post].RequestBody.Content); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + var content = Assert.Single(document.Paths["/resource"].Operations[HttpMethod.Post].RequestBody.Content); Assert.Equal("application/someMediaType", content.Key); Assert.NotNull(content.Value.Schema); Assert.Equal(JsonSchemaTypes.Object, content.Value.Schema.Type); @@ -2542,8 +2542,8 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSt var document = subject.GetSwagger("v1"); - Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[OperationType.Post].OperationId); - var content = Assert.Single(document.Paths["/resource"].Operations[OperationType.Post].RequestBody.Content); + Assert.Equal("OperationIdSetInMetadata", document.Paths["/resource"].Operations[HttpMethod.Post].OperationId); + var content = Assert.Single(document.Paths["/resource"].Operations[HttpMethod.Post].RequestBody.Content); Assert.Equal("application/someMediaType", content.Key); Assert.NotNull(content.Value.Schema); Assert.Equal(JsonSchemaTypes.Object, content.Value.Schema.Type); @@ -2591,7 +2591,7 @@ public void GetSwagger_OpenApiOperationWithRawContent_IsHandled() Assert.Equal("V1", document.Info.Version); Assert.Equal("Test API", document.Info.Title); Assert.Equal(["/resource"], [.. document.Paths.Keys]); - Assert.Equal([OperationType.Post], document.Paths["/resource"].Operations.Keys); + Assert.Equal([HttpMethod.Post], document.Paths["/resource"].Operations.Keys); Assert.Single(document.Paths["/resource"].Operations); } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj index 818f4af5a4..d69699d6a0 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/Swashbuckle.AspNetCore.SwaggerGen.Test.csproj @@ -9,6 +9,10 @@ $(MSBuildThisFileDirectory)..\..\src\Swashbuckle.AspNetCore.Swagger\Swashbuckle.AspNetCore.Swagger.snk + + + + diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs index ef61c20f2f..a8ae6dd7ab 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs @@ -10,8 +10,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Routing; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen.Test.Fixtures; using Swashbuckle.AspNetCore.TestSupport; diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs index 54602b782f..c87ecc54ba 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsDocumentFilterTests.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs index 2b56518689..20fac1da14 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsExampleHelperTests.cs @@ -1,5 +1,5 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using System.Text.Json; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -19,16 +19,17 @@ public void Create_Builds_OpenApiArrayJson_When_Not_String_Type_And_Data_Is_Arra Assert.NotNull(example); - var actual = Assert.IsType(example); + Assert.Equal(JsonValueKind.Array, example.GetValueKind()); + var actual = example.AsArray(); Assert.Equal(3, actual.Count); - var item1 = Assert.IsType(actual[0]); - var item2 = Assert.IsType(actual[1]); - var item3 = Assert.IsType(actual[2]); - Assert.Equal("one", item1.Value); - Assert.Equal("two", item2.Value); - Assert.Equal("three", item3.Value); + Assert.Equal(JsonValueKind.String, actual[0].GetValueKind()); + Assert.Equal(JsonValueKind.String, actual[1].GetValueKind()); + Assert.Equal(JsonValueKind.String, actual[2].GetValueKind()); + Assert.Equal("one", actual[0].GetValue()); + Assert.Equal("two", actual[1].GetValue()); + Assert.Equal("three", actual[2].GetValue()); } [Theory] @@ -46,8 +47,8 @@ public void Create_Builds_OpenApiString_When_Type_String(string exampleString) Assert.NotNull(example); - var actual = Assert.IsType(example); - Assert.Equal(actual.Value, exampleString); + Assert.Equal(JsonValueKind.String, example.GetValueKind()); + Assert.Equal(exampleString, example.GetValue()); } [Fact] @@ -59,10 +60,7 @@ public void Create_Returns_Null_When_Type_String_And_Value_Is_Null() var example = XmlCommentsExampleHelper.Create( schemaRepository, schema, null); - Assert.NotNull(example); - - var actual = Assert.IsType(example); - Assert.Equal(AnyType.Null, actual.AnyType); + Assert.Null(example); } [Fact] @@ -83,9 +81,7 @@ public void Create_Returns_Null_When_Type_String_And_Value_Null_String_Literal() schemaRepository, schema, "null"); Assert.NotNull(example); - - var actual = Assert.IsType(example); - Assert.Equal(AnyType.Null, actual.AnyType); + Assert.Equal("null", example.GetValue()); } [Fact] @@ -97,8 +93,8 @@ public void Create_Allows_Schema_To_Be_Null() Assert.NotNull(example); - var actual = Assert.IsType(example); - Assert.Empty(actual); + Assert.Equal(JsonValueKind.Array, example.GetValueKind()); + Assert.Empty(example.AsArray()); } [Fact] @@ -112,9 +108,7 @@ public void Create_Builds_OpenApiString_When_Type_Integer() schemaRepository, schema, exampleString); Assert.NotNull(example); - - var actual = Assert.IsType(example); - Assert.Equal(1, actual.Value); + Assert.Equal(1, example.GetValue()); } [Fact] diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsOperationFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsOperationFilterTests.cs index 8a06b6f25a..47b33d182b 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsOperationFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsOperationFilterTests.cs @@ -1,6 +1,6 @@ using System.Xml.XPath; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.TestSupport; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -14,7 +14,7 @@ public void Apply_SetsSummaryAndDescription_FromActionSummaryAndRemarksTags() var methodInfo = typeof(FakeControllerWithXmlComments) .GetMethod(nameof(FakeControllerWithXmlComments.ActionWithSummaryAndRemarksTags)); var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource"); - var filterContext = new OperationFilterContext(apiDescription, null, null, methodInfo); + var filterContext = new OperationFilterContext(apiDescription, null, null, null, methodInfo); Subject().Apply(operation, filterContext); @@ -29,7 +29,7 @@ public void Apply_SetsSummaryAndDescription_FromUnderlyingGenericTypeActionSumma var methodInfo = typeof(FakeConstructedControllerWithXmlComments) .GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithSummaryAndResponseTags)); var apiDescription = ApiDescriptionFactory.Create(methodInfo: methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource"); - var filterContext = new OperationFilterContext(apiDescription, null, null, methodInfo); + var filterContext = new OperationFilterContext(apiDescription, null, null, null, methodInfo); Subject().Apply(operation, filterContext); @@ -55,12 +55,12 @@ public void Apply_SetsResponseDescription_FromActionOrControllerResponseTags() groupName: "v1", httpMethod: "POST", relativePath: "resource", - supportedResponseTypes: new[] - { + supportedResponseTypes: + [ new ApiResponseType { StatusCode = 200 }, new ApiResponseType { StatusCode = 400 }, - }); - var filterContext = new OperationFilterContext(apiDescription, null, null, methodInfo: methodInfo); + ]); + var filterContext = new OperationFilterContext(apiDescription, null, null, null, methodInfo: methodInfo); Subject().Apply(operation, filterContext); diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs index 2ddb64765a..31f4f1ef71 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsParameterFilterTests.cs @@ -1,6 +1,6 @@ using System.Xml.XPath; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; #if !NET10_0_OR_GREATER using Swashbuckle.AspNetCore.TestSupport; #endif @@ -17,13 +17,14 @@ public void Apply_SetsDescriptionAndExample_FromActionParamTag() .GetMethod(nameof(FakeControllerWithXmlComments.ActionWithParamTags)) .GetParameters()[0]; var apiParameterDescription = new ApiParameterDescription { }; - var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, parameterInfo: parameterInfo); + var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, parameterInfo: parameterInfo); Subject().Apply(parameter, filterContext); Assert.Equal("Description for param1", parameter.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"Example for \\\"param1\\\"\"", parameter.Example.ToJson()); + + Assert.Equal("\"Example for \\u0022param1\\u0022\"", parameter.Example.ToJson()); } [Fact] @@ -34,13 +35,14 @@ public void Apply_SetsDescriptionAndExample_FromUriTypeActionParamTag() .GetMethod(nameof(FakeControllerWithXmlComments.ActionWithParamTags)) .GetParameters()[1]; var apiParameterDescription = new ApiParameterDescription { }; - var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, parameterInfo: parameterInfo); + var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, parameterInfo: parameterInfo); Subject().Apply(parameter, filterContext); Assert.Equal("Description for param2", parameter.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"http://test.com/?param1=1¶m2=2\"", parameter.Example.ToJson()); + + Assert.Equal("\"http://test.com/?param1=1\\u0026param2=2\"", parameter.Example.ToJson()); } [Fact] @@ -51,13 +53,14 @@ public void Apply_SetsDescriptionAndExample_FromUnderlyingGenericTypeActionParam .GetMethod(nameof(FakeConstructedControllerWithXmlComments.ActionWithParamTags)) .GetParameters()[0]; var apiParameterDescription = new ApiParameterDescription { }; - var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, parameterInfo: parameterInfo); + var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, parameterInfo: parameterInfo); Subject().Apply(parameter, filterContext); Assert.Equal("Description for param1", parameter.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"Example for \\\"param1\\\"\"", parameter.Example.ToJson()); + + Assert.Equal("\"Example for \\u0022param1\\u0022\"", parameter.Example.ToJson()); } [Fact] @@ -66,7 +69,7 @@ public void Apply_SetsDescriptionAndExample_FromPropertySummaryAndExampleTags() var parameter = new OpenApiParameter { Schema = new OpenApiSchema { Type = JsonSchemaTypes.String, Description = "schema-level description" } }; var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringProperty)); var apiParameterDescription = new ApiParameterDescription { }; - var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo); + var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, propertyInfo: propertyInfo); Subject().Apply(parameter, filterContext); @@ -82,14 +85,15 @@ public void Apply_SetsDescriptionAndExample_FromUriTypePropertySummaryAndExample var parameter = new OpenApiParameter { Schema = new OpenApiSchema { Type = JsonSchemaTypes.String, Description = "schema-level description" } }; var propertyInfo = typeof(XmlAnnotatedType).GetProperty(nameof(XmlAnnotatedType.StringPropertyWithUri)); var apiParameterDescription = new ApiParameterDescription { }; - var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, propertyInfo: propertyInfo); + var filterContext = new ParameterFilterContext(apiParameterDescription, null, null, null, propertyInfo: propertyInfo); Subject().Apply(parameter, filterContext); Assert.Equal("Summary for StringPropertyWithUri", parameter.Description); Assert.Null(parameter.Schema.Description); Assert.NotNull(parameter.Example); - Assert.Equal("\"https://test.com/a?b=1&c=2\"", parameter.Example.ToJson()); + + Assert.Equal("\"https://test.com/a?b=1\\u0026c=2\"", parameter.Example.ToJson()); } private static XmlCommentsParameterFilter Subject() diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs index bf08819578..0c141fd88d 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsRequestBodyFilterTests.cs @@ -2,7 +2,7 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.TestSupport; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -26,13 +26,14 @@ public void Apply_SetsDescriptionAndExample_FromActionParamTag() { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, filterContext); Assert.Equal("Description for param1", requestBody.Description); Assert.NotNull(requestBody.Content["application/json"].Example); - Assert.Equal("\"Example for \\\"param1\\\"\"", requestBody.Content["application/json"].Example.ToJson()); + + Assert.Equal("\"Example for \\u0022param1\\u0022\"", requestBody.Content["application/json"].Example.ToJson()); } [Fact] @@ -52,13 +53,14 @@ public void Apply_SetsDescriptionAndExample_FromUnderlyingGenericTypeActionParam { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, filterContext); Assert.Equal("Description for param1", requestBody.Description); Assert.NotNull(requestBody.Content["application/json"].Example); - Assert.Equal("\"Example for \\\"param1\\\"\"", requestBody.Content["application/json"].Example.ToJson()); + + Assert.Equal("\"Example for \\u0022param1\\u0022\"", requestBody.Content["application/json"].Example.ToJson()); } [Fact] @@ -75,7 +77,7 @@ public void Apply_SetsDescriptionAndExample_FromPropertySummaryAndExampleTags() { ModelMetadata = ModelMetadataFactory.CreateForProperty(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringProperty)) }; - var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, filterContext); @@ -99,13 +101,14 @@ public void Apply_SetsDescriptionAndExample_FromUriTypePropertySummaryAndExample { ModelMetadata = ModelMetadataFactory.CreateForProperty(typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithUri)) }; - var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, filterContext); Assert.Equal("Summary for StringPropertyWithUri", requestBody.Description); Assert.NotNull(requestBody.Content["application/json"].Example); - Assert.Equal("\"https://test.com/a?b=1&c=2\"", requestBody.Content["application/json"].Example.ToJson()); + + Assert.Equal("\"https://test.com/a?b=1\\u0026c=2\"", requestBody.Content["application/json"].Example.ToJson()); } [Fact] @@ -125,7 +128,7 @@ public void Apply_SetsDescription_ForParameterFromBody() { ParameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameterInfo } }; - var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null); + var filterContext = new RequestBodyFilterContext(bodyParameterDescription, null, null, null, null); Subject().Apply(requestBody, filterContext); @@ -148,7 +151,7 @@ public void Apply_SetsDescription_ForParameterFromForm() Schema = new OpenApiSchema { Type = JsonSchemaTypes.String, - Properties = new Dictionary() + Properties = new Dictionary() { [parameterInfo.Name] = new OpenApiSchema() } @@ -163,7 +166,7 @@ public void Apply_SetsDescription_ForParameterFromForm() Name = parameterInfo.Name, Source = BindingSource.Form }; - var filterContext = new RequestBodyFilterContext(null, [bodyParameterDescription], null, null); + var filterContext = new RequestBodyFilterContext(null, [bodyParameterDescription], null, null, null); Subject().Apply(requestBody, filterContext); diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs index ca2a3db180..11d6337651 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/XmlComments/XmlCommentsSchemaFilterTests.cs @@ -1,9 +1,6 @@ using System.Globalization; using System.Xml.XPath; -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.TestSupport; - -using JsonSchemaType = string; +using Microsoft.OpenApi; namespace Swashbuckle.AspNetCore.SwaggerGen.Test; @@ -67,8 +64,6 @@ public void Apply_SetsDescription_FromPropertySummaryTag( { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.GuidProperty), JsonSchemaTypes.String, "\"d3966535-2637-48fa-b911-e3c27405ee09\"" }, { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringProperty), JsonSchemaTypes.String, "\"Example for StringProperty\"" }, { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.ObjectProperty), JsonSchemaTypes.Object, "{\n \"prop1\": 1,\n \"prop2\": \"foobar\"\n}" }, - { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithNullExample), JsonSchemaTypes.String, "null" }, - { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithUri), JsonSchemaTypes.String, "\"https://test.com/a?b=1&c=2\"" }, { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.BoolProperty), JsonSchemaTypes.Boolean, "true" }, { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.IntProperty), JsonSchemaTypes.Integer, "10" }, { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.LongProperty), JsonSchemaTypes.Integer, "4294967295" }, @@ -79,8 +74,10 @@ public void Apply_SetsDescription_FromPropertySummaryTag( { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.GuidProperty), JsonSchemaTypes.String, "\"d3966535-2637-48fa-b911-e3c27405ee09\"" }, { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringProperty), JsonSchemaTypes.String, "\"Example for StringProperty\"" }, { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.ObjectProperty), JsonSchemaTypes.Object, "{\n \"prop1\": 1,\n \"prop2\": \"foobar\"\n}" }, - { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithNullExample), JsonSchemaTypes.String, "null" }, - { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithUri), JsonSchemaTypes.String, "\"https://test.com/a?b=1&c=2\"" }, + { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithUri), JsonSchemaTypes.String, "\"https://test.com/a?b=1\\u0026c=2\"" }, + { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithUri), JsonSchemaTypes.String, "\"https://test.com/a?b=1\\u0026c=2\"" }, + { typeof(XmlAnnotatedType), nameof(XmlAnnotatedType.StringPropertyWithNullExample), JsonSchemaTypes.String, "\"null\"" }, + { typeof(XmlAnnotatedRecord), nameof(XmlAnnotatedRecord.StringPropertyWithNullExample), JsonSchemaTypes.String, "\"null\"" }, }; [Theory] @@ -102,8 +99,7 @@ public void Apply_SetsExample_FromPropertyExampleTag( Subject().Apply(schema, filterContext); // Assert - Assert.NotNull(schema.Example); - Assert.Equal(expectedExampleAsJson, schema.Example.ToJson()); + Assert.Equal(expectedExampleAsJson, schema.Example?.ToJson()); } public static TheoryData Apply_DoesNotSetExample_WhenPropertyExampleTagIsNotProvided_Data => new() @@ -173,7 +169,8 @@ public void Apply_UsesInvariantCulture_WhenSettingExample( CultureInfo.CurrentCulture = defaultCulture; - Assert.Equal(expectedValue, schema.Example.GetType().GetProperty("Value").GetValue(schema.Example)); + Assert.NotNull(schema.Example); + Assert.Equal(expectedValue, schema.Example.GetValue()); } private static XmlCommentsSchemaFilter Subject() diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet10_0.verified.txt new file mode 100644 index 0000000000..6d14c6201c --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "type": "string" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet8_0.verified.txt index d563468b10..6d14c6201c 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet9_0.verified.txt index d563468b10..6d14c6201c 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Body.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet10_0.verified.txt new file mode 100644 index 0000000000..bc6ffc3f3f --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet10_0.verified.txt @@ -0,0 +1,46 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "type": "object", + "properties": { + "param": { + "type": "string" + } + } + }, + "encoding": { + "param": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet8_0.verified.txt index bae2a174da..bc6ffc3f3f 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet8_0.verified.txt @@ -37,5 +37,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet9_0.verified.txt index bae2a174da..bc6ffc3f3f 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasConsumesAttribute_bindingSourceId=Form.DotNet9_0.verified.txt @@ -37,5 +37,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet10_0.verified.txt new file mode 100644 index 0000000000..3273311954 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet10_0.verified.txt @@ -0,0 +1,35 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/zip": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet8_0.verified.txt index fb597ff815..3273311954 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet8_0.verified.txt @@ -26,5 +26,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet9_0.verified.txt index fb597ff815..3273311954 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasFileResult.DotNet9_0.verified.txt @@ -26,5 +26,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..5ec4767ac3 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet10_0.verified.txt @@ -0,0 +1,28 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + }, + "deprecated": true + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet8_0.verified.txt index e846f5db84..5ec4767ac3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet8_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet9_0.verified.txt index e846f5db84..5ec4767ac3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasObsoleteAttribute.DotNet9_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..919cb44f3b --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet10_0.verified.txt @@ -0,0 +1,35 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/someMediaType": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet8_0.verified.txt index 40f97e8104..919cb44f3b 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet8_0.verified.txt @@ -26,5 +26,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet9_0.verified.txt index 40f97e8104..919cb44f3b 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHasProducesAttribute.DotNet9_0.verified.txt @@ -26,5 +26,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet10_0.verified.txt new file mode 100644 index 0000000000..0ac5eeddeb --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet10_0.verified.txt @@ -0,0 +1,44 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param1", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "param2", + "in": "query", + "schema": { + "type": "string", + "format": "binary" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet8_0.verified.txt index beaf092287..0ac5eeddeb 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet8_0.verified.txt @@ -35,5 +35,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet9_0.verified.txt index beaf092287..0ac5eeddeb 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeButNotWithIFormFile.DotNet9_0.verified.txt @@ -35,5 +35,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet10_0.verified.txt new file mode 100644 index 0000000000..09943db9a0 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet10_0.verified.txt @@ -0,0 +1,46 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "NotIgnoredString": { + "type": "string" + } + } + }, + "encoding": { + "NotIgnoredString": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet8_0.verified.txt index 7bfe672606..09943db9a0 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet8_0.verified.txt @@ -37,5 +37,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet9_0.verified.txt index 7bfe672606..09943db9a0 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionHavingFromFormAttributeWithSwaggerIgnore.DotNet9_0.verified.txt @@ -37,5 +37,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..c4978dc754 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet8_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet8_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet9_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasBindNeverAttribute.DotNet9_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..c4978dc754 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet8_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet8_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet9_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionParameterHasSwaggerIgnoreAttribute.DotNet9_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithConsumesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithConsumesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt new file mode 100644 index 0000000000..0477b2747b --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithConsumesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt @@ -0,0 +1,38 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "$ref": "#/components/schemas/TestDto" + } + } + } + }, + "responses": { } + } + } + }, + "components": { + "schemas": { + "TestDto": { + "type": "object", + "properties": { + "Prop1": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet10_0.verified.txt new file mode 100644 index 0000000000..564eaef913 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet10_0.verified.txt @@ -0,0 +1,28 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "operationId": "SomeEndpointName", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet8_0.verified.txt index 881b2f804c..564eaef913 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet8_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet9_0.verified.txt index 881b2f804c..564eaef913 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithEndpointNameMetadata.DotNet9_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithParameterAndProvidedOpenApiOperation.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithParameterAndProvidedOpenApiOperation.DotNet10_0.verified.txt new file mode 100644 index 0000000000..215b43fba8 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithParameterAndProvidedOpenApiOperation.DotNet10_0.verified.txt @@ -0,0 +1,24 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "parameters": [ + { + "name": "ParameterInMetadata", + "schema": { + "type": "string" + } + } + ], + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProducesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProducesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt new file mode 100644 index 0000000000..5701dd9909 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProducesAttributeAndProvidedOpenApiOperation.DotNet10_0.verified.txt @@ -0,0 +1,40 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "responses": { + "200": { + "description": null, + "content": { + "application/someMediaType": { + "schema": { + "$ref": "#/components/schemas/TestDto" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "TestDto": { + "type": "object", + "properties": { + "Prop1": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProvidedOpenApiMetadata.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProvidedOpenApiMetadata.DotNet10_0.verified.txt new file mode 100644 index 0000000000..f9809c47a5 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithProvidedOpenApiMetadata.DotNet10_0.verified.txt @@ -0,0 +1,21 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "parameters": [ + { + "name": "ParameterInMetadata" + } + ], + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..b6f6dc6fe7 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt index d8feabf909..b6f6dc6fe7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt index d8feabf909..b6f6dc6fe7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..b6f6dc6fe7 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt index d8feabf909..b6f6dc6fe7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt index d8feabf909..b6f6dc6fe7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredBodyParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet10_0.verified.txt new file mode 100644 index 0000000000..4b47d6bf47 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet8_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet9_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredMember.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..4b47d6bf47 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithBindRequiredAttribute.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..4b47d6bf47 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt index d01ccb87ff..4b47d6bf47 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRequiredQueryParameter_action=ActionWithParameterWithRequiredAttribute.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet10_0.verified.txt new file mode 100644 index 0000000000..ab92669e55 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet10_0.verified.txt @@ -0,0 +1,44 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "422": { + "description": "Unprocessable Content" + }, + "default": { + "description": "Error" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet8_0.verified.txt index 7ce9616bd4..ab92669e55 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet8_0.verified.txt @@ -35,5 +35,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet9_0.verified.txt index 7ce9616bd4..ab92669e55 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithReturnValueAndSupportedResponseTypes.DotNet9_0.verified.txt @@ -35,5 +35,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet10_0.verified.txt new file mode 100644 index 0000000000..78e049e122 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet10_0.verified.txt @@ -0,0 +1,28 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "operationId": "SomeRouteName", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet8_0.verified.txt index 96e2b65032..78e049e122 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet8_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet9_0.verified.txt index 96e2b65032..78e049e122 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionWithRouteNameMetadata.DotNet9_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..6624ddf8a3 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet8_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet9_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAcceptFromHeaderParameter.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..6624ddf8a3 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet8_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet9_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithAuthorizationFromHeaderParameter.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..6624ddf8a3 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet8_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet9_0.verified.txt index 1dc73c4cb6..6624ddf8a3 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ActionsWithIllegalHeaderParameters_action=ActionWithContentTypeFromHeaderParameter.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet10_0.verified.txt new file mode 100644 index 0000000000..ac07216149 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + }, + "get": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet8_0.verified.txt index 01c001ba5c..ac07216149 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet9_0.verified.txt index 01c001ba5c..ac07216149 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiDescriptionsWithMatchingGroupName.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet10_0.verified.txt new file mode 100644 index 0000000000..42b3ff16fb --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet10_0.verified.txt @@ -0,0 +1,31 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Foo" + ], + "requestBody": { + "content": { }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Foo" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet8_0.verified.txt index b960cc7d32..42b3ff16fb 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet8_0.verified.txt @@ -22,5 +22,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Foo" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet9_0.verified.txt index b960cc7d32..42b3ff16fb 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterDescriptionForBodyIsRequired.DotNet9_0.verified.txt @@ -22,5 +22,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Foo" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..9a842b7172 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet8_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet9_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterHasNoCorrespondingActionParameter.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet10_0.verified.txt new file mode 100644 index 0000000000..9a842b7172 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet8_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet9_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParameterIsBoundToPath.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet10_0.verified.txt new file mode 100644 index 0000000000..5fb54c13b8 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet10_0.verified.txt @@ -0,0 +1,53 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "properties": { + "param1": { + "type": "string" + }, + "param2": { + "type": "integer", + "format": "int32" + } + } + }, + "encoding": { + "param1": { + "style": "form" + }, + "param2": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet8_0.verified.txt index 3b3c806d7f..5fb54c13b8 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet8_0.verified.txt @@ -44,5 +44,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet9_0.verified.txt index 3b3c806d7f..5fb54c13b8 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreBoundToForm.DotNet9_0.verified.txt @@ -44,5 +44,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet10_0.verified.txt new file mode 100644 index 0000000000..c69a69d8a5 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "header", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet8_0.verified.txt index edd30d7ed8..c69a69d8a5 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet9_0.verified.txt index edd30d7ed8..c69a69d8a5 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Header.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet10_0.verified.txt new file mode 100644 index 0000000000..9a842b7172 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet10_0.verified.txt @@ -0,0 +1,37 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet8_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet8_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet9_0.verified.txt index 12b0d9a064..9a842b7172 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Path.DotNet9_0.verified.txt @@ -28,5 +28,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet10_0.verified.txt new file mode 100644 index 0000000000..8730d69c92 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet8_0.verified.txt index 2d0449f965..8730d69c92 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet9_0.verified.txt index 2d0449f965..8730d69c92 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=Query.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet10_0.verified.txt new file mode 100644 index 0000000000..8730d69c92 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet8_0.verified.txt index 2d0449f965..8730d69c92 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet8_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet9_0.verified.txt index 2d0449f965..8730d69c92 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ApiParametersThatAreNotBoundToBodyOrForm_bindingSourceId=null.DotNet9_0.verified.txt @@ -27,5 +27,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet10_0.verified.txt new file mode 100644 index 0000000000..c4978dc754 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet8_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet8_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet9_0.verified.txt index d43830691c..c4978dc754 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.ConflictingActionsResolverIsSpecified.DotNet9_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..1240f3ab27 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet10_0.verified.txt @@ -0,0 +1,28 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "description": "A Test Description", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet8_0.verified.txt index e99cc7c30a..1240f3ab27 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet8_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet9_0.verified.txt index e99cc7c30a..1240f3ab27 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasDescriptionAttribute.DotNet9_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..63a367e531 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet10_0.verified.txt @@ -0,0 +1,28 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "summary": "A Test Summary", + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet8_0.verified.txt index c9dbd1f23b..63a367e531 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet8_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet9_0.verified.txt index c9dbd1f23b..63a367e531 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasSummaryAttribute.DotNet9_0.verified.txt @@ -19,5 +19,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet10_0.verified.txt new file mode 100644 index 0000000000..22721ecaf5 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet10_0.verified.txt @@ -0,0 +1,35 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Some", + "Tags", + "Here" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Some" + }, + { + "name": "Tags" + }, + { + "name": "Here" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet8_0.verified.txt index 9a55caf777..22721ecaf5 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet8_0.verified.txt @@ -20,5 +20,16 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Some" + }, + { + "name": "Tags" + }, + { + "name": "Here" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet9_0.verified.txt index 9a55caf777..22721ecaf5 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.EndpointMetadataHasTags.DotNet9_0.verified.txt @@ -20,5 +20,16 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Some" + }, + { + "name": "Tags" + }, + { + "name": "Here" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet10_0.verified.txt new file mode 100644 index 0000000000..66ef5af646 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet10_0.verified.txt @@ -0,0 +1,65 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "EnumWithDefault", + "in": "query", + "description": "

Members:

  • Value2 - 2
  • Value4 - 4
  • Value8 - 8
", + "schema": { + "$ref": "#/components/schemas/IntEnum" + } + }, + { + "name": "EnumArrayWithDefault", + "in": "query", + "description": "

Members:

  • Value2 - 2
  • Value4 - 4
  • Value8 - 8
", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntEnum" + }, + "description": "

Members:

  • Value2 - 2
  • Value4 - 4
  • Value8 - 8
", + "default": [ + 4 + ] + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "IntEnum": { + "enum": [ + 2, + 4, + 8 + ], + "type": "integer", + "description": "

Members:

  • Value2 - 2
  • Value4 - 4
  • Value8 - 8
", + "format": "int32" + } + } + }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet8_0.verified.txt index 269352bca8..66ef5af646 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet8_0.verified.txt @@ -56,5 +56,10 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet9_0.verified.txt index 269352bca8..66ef5af646 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Copies_Description_From_GeneratedSchema.DotNet9_0.verified.txt @@ -56,5 +56,10 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFile.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFile.DotNet10_0.verified.txt new file mode 100644 index 0000000000..0b8be7e8df --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFile.DotNet10_0.verified.txt @@ -0,0 +1,36 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "type": "object", + "properties": { + "param": { + "type": "string", + "format": "binary" + } + } + }, + "encoding": { + "param": { + "style": "form" + } + } + } + } + }, + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFileCollection.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFileCollection.DotNet10_0.verified.txt new file mode 100644 index 0000000000..13fdf02051 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIFormFileCollection.DotNet10_0.verified.txt @@ -0,0 +1,39 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "type": "object", + "properties": { + "param": { + "type": "array", + "items": { + "type": "string", + "format": "binary" + } + } + } + }, + "encoding": { + "param": { + "style": "form" + } + } + } + } + }, + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.DotNet10_0.verified.txt new file mode 100644 index 0000000000..1a5220d12f --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSeveralFromForms.DotNet10_0.verified.txt @@ -0,0 +1,73 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/TestDto" + }, + { + "$ref": "#/components/schemas/TypeWithDefaultAttributeOnEnum" + } + ] + } + } + } + }, + "responses": { } + } + } + }, + "components": { + "schemas": { + "IntEnum": { + "enum": [ + 2, + 4, + 8 + ], + "type": "integer", + "format": "int32" + }, + "TestDto": { + "type": "object", + "properties": { + "Prop1": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "TypeWithDefaultAttributeOnEnum": { + "type": "object", + "properties": { + "EnumWithDefault": { + "$ref": "#/components/schemas/IntEnum" + }, + "EnumArrayWithDefault": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntEnum" + }, + "default": [ + 4 + ], + "nullable": true + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithStringFromForm.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithStringFromForm.DotNet10_0.verified.txt new file mode 100644 index 0000000000..fb4a0d9b0d --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithStringFromForm.DotNet10_0.verified.txt @@ -0,0 +1,35 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "operationId": "OperationIdSetInMetadata", + "requestBody": { + "content": { + "application/someMediaType": { + "schema": { + "type": "object", + "properties": { + "param": { + "type": "string" + } + } + }, + "encoding": { + "param": { + "style": "form" + } + } + } + } + }, + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet10_0.verified.txt new file mode 100644 index 0000000000..3f7e0fca72 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet10_0.verified.txt @@ -0,0 +1,49 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SwaggerIngoreAnnotatedType" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "SwaggerIngoreAnnotatedType": { + "type": "object", + "properties": { + "NotIgnoredString": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet8_0.verified.txt index fa3409d7a6..3f7e0fca72 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet8_0.verified.txt @@ -40,5 +40,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet9_0.verified.txt index fa3409d7a6..3f7e0fca72 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject.DotNet9_0.verified.txt @@ -40,5 +40,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet10_0.verified.txt new file mode 100644 index 0000000000..09cd6845a7 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet10_0.verified.txt @@ -0,0 +1,66 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SwaggerIngoreAnnotatedType" + }, + { + "type": "object", + "properties": { + "param2": { + "type": "string" + } + } + } + ] + }, + "encoding": { + "param2": { + "style": "form" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "SwaggerIngoreAnnotatedType": { + "type": "object", + "properties": { + "NotIgnoredString": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet8_0.verified.txt index ce2208344a..09cd6845a7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet8_0.verified.txt @@ -57,5 +57,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet9_0.verified.txt index ce2208344a..09cd6845a7 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_FromFormObject_AndString.DotNet9_0.verified.txt @@ -57,5 +57,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet10_0.verified.txt new file mode 100644 index 0000000000..dcb2bf290f --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet10_0.verified.txt @@ -0,0 +1,48 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "Fake" + ], + "parameters": [ + { + "name": "param1", + "in": "query", + "schema": { + "$ref": "#/components/schemas/IntEnum" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "IntEnum": { + "enum": [ + 2, + 4, + 8 + ], + "type": "integer", + "format": "int32" + } + } + }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet8_0.verified.txt index 0eb507c649..dcb2bf290f 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet8_0.verified.txt @@ -39,5 +39,10 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet9_0.verified.txt index 0eb507c649..dcb2bf290f 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.GetSwagger_Works_As_Expected_When_TypeIsEnum_AndModelMetadataTypeIsString.DotNet9_0.verified.txt @@ -39,5 +39,10 @@ "format": "int32" } } - } + }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..7e8db45714 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAcceptFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "operationId": "OperationIdSetInMetadata", + "parameters": [ + { + "name": "accept" + }, + { + "name": "param", + "schema": { + "type": "string" + } + } + ], + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..ad0d5948d7 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithAuthorizationFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "operationId": "OperationIdSetInMetadata", + "parameters": [ + { + "name": "authorization" + }, + { + "name": "param", + "schema": { + "type": "string" + } + } + ], + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt new file mode 100644 index 0000000000..53d0e30a9a --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.IllegalHeaderForOperation_action=ActionWithContentTypeFromHeaderParameter.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "get": { + "operationId": "OperationIdSetInMetadata", + "parameters": [ + { + "name": "Content-Type" + }, + { + "name": "param", + "schema": { + "type": "string" + } + } + ], + "responses": { } + } + } + }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.OperationHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.OperationHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt new file mode 100644 index 0000000000..0e2c5aacab --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.OperationHasSwaggerIgnoreAttribute.DotNet10_0.verified.txt @@ -0,0 +1,9 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { }, + "components": { } +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet10_0.verified.txt new file mode 100644 index 0000000000..48ec45c64a --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet10_0.verified.txt @@ -0,0 +1,51 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource1": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/resource2": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/resource3": { + "post": { + "tags": [ + "Fake" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet8_0.verified.txt index cc883c410c..48ec45c64a 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet8_0.verified.txt @@ -42,5 +42,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet9_0.verified.txt index cc883c410c..48ec45c64a 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.SortKeySelectorIsSpecified.DotNet9_0.verified.txt @@ -42,5 +42,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "Fake" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet10_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet10_0.verified.txt new file mode 100644 index 0000000000..a007011e63 --- /dev/null +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet10_0.verified.txt @@ -0,0 +1,27 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "Test API", + "version": "V1" + }, + "paths": { + "/resource": { + "post": { + "tags": [ + "resource" + ], + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { }, + "tags": [ + { + "name": "resource" + } + ] +} \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet8_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet8_0.verified.txt index 26ccf63f38..a007011e63 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet8_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet8_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "resource" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet9_0.verified.txt b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet9_0.verified.txt index 26ccf63f38..a007011e63 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet9_0.verified.txt +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/snapshots/VerifyTests.TagSelectorIsSpecified.DotNet9_0.verified.txt @@ -18,5 +18,10 @@ } } }, - "components": { } + "components": { }, + "tags": [ + { + "name": "resource" + } + ] } \ No newline at end of file diff --git a/test/Swashbuckle.AspNetCore.TestSupport/Extensions/IOpenApiAnyExtensions.cs b/test/Swashbuckle.AspNetCore.TestSupport/Extensions/JsonNodeExtensionExtensions.cs similarity index 61% rename from test/Swashbuckle.AspNetCore.TestSupport/Extensions/IOpenApiAnyExtensions.cs rename to test/Swashbuckle.AspNetCore.TestSupport/Extensions/JsonNodeExtensionExtensions.cs index 8ee45287e7..ce37f7fb67 100644 --- a/test/Swashbuckle.AspNetCore.TestSupport/Extensions/IOpenApiAnyExtensions.cs +++ b/test/Swashbuckle.AspNetCore.TestSupport/Extensions/JsonNodeExtensionExtensions.cs @@ -1,17 +1,15 @@ using Microsoft.OpenApi; -using Microsoft.OpenApi.Writers; - -using Any = Microsoft.OpenApi.Any.IOpenApiAny; namespace Swashbuckle.AspNetCore.TestSupport; -public static class IOpenApiAnyExtensions +public static class JsonNodeExtensionExtensions { - public static string ToJson(this Any openApiAny) + public static string ToJson(this JsonNodeExtension openApiAny) { var stringWriter = new StringWriter(); var jsonWriter = new OpenApiJsonWriter(stringWriter); + // Use 3.0 for consistency with previous versions of Microsoft.OpenApi openApiAny.Write(jsonWriter, OpenApiSpecVersion.OpenApi3_0); return stringWriter.ToString(); diff --git a/test/WebSites/Basic/Controllers/CrudActionsController.cs b/test/WebSites/Basic/Controllers/CrudActionsController.cs index 0735ed892b..a143e53458 100644 --- a/test/WebSites/Basic/Controllers/CrudActionsController.cs +++ b/test/WebSites/Basic/Controllers/CrudActionsController.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; namespace Basic.Controllers; @@ -27,10 +28,17 @@ public class CrudActionsController /// /// [HttpPost(Name = "CreateProduct")] +#if NET10_0_OR_GREATER + public Ok Create([FromBody, Required] Product product) + { + return TypedResults.Ok(product); + } +#else public Product Create([FromBody, Required] Product product) { return product; } +#endif /// Get all products /// diff --git a/test/WebSites/Basic/Startup.cs b/test/WebSites/Basic/Startup.cs index ba8ce11df0..221a801a32 100644 --- a/test/WebSites/Basic/Startup.cs +++ b/test/WebSites/Basic/Startup.cs @@ -2,7 +2,7 @@ using System.Reflection; using Basic.Swagger; using Microsoft.AspNetCore.Localization; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace Basic; @@ -61,7 +61,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) endpoints.MapSwagger("swagger/{documentName}/swagger.json"); endpoints.MapSwagger("swagger/{documentName}/swaggerv2.json", c => { - c.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0; + c.OpenApiVersion = OpenApiSpecVersion.OpenApi2_0; + }); + endpoints.MapSwagger("swagger/{documentName}/swaggerv3_1.json", c => + { + c.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; }); }); diff --git a/test/WebSites/Basic/Swagger/AddCartsByIdGetExternalDocs.cs b/test/WebSites/Basic/Swagger/AddCartsByIdGetExternalDocs.cs index 94e389c07a..b4ed94831e 100644 --- a/test/WebSites/Basic/Swagger/AddCartsByIdGetExternalDocs.cs +++ b/test/WebSites/Basic/Swagger/AddCartsByIdGetExternalDocs.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Basic.Swagger; diff --git a/test/WebSites/Basic/Swagger/AssignOperationVendorExtensions.cs b/test/WebSites/Basic/Swagger/AssignOperationVendorExtensions.cs index ee0c48bba5..300f8eb731 100644 --- a/test/WebSites/Basic/Swagger/AssignOperationVendorExtensions.cs +++ b/test/WebSites/Basic/Swagger/AssignOperationVendorExtensions.cs @@ -1,5 +1,4 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Basic.Swagger; @@ -8,6 +7,7 @@ public class AssignOperationVendorExtensions : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { - operation.Extensions.Add("x-purpose", new OpenApiString("test")); + operation.Extensions ??= new Dictionary(); + operation.Extensions.Add("x-purpose", new JsonNodeExtension("test")); } } diff --git a/test/WebSites/Basic/Swagger/AssignRequestBodyVendorExtensions.cs b/test/WebSites/Basic/Swagger/AssignRequestBodyVendorExtensions.cs index b4243a40ee..e61b86f6d1 100644 --- a/test/WebSites/Basic/Swagger/AssignRequestBodyVendorExtensions.cs +++ b/test/WebSites/Basic/Swagger/AssignRequestBodyVendorExtensions.cs @@ -1,13 +1,16 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Basic.Swagger; public class AssignRequestBodyVendorExtensions : IRequestBodyFilter { - public void Apply(OpenApiRequestBody requestBody, RequestBodyFilterContext context) + public void Apply(IOpenApiRequestBody requestBody, RequestBodyFilterContext context) { - requestBody.Extensions.Add("x-purpose", new OpenApiString("test")); + if (requestBody is OpenApiRequestBody body) + { + body.Extensions ??= new Dictionary(); + body.Extensions.Add("x-purpose", new JsonNodeExtension("test")); + } } } diff --git a/test/WebSites/Basic/Swagger/ExamplesSchemaFilter.cs b/test/WebSites/Basic/Swagger/ExamplesSchemaFilter.cs index 5092ee5453..3d52ffb5b7 100644 --- a/test/WebSites/Basic/Swagger/ExamplesSchemaFilter.cs +++ b/test/WebSites/Basic/Swagger/ExamplesSchemaFilter.cs @@ -1,20 +1,25 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using System.Text.Json.Nodes; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace Basic.Swagger; public class ExamplesSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Example = context.Type.Name switch + if (schema is not OpenApiSchema concrete) { - "Product" => new OpenApiObject + return; + } + + concrete.Example = context.Type.Name switch + { + "Product" => new JsonObject { - ["id"] = new OpenApiInteger(123), - ["description"] = new OpenApiString("foobar"), - ["price"] = new OpenApiDouble(14.37) + ["id"] = 123, + ["description"] = "foobar", + ["price"] = 14.37d }, _ => null, }; diff --git a/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net10.0.json b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net10.0.json new file mode 100644 index 0000000000..91a88cd8d1 --- /dev/null +++ b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net10.0.json @@ -0,0 +1,59 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CliExample", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:51071" + } + ], + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net8.0.json b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net8.0.json index d5707c9824..91a88cd8d1 100644 --- a/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net8.0.json +++ b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net8.0.json @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net9.0.json b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net9.0.json index d5707c9824..91a88cd8d1 100644 --- a/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net9.0.json +++ b/test/WebSites/CliExample/wwwroot/swagger/v1/swagger_net9.0.json @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net10.0.json b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net10.0.json new file mode 100644 index 0000000000..eeda72e5e4 --- /dev/null +++ b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net10.0.json @@ -0,0 +1,59 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "CliExampleWithFactory", + "version": "1.0" + }, + "servers": [ + { + "url": "http://localhost:57556/" + } + ], + "paths": { + "/products": { + "get": { + "tags": [ + "Products" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Product" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Product": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int32" + }, + "description": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "tags": [ + { + "name": "Products" + } + ] +} \ No newline at end of file diff --git a/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net8.0.json b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net8.0.json index b2d3ff6db2..eeda72e5e4 100644 --- a/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net8.0.json +++ b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net8.0.json @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net9.0.json b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net9.0.json index b2d3ff6db2..eeda72e5e4 100644 --- a/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net9.0.json +++ b/test/WebSites/CliExampleWithFactory/wwwroot/swagger/v1/swagger_net9.0.json @@ -50,5 +50,10 @@ "additionalProperties": false } } - } + }, + "tags": [ + { + "name": "Products" + } + ] } \ No newline at end of file diff --git a/test/WebSites/CustomDocumentSerializer/DocumentSerializerTest.cs b/test/WebSites/CustomDocumentSerializer/DocumentSerializerTest.cs index 859b6c9952..8e93c7a124 100644 --- a/test/WebSites/CustomDocumentSerializer/DocumentSerializerTest.cs +++ b/test/WebSites/CustomDocumentSerializer/DocumentSerializerTest.cs @@ -1,6 +1,4 @@ using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; using Swashbuckle.AspNetCore.Swagger; namespace CustomDocumentSerializer; @@ -15,6 +13,7 @@ public void SerializeDocument(OpenApiDocument document, IOpenApiWriter writer, O { OpenApiSpecVersion.OpenApi2_0 => "DocumentSerializerTest2.0", OpenApiSpecVersion.OpenApi3_0 => "DocumentSerializerTest3.0", + OpenApiSpecVersion.OpenApi3_1 => "DocumentSerializerTest3.1", _ => throw new NotImplementedException() }; diff --git a/test/WebSites/CustomDocumentSerializer/Startup.cs b/test/WebSites/CustomDocumentSerializer/Startup.cs index 196d301c24..82471b8138 100644 --- a/test/WebSites/CustomDocumentSerializer/Startup.cs +++ b/test/WebSites/CustomDocumentSerializer/Startup.cs @@ -27,6 +27,7 @@ public void Configure(IApplicationBuilder app) endpoints.MapControllers(); endpoints.MapSwagger("swagger/{documentName}/swagger.json"); endpoints.MapSwagger("swagger/{documentName}/swaggerv2.json", c => c.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi2_0); + endpoints.MapSwagger("swagger/{documentName}/swaggerv3_1.json", c => c.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1); }); } } diff --git a/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs b/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs index 166d338c93..63e199d152 100644 --- a/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs +++ b/test/WebSites/DocumentationSnippets/AuthResponsesOperationFilter.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; diff --git a/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs b/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs index 4497d60d75..5aec7d5bd1 100644 --- a/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs +++ b/test/WebSites/DocumentationSnippets/AutoRestSchemaFilter.cs @@ -1,5 +1,5 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using System.Text.Json.Nodes; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -7,18 +7,21 @@ namespace DocumentationSnippets; // begin-snippet: SwaggerGen-AutoRestSchemaFilter public class AutoRestSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { var type = context.Type; - if (type.IsEnum) + if (type.IsEnum && schema is OpenApiSchema concrete) { - schema.Extensions.Add( + concrete.Extensions ??= new Dictionary(); + concrete.Extensions.Add( "x-ms-enum", - new OpenApiObject - { - ["name"] = new OpenApiString(type.Name), - ["modelAsString"] = new OpenApiBoolean(true) - } + new JsonNodeExtension( + new JsonObject + { + ["name"] = type.Name, + ["modelAsString"] = true + } + ) ); } } diff --git a/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs b/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs index 3e85090f3c..ea7d3fc760 100644 --- a/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs +++ b/test/WebSites/DocumentationSnippets/CustomDocumentSerializer.cs @@ -1,6 +1,4 @@ using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Writers; using Swashbuckle.AspNetCore.Swagger; namespace DocumentationSnippets; diff --git a/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs b/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs index 08345f37a2..2c85551838 100644 --- a/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs +++ b/test/WebSites/DocumentationSnippets/DictionaryTKeyEnumTValueSchemaFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -6,8 +6,13 @@ namespace DocumentationSnippets; // begin-snippet: SwaggerGen-DictionaryTKeyEnumTValueSchemaFilter public class DictionaryTKeyEnumTValueSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { + if (schema is not OpenApiSchema concrete) + { + return; + } + // Only run for fields that are a Dictionary if (!context.Type.IsGenericType || !context.Type.GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>))) { @@ -23,8 +28,8 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context) return; } - schema.Type = "object"; - schema.Properties = keyType.GetEnumNames().ToDictionary( + concrete.Type = JsonSchemaType.Object; + concrete.Properties = keyType.GetEnumNames().ToDictionary( name => name, name => context.SchemaGenerator.GenerateSchema(valueType, context.SchemaRepository)); } diff --git a/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs b/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs index 1cbb116eb9..febdaab384 100644 --- a/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs +++ b/test/WebSites/DocumentationSnippets/IServiceCollectionExtensions.cs @@ -1,5 +1,5 @@ using System.Reflection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -159,7 +159,7 @@ public static void Configure(this IServiceCollection services) // begin-snippet: SwaggerGen-CustomSchemaMapping services.AddSwaggerGen(options => { - options.MapType(() => new OpenApiSchema { Type = "string" }); + options.MapType(() => new OpenApiSchema { Type = JsonSchemaType.String }); }); // end-snippet @@ -213,15 +213,9 @@ public static void Configure(this IServiceCollection services) // begin-snippet: SwaggerGen-AddSecurityRequirement services.AddSwaggerGen(options => { - options.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement((document) => new OpenApiSecurityRequirement() { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }, - ["readAccess", "writeAccess"] - } + [new OpenApiSecuritySchemeReference("oauth2", document)] = ["readAccess", "writeAccess"] }); }); // end-snippet @@ -236,15 +230,9 @@ public static void Configure(this IServiceCollection services) BearerFormat = "JWT", Description = "JWT Authorization header using the Bearer scheme." }); - options.AddSecurityRequirement(new OpenApiSecurityRequirement + options.AddSecurityRequirement(document => new OpenApiSecurityRequirement { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } - }, - [] - } + [new OpenApiSecuritySchemeReference("bearer", document)] = [] }); }); // end-snippet diff --git a/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs b/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs index 316c5729ee..dbb3c212cb 100644 --- a/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs +++ b/test/WebSites/DocumentationSnippets/ItemSchemaFilter.cs @@ -1,5 +1,5 @@ -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Models; +using System.Text.Json.Nodes; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -7,13 +7,16 @@ namespace DocumentationSnippets; // begin-snippet: Annotations-SchemaFilter public class ItemSchemaFilter : ISchemaFilter { - public void Apply(OpenApiSchema schema, SchemaFilterContext context) + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) { - schema.Example = new OpenApiObject + if (schema is OpenApiSchema concrete) { - ["Id"] = new OpenApiInteger(1), - ["Description"] = new OpenApiString("An awesome item") - }; + concrete.Example = new JsonObject + { + ["Id"] = 1, + ["Description"] = "An awesome item" + }; + } } } // end-snippet diff --git a/test/WebSites/DocumentationSnippets/Program.cs b/test/WebSites/DocumentationSnippets/Program.cs index 340d5ce89d..3bc15b890c 100644 --- a/test/WebSites/DocumentationSnippets/Program.cs +++ b/test/WebSites/DocumentationSnippets/Program.cs @@ -1,5 +1,5 @@ // begin-snippet: README-configure -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; var builder = WebApplication.CreateBuilder(args); diff --git a/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs b/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs index 6e5bc21460..e939ccda0c 100644 --- a/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs +++ b/test/WebSites/DocumentationSnippets/SecurityRequirementsOperationFilter.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Authorization; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -23,10 +23,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); - var scheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var scheme = new OpenApiSecuritySchemeReference("oauth2", context.Document); operation.Security = [ diff --git a/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs b/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs index c9d9806733..562b18759e 100644 --- a/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs +++ b/test/WebSites/DocumentationSnippets/TagDescriptionsDocumentFilter.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace DocumentationSnippets; @@ -8,11 +8,11 @@ public class TagDescriptionsDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags = - [ - new OpenApiTag { Name = "Products", Description = "Browse/manage the product catalog" }, - new OpenApiTag { Name = "Orders", Description = "Submit orders" } - ]; + swaggerDoc.Tags = new HashSet() + { + new() { Name = "Products", Description = "Browse/manage the product catalog" }, + new() { Name = "Orders", Description = "Submit orders" } + }; } } // end-snippet diff --git a/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs b/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs index 3181fe7b13..8e096e4fef 100644 --- a/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs +++ b/test/WebSites/DocumentationSnippets/WebApplicationExtensions.cs @@ -1,5 +1,4 @@ using Microsoft.OpenApi; -using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerUI; namespace DocumentationSnippets; @@ -216,6 +215,13 @@ public static void Configure(WebApplication app) }); // end-snippet + // begin-snippet: Swagger-OpenAPI3.1 + app.UseSwagger(options => + { + options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1; + }); + // end-snippet + // begin-snippet: Swagger-Swagger2.0 app.UseSwagger(options => { diff --git a/test/WebSites/GenericControllers/Startup.cs b/test/WebSites/GenericControllers/Startup.cs index 4a1871740a..23eff3af3a 100644 --- a/test/WebSites/GenericControllers/Startup.cs +++ b/test/WebSites/GenericControllers/Startup.cs @@ -1,5 +1,5 @@ using System.Reflection; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; namespace GenericControllers; diff --git a/test/WebSites/GenericControllers/Swagger/ApplySummariesOperationFilter.cs b/test/WebSites/GenericControllers/Swagger/ApplySummariesOperationFilter.cs index 9236b076fd..2cbc4ae3bc 100644 --- a/test/WebSites/GenericControllers/Swagger/ApplySummariesOperationFilter.cs +++ b/test/WebSites/GenericControllers/Swagger/ApplySummariesOperationFilter.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace GenericControllers.Swagger; diff --git a/test/WebSites/MultipleVersions/ConfigureSwaggerGenOptions.cs b/test/WebSites/MultipleVersions/ConfigureSwaggerGenOptions.cs index 01af933c06..d7900570e5 100644 --- a/test/WebSites/MultipleVersions/ConfigureSwaggerGenOptions.cs +++ b/test/WebSites/MultipleVersions/ConfigureSwaggerGenOptions.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace MultipleVersions; diff --git a/test/WebSites/MvcWithNullable/MvcWithNullable.csproj b/test/WebSites/MvcWithNullable/MvcWithNullable.csproj index f4465a728b..80a6a0d685 100644 --- a/test/WebSites/MvcWithNullable/MvcWithNullable.csproj +++ b/test/WebSites/MvcWithNullable/MvcWithNullable.csproj @@ -13,12 +13,4 @@
- - - - - - - - diff --git a/test/WebSites/NswagClientExample/NswagClientExample.csproj b/test/WebSites/NswagClientExample/NswagClientExample.csproj index 5de3bfe65a..c4e3133343 100644 --- a/test/WebSites/NswagClientExample/NswagClientExample.csproj +++ b/test/WebSites/NswagClientExample/NswagClientExample.csproj @@ -5,6 +5,7 @@ $([System.IO.Path]::Combine('$(ArtifactsPath)', 'bin', 'Swashbuckle.AspNetCore.Cli', '$(Configuration.ToLower())_$(TargetFramework)')) <_NSwagTool>$(NSwagExe_Net80) <_NSwagTool Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">$(NSwagExe_Net90) + <_NSwagTool Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net10.0'))">$(NSwagExe_Net100) diff --git a/test/WebSites/NswagClientExample/swagger_net10.0.json b/test/WebSites/NswagClientExample/swagger_net10.0.json new file mode 100644 index 0000000000..cc22ed49f6 --- /dev/null +++ b/test/WebSites/NswagClientExample/swagger_net10.0.json @@ -0,0 +1,479 @@ +{ + "openapi": "3.0.4", + "info": { + "title": "NswagClientExample", + "version": "1.0" + }, + "servers": [ + { + "url": "http://example.com" + } + ], + "paths": { + "/Animals": { + "post": { + "tags": [ + "Animals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "$ref": "#/components/schemas/Cat" + }, + { + "$ref": "#/components/schemas/Dog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/SecondLevel": { + "post": { + "tags": [ + "SecondLevel" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SubSubType" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" + } + } + } + } + } + } + }, + "/SystemTextJsonAnimals": { + "post": { + "tags": [ + "SystemTextJsonAnimals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/SystemTextJsonDefaultDiscriminatorAnimals": { + "post": { + "tags": [ + "SystemTextJsonDefaultDiscriminatorAnimals" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + }, + "text/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + }, + "application/*+json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat" + }, + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + ] + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "Animal": { + "required": [ + "animalType" + ], + "type": "object", + "properties": { + "animalType": { + "$ref": "#/components/schemas/AnimalType" + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "animalType", + "mapping": { + "Cat": "#/components/schemas/Cat", + "Dog": "#/components/schemas/Dog" + } + } + }, + "AnimalType": { + "enum": [ + "Cat", + "Dog" + ], + "type": "string" + }, + "BaseType": { + "required": [ + "discriminator" + ], + "type": "object", + "properties": { + "discriminator": { + "type": "string" + }, + "property": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "discriminator", + "mapping": { + "SubSubType": "#/components/schemas/SubSubType" + } + } + }, + "Cat": { + "allOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "Dog": { + "allOf": [ + { + "$ref": "#/components/schemas/Animal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SubSubType": { + "allOf": [ + { + "$ref": "#/components/schemas/BaseType" + }, + { + "type": "object", + "properties": { + "property2": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonAnimal": { + "required": [ + "animalType" + ], + "type": "object", + "properties": { + "animalType": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "animalType", + "mapping": { + "Cat": "#/components/schemas/SystemTextJsonCat", + "Dog": "#/components/schemas/SystemTextJsonDog" + } + } + }, + "SystemTextJsonCat": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDefaultDiscriminatorAnimal": { + "required": [ + "$type" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "animalType": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "Cat": "#/components/schemas/SystemTextJsonDefaultDiscriminatorCat", + "Dog": "#/components/schemas/SystemTextJsonDefaultDiscriminatorDog" + } + } + }, + "SystemTextJsonDefaultDiscriminatorCat": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "type": "object", + "properties": { + "catSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDefaultDiscriminatorDog": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonDefaultDiscriminatorAnimal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + }, + "SystemTextJsonDog": { + "allOf": [ + { + "$ref": "#/components/schemas/SystemTextJsonAnimal" + }, + { + "type": "object", + "properties": { + "dogSpecificProperty": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + ] + } + } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] +} \ No newline at end of file diff --git a/test/WebSites/NswagClientExample/swagger_net8.0.json b/test/WebSites/NswagClientExample/swagger_net8.0.json index e65495816b..cc22ed49f6 100644 --- a/test/WebSites/NswagClientExample/swagger_net8.0.json +++ b/test/WebSites/NswagClientExample/swagger_net8.0.json @@ -461,5 +461,19 @@ ] } } - } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] } \ No newline at end of file diff --git a/test/WebSites/NswagClientExample/swagger_net9.0.json b/test/WebSites/NswagClientExample/swagger_net9.0.json index e65495816b..cc22ed49f6 100644 --- a/test/WebSites/NswagClientExample/swagger_net9.0.json +++ b/test/WebSites/NswagClientExample/swagger_net9.0.json @@ -461,5 +461,19 @@ ] } } - } + }, + "tags": [ + { + "name": "Animals" + }, + { + "name": "SecondLevel" + }, + { + "name": "SystemTextJsonAnimals" + }, + { + "name": "SystemTextJsonDefaultDiscriminatorAnimals" + } + ] } \ No newline at end of file diff --git a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj index 52db6fe4a1..96c55309c9 100644 --- a/test/WebSites/OAuth2Integration/OAuth2Integration.csproj +++ b/test/WebSites/OAuth2Integration/OAuth2Integration.csproj @@ -10,14 +10,20 @@ - - + + + + + - - + + + + + diff --git a/test/WebSites/OAuth2Integration/ResourceServer/Swagger/SecurityRequirementsOperationFilter.cs b/test/WebSites/OAuth2Integration/ResourceServer/Swagger/SecurityRequirementsOperationFilter.cs index 86625a0c14..38b80f4c65 100644 --- a/test/WebSites/OAuth2Integration/ResourceServer/Swagger/SecurityRequirementsOperationFilter.cs +++ b/test/WebSites/OAuth2Integration/ResourceServer/Swagger/SecurityRequirementsOperationFilter.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization.Infrastructure; using Microsoft.Extensions.Options; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.SwaggerGen; namespace OAuth2Integration.ResourceServer.Swagger; @@ -40,16 +40,13 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" }); operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" }); - var oAuthScheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }; + var scheme = new OpenApiSecuritySchemeReference("oauth2", context.Document); operation.Security = [ new OpenApiSecurityRequirement { - [oAuthScheme] = requiredScopes + [scheme] = requiredScopes } ]; } diff --git a/test/WebSites/OAuth2Integration/Startup.cs b/test/WebSites/OAuth2Integration/Startup.cs index 2353899018..1983494958 100644 --- a/test/WebSites/OAuth2Integration/Startup.cs +++ b/test/WebSites/OAuth2Integration/Startup.cs @@ -1,5 +1,5 @@ using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using OAuth2Integration.ResourceServer.Swagger; namespace OAuth2Integration; @@ -69,13 +69,10 @@ public void ConfigureServices(IServiceCollection services) } }); - c.AddSecurityRequirement(new OpenApiSecurityRequirement + c.AddSecurityRequirement((document) => new OpenApiSecurityRequirement { { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" } - }, + new OpenApiSecuritySchemeReference("oauth2", document), ["readAccess", "writeAccess"] } }); diff --git a/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs b/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs index 2dcbfcea93..68045c8d5d 100644 --- a/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs +++ b/test/WebSites/TestFirst.IntegrationTests/ApiTestsSetup.cs @@ -1,4 +1,4 @@ -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Swashbuckle.AspNetCore.ApiTesting; using Xunit; diff --git a/test/WebSites/TestFirst.IntegrationTests/CreateUserTests.cs b/test/WebSites/TestFirst.IntegrationTests/CreateUserTests.cs index a6da6e08fd..8daf39da24 100644 --- a/test/WebSites/TestFirst.IntegrationTests/CreateUserTests.cs +++ b/test/WebSites/TestFirst.IntegrationTests/CreateUserTests.cs @@ -1,6 +1,6 @@ using System.Text; using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; using Newtonsoft.Json; using Swashbuckle.AspNetCore; using Swashbuckle.AspNetCore.ApiTesting.Xunit; @@ -15,7 +15,7 @@ public CreateUserTests( WebApplicationFactory webApplicationFactory) : base(apiTestRunner, webApplicationFactory, "v1-generated") { - Describe("/api/users", OperationType.Post, new OpenApiOperation + Describe("/api/users", HttpMethod.Post, new OpenApiOperation { OperationId = "CreateUser", RequestBody = new OpenApiRequestBody @@ -27,12 +27,12 @@ public CreateUserTests( Schema = new OpenApiSchema { Type = JsonSchemaTypes.Object, - Properties = new Dictionary + Properties = new Dictionary { [ "email" ] = new OpenApiSchema { Type = JsonSchemaTypes.String }, [ "password" ] = new OpenApiSchema { Type = JsonSchemaTypes.String }, }, - Required = new SortedSet { "email", "password" } + Required = new SortedSet { "email", "password" }, } } }, @@ -43,7 +43,7 @@ public CreateUserTests( [ "201" ] = new OpenApiResponse { Description = "User created", - Headers = new Dictionary + Headers = new Dictionary { [ "Location" ] = new OpenApiHeader { diff --git a/test/WebSites/WebApi.Aot/Program.cs b/test/WebSites/WebApi.Aot/Program.cs index 31f22d2cab..57c0864b56 100644 --- a/test/WebSites/WebApi.Aot/Program.cs +++ b/test/WebSites/WebApi.Aot/Program.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; using Microsoft.AspNetCore.Routing.Constraints; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi; var builder = WebApplication.CreateSlimBuilder(args); diff --git a/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs b/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs index 04d3a629d5..c8a776a460 100644 --- a/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs +++ b/test/WebSites/WebApi/EndPoints/OpenApiEndpoints.cs @@ -43,57 +43,47 @@ public static IEndpointRouteBuilder MapWithOpenApiEndpoints(this IEndpointRouteB )) .ToArray(); }) - .WithName("GetWeatherForecast") - .WithOpenApi(); + .WithName("GetWeatherForecast"); group.MapPost("/multipleForms", ([FromForm] Person person, [FromForm] Address address) => { return $"{person} {address}"; - }) - .WithOpenApi(); + }); group.MapPost("/IFromFile", (IFormFile file, string queryParameter) => { return $"{file.FileName}{queryParameter}"; - }).WithOpenApi(o => - { - var parameter = o.Parameters?.FirstOrDefault(p => string.Equals(p.Name, "queryParameter", StringComparison.OrdinalIgnoreCase)); - if (parameter is not null) - { - parameter.Description = $"{parameter.Name} Description"; - } - return o; }); group.MapPost("/IFromFileCollection", (IFormFileCollection collection) => { return $"{collection.Count} {string.Join(',', collection.Select(f => f.FileName))}"; - }).WithOpenApi(); + }); group.MapPost("/IFromBody", (OrganizationCustomExchangeRatesDto dto) => { return $"{dto}"; - }).WithOpenApi(); + }); group.MapPost("/IFromFileAndString", (IFormFile file, [FromForm] string tags) => { return $"{file.FileName}{tags}"; - }).WithOpenApi(); + }); group.MapPost("/IFromFileAndEnum", (IFormFile file, [FromForm] DateTimeKind dateTimeKind) => { return $"{file.FileName}{dateTimeKind}"; - }).WithOpenApi(); + }); group.MapPost("/IFromObjectAndString", ([FromForm] Person person, [FromForm] string tags) => { return $"{person}{tags}"; - }).WithOpenApi(); + }); app.MapGet("/TypeWithTryParse/{tryParse}", (TypeWithTryParse tryParse) => { return tryParse.Name; - }).WithOpenApi(); + }); return app; } diff --git a/test/WebSites/WebApi/EndPoints/SwaggerAnnotationsEndpoints.cs b/test/WebSites/WebApi/EndPoints/SwaggerAnnotationsEndpoints.cs index 05fe9c001e..7e0c02b2aa 100644 --- a/test/WebSites/WebApi/EndPoints/SwaggerAnnotationsEndpoints.cs +++ b/test/WebSites/WebApi/EndPoints/SwaggerAnnotationsEndpoints.cs @@ -17,8 +17,7 @@ public static IEndpointRouteBuilder MapAnnotationsEndpoints(this IEndpointRouteB .WithTags("Annotations") .DisableAntiforgery(); - group.MapPost("/fruit/{id}", CreateFruit) - .WithOpenApi(); + group.MapPost("/fruit/{id}", CreateFruit); group.MapPost("/singleForm", ([FromForm] PersonAnnotated person) => { diff --git a/test/WebSites/WebApi/EndPoints/XmlCommentsEndpoints.cs b/test/WebSites/WebApi/EndPoints/XmlCommentsEndpoints.cs index e5e93a9d96..0e35fca1cd 100644 --- a/test/WebSites/WebApi/EndPoints/XmlCommentsEndpoints.cs +++ b/test/WebSites/WebApi/EndPoints/XmlCommentsEndpoints.cs @@ -1,4 +1,8 @@ -namespace WebApi.EndPoints; +#if NET10_0_OR_GREATER +using Microsoft.AspNetCore.Mvc; +#endif + +namespace WebApi.EndPoints; /// /// Class of Extensions to add XmlEndpoints @@ -14,6 +18,12 @@ public static IEndpointRouteBuilder MapXmlCommentsEndpoints(this IEndpointRouteB group.MapGet("/Car/{id}", GetProduct); +#if NET10_0_OR_GREATER + group.MapGet("Car", GetProductAsParameters); + group.MapGet("CarWithProduces",GetProductWithProduces); + group.MapGet("CarWithProducesDefaultResponseType",GetProductProducesDefaultResponseType); +#endif + return app; } /// @@ -23,6 +33,29 @@ public static IEndpointRouteBuilder MapXmlCommentsEndpoints(this IEndpointRouteB /// A Product Id private static Product GetProduct(int id) => new() { Id = id, Description = "A product" }; + +#if NET10_0_OR_GREATER + /// + /// Returns a specific product using asParameters record + /// + [ProducesResponseType(typeof(Product), 200, Description = "A Product")] + private static Product GetProductAsParameters([AsParameters] Product productAsParameters) + => productAsParameters; + + /// + /// Returns a specific product With Produces attribute + /// + [Produces(typeof(Product), Description = "A Product")] + private static Product GetProductWithProduces(int id) + => new() { Id = id, Description = "A product" }; + + /// + /// Returns a specific product With ProducesDefaultResponseType attribute + /// + [ProducesDefaultResponseType(typeof(Product), Description = "A Product")] + private static Product GetProductProducesDefaultResponseType(int id) + => new() { Id = id, Description = "A product" }; +#endif } /// diff --git a/test/WebSites/WebApi/WebApi.csproj b/test/WebSites/WebApi/WebApi.csproj index c509238d96..86cbcaf1fd 100644 --- a/test/WebSites/WebApi/WebApi.csproj +++ b/test/WebSites/WebApi/WebApi.csproj @@ -14,12 +14,4 @@ - - - - - - - - From 839d18a9b0fa9881fc1028426b1b3e8feb0225a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:26:06 +0000 Subject: [PATCH 609/680] Bump actions/dependency-review-action from 4.8.1 to 4.8.2 (#3643) Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 4.8.1 to 4.8.2. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/40c09b7dc99638e5ddb0bfd91c1673effc064d8a...3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.8.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 56 ++++++++++++------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b5847a4f55..027d57ef32 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,28 +1,28 @@ -name: dependency-review - -on: - pull_request: - branches: [ master, dotnet-vnext ] - -permissions: {} - -jobs: - dependency-review: - runs-on: ubuntu-latest - - permissions: - contents: read - - steps: - - - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - filter: 'tree:0' - persist-credentials: false - show-progress: false - - - name: Review dependencies - uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 - with: - allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' +name: dependency-review + +on: + pull_request: + branches: [ master, dotnet-vnext ] + +permissions: {} + +jobs: + dependency-review: + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + + - name: Checkout code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + filter: 'tree:0' + persist-credentials: false + show-progress: false + + - name: Review dependencies + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 + with: + allow-licenses: '(MPL-2.0 OR Apache-2.0),0BSD,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MIT,Python-2.0,LicenseRef-scancode-generic-cla AND MIT' From 64a36ebfb2eeb3ded39e5f6d86ce126447c69198 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:04:51 +0000 Subject: [PATCH 610/680] Bump the xunit group with 1 update (#3644) Bumps Verify.XunitV3 from 31.4.3 to 31.5.3 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.5.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index bbb67b60ea..71a27c41b2 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 70e4cb3c72766d18ebfbba8001b9144197b660f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:07:52 +0000 Subject: [PATCH 611/680] Bump version (#3645) Bump version to 10.0.1 for the next release. Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] --- Directory.Build.props | 4 +- .../PublicAPI/PublicAPI.Shipped.txt | 8 ++- .../PublicAPI/PublicAPI.Unshipped.txt | 7 +-- .../PublicAPI/PublicAPI.Shipped.txt | 3 +- .../PublicAPI/PublicAPI.Unshipped.txt | 2 +- .../PublicAPI/PublicAPI.Shipped.txt | 32 +++++++++++- .../PublicAPI/PublicAPI.Unshipped.txt | 31 +---------- .../PublicAPI/PublicAPI.Shipped.txt | 5 ++ .../PublicAPI/PublicAPI.Unshipped.txt | 6 +-- .../PublicAPI/PublicAPI.Shipped.txt | 51 ++++++++++++++++++- .../PublicAPI/PublicAPI.Unshipped.txt | 48 +---------------- 11 files changed, 101 insertions(+), 96 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0ba6096f29..7f1dca9fee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,7 @@ package-readme.md See $(PackageProjectUrl)/releases for details. false - 9.0.6 + 10.0.0 true git $(PackageProjectUrl).git @@ -37,7 +37,7 @@ snupkg true true - 10.0.0 + 10.0.1 false diff --git a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt index 95e1451167..84f67d2738 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Shipped.txt @@ -1,16 +1,22 @@ -Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions +Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions static Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions.EnableAnnotations(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions options) -> void static Microsoft.Extensions.DependencyInjection.AnnotationsSwaggerGenOptionsExtensions.EnableAnnotations(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions options, bool enableAnnotationsForInheritance, bool enableAnnotationsForPolymorphism) -> void +static Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.ApplySwaggerOperationFilterAttributes(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Collections.Generic.IEnumerable controllerAndActionAttributes) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.AnnotationsDocumentFilter() -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.AnnotationsOperationFilter() -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.AnnotationsParameterFilter() -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.AnnotationsRequestBodyFilter() -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.AnnotationsSchemaFilter(System.IServiceProvider serviceProvider) -> void +Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute.PropertyName.get -> string Swashbuckle.AspNetCore.Annotations.SwaggerDiscriminatorAttribute.PropertyName.set -> void diff --git a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt index 72edd514e5..5f282702bb 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.Annotations/PublicAPI/PublicAPI.Unshipped.txt @@ -1,6 +1 @@ -static Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.ApplySwaggerOperationFilterAttributes(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Collections.Generic.IEnumerable controllerAndActionAttributes) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void -Swashbuckle.AspNetCore.Annotations.AnnotationsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void + \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt index c36b8bf81c..ba30b7d9ed 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Shipped.txt @@ -1,3 +1,4 @@ -Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture +Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.ApiTestFixture(Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase apiTestRunner, Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory webAppFactory, string documentName) -> void +Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.Describe(string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operationSpec) -> void Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.TestAsync(string operationId, string expectedStatusCode, System.Net.Http.HttpRequestMessage request) -> System.Threading.Tasks.Task diff --git a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt index 4b3b9b0954..5f282702bb 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting.Xunit/PublicAPI/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Swashbuckle.AspNetCore.ApiTesting.Xunit.ApiTestFixture.Describe(string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operationSpec) -> void + \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt index 476a5fce9e..b49ca145b4 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Shipped.txt @@ -1,7 +1,11 @@ + static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.AddOpenApiFile(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName, string filePath) -> void +static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.GetOpenApiDocument(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName) -> Microsoft.OpenApi.OpenApiDocument Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ApiTestRunnerBase() -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Configure(System.Action setupAction) -> void +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ConfigureOperation(string documentName, string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operation) -> void +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Dispose() -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.TestAsync(string documentName, string operationId, string expectedStatusCode, System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpClient httpClient) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.ApiTestRunnerOptions() -> void @@ -10,43 +14,69 @@ Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.FileOutputRoot.get -> str Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.FileOutputRoot.set -> void Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.GenerateOpenApiFiles.get -> bool Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.GenerateOpenApiFiles.set -> void +Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.OpenApiDocs.get -> System.Collections.Generic.Dictionary Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions Swashbuckle.AspNetCore.ApiTesting.ContentDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.ContentDoesNotMatchSpecException.ContentDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.HttpHeadersExtensions Swashbuckle.AspNetCore.ApiTesting.IContentValidator Swashbuckle.AspNetCore.ApiTesting.IContentValidator.CanValidate(string mediaType) -> bool +Swashbuckle.AspNetCore.ApiTesting.IContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void Swashbuckle.AspNetCore.ApiTesting.IJsonValidator +Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool +Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator +Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.JsonAllOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator +Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.JsonAnyOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator +Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.JsonArrayValidator(Swashbuckle.AspNetCore.ApiTesting.IJsonValidator jsonValidator) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator +Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.JsonBooleanValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.CanValidate(string mediaType) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.JsonContentValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator +Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.JsonNullValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator +Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.JsonNumberValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator +Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.JsonObjectValidator(Swashbuckle.AspNetCore.ApiTesting.IJsonValidator jsonValidator) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator +Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.JsonOneOfValidator(Swashbuckle.AspNetCore.ApiTesting.JsonValidator jsonValidator) -> void +Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator +Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.JsonStringValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonValidator +Swashbuckle.AspNetCore.ApiTesting.JsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool Swashbuckle.AspNetCore.ApiTesting.JsonValidator.JsonValidator() -> void +Swashbuckle.AspNetCore.ApiTesting.JsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool Swashbuckle.AspNetCore.ApiTesting.OpenApiDocumentExtensions Swashbuckle.AspNetCore.ApiTesting.RequestDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.RequestDoesNotMatchSpecException.RequestDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.RequestValidator Swashbuckle.AspNetCore.ApiTesting.RequestValidator.RequestValidator(System.Collections.Generic.IEnumerable contentValidators) -> void +Swashbuckle.AspNetCore.ApiTesting.RequestValidator.Validate(System.Net.Http.HttpRequestMessage request, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType) -> void Swashbuckle.AspNetCore.ApiTesting.ResponseDoesNotMatchSpecException Swashbuckle.AspNetCore.ApiTesting.ResponseDoesNotMatchSpecException.ResponseDoesNotMatchSpecException(string message) -> void Swashbuckle.AspNetCore.ApiTesting.ResponseValidator Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.ResponseValidator(System.Collections.Generic.IEnumerable contentValidators) -> void - +Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.Validate(System.Net.Http.HttpResponseMessage response, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType, string expectedStatusCode) -> void diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt index 2e5e2aa1a7..5f282702bb 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.ApiTesting/PublicAPI/PublicAPI.Unshipped.txt @@ -1,30 +1 @@ -static Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptionsExtensions.GetOpenApiDocument(this Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions options, string documentName) -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.ConfigureOperation(string documentName, string pathTemplate, System.Net.Http.HttpMethod operationType, Microsoft.OpenApi.OpenApiOperation operation) -> void -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerBase.Dispose() -> void -Swashbuckle.AspNetCore.ApiTesting.ApiTestRunnerOptions.OpenApiDocs.get -> System.Collections.Generic.Dictionary -Swashbuckle.AspNetCore.ApiTesting.IContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void -Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.IJsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonAllOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonAnyOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonArrayValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonBooleanValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonContentValidator.Validate(Microsoft.OpenApi.OpenApiMediaType mediaTypeSpec, Microsoft.OpenApi.OpenApiDocument openApiDocument, System.Net.Http.HttpContent content) -> void -Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonNullValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonNumberValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonObjectValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonOneOfValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonStringValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonValidator.CanValidate(Microsoft.OpenApi.IOpenApiSchema schema) -> bool -Swashbuckle.AspNetCore.ApiTesting.JsonValidator.Validate(Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.OpenApi.OpenApiDocument openApiDocument, Newtonsoft.Json.Linq.JToken instance, out System.Collections.Generic.IEnumerable errorMessages) -> bool -Swashbuckle.AspNetCore.ApiTesting.RequestValidator.Validate(System.Net.Http.HttpRequestMessage request, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType) -> void -Swashbuckle.AspNetCore.ApiTesting.ResponseValidator.Validate(System.Net.Http.HttpResponseMessage response, Microsoft.OpenApi.OpenApiDocument openApiDocument, string pathTemplate, System.Net.Http.HttpMethod operationType, string expectedStatusCode) -> void + \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt index a0d2fb49b5..0b63686881 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Shipped.txt @@ -7,19 +7,24 @@ static Microsoft.AspNetCore.Builder.SwaggerBuilderExtensions.UseSwagger(this Mic static Microsoft.Extensions.DependencyInjection.SwaggerOptionsExtensions.SetCustomDocumentSerializer(this Swashbuckle.AspNetCore.Swagger.SwaggerOptions swaggerOptions, params object[] constructorParameters) -> void static Microsoft.Extensions.DependencyInjection.SwaggerServiceCollectionExtensions.ConfigureSwagger(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action setupAction) -> void Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider +Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentMetadataProvider.GetDocumentNames() -> System.Collections.Generic.IList Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer +Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer.SerializeDocument(Microsoft.OpenApi.OpenApiDocument document, Microsoft.OpenApi.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void Swashbuckle.AspNetCore.Swagger.ISwaggerProvider +Swashbuckle.AspNetCore.Swagger.ISwaggerProvider.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.OpenApiVersion.set -> void +Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.PreSerializeFilters.get -> System.Collections.Generic.List> Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.SwaggerEndpointOptions() -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.get -> Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer Swashbuckle.AspNetCore.Swagger.SwaggerOptions.CustomDocumentSerializer.set -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.get -> Microsoft.OpenApi.OpenApiSpecVersion Swashbuckle.AspNetCore.Swagger.SwaggerOptions.OpenApiVersion.set -> void +Swashbuckle.AspNetCore.Swagger.SwaggerOptions.PreSerializeFilters.get -> System.Collections.Generic.List> Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.get -> string Swashbuckle.AspNetCore.Swagger.SwaggerOptions.RouteTemplate.set -> void Swashbuckle.AspNetCore.Swagger.SwaggerOptions.SwaggerOptions() -> void diff --git a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt index f5d8dc9185..5f282702bb 100644 --- a/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.Swagger/PublicAPI/PublicAPI.Unshipped.txt @@ -1,5 +1 @@ -Swashbuckle.AspNetCore.Swagger.IAsyncSwaggerProvider.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.Swagger.ISwaggerDocumentSerializer.SerializeDocument(Microsoft.OpenApi.OpenApiDocument document, Microsoft.OpenApi.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion specVersion) -> void -Swashbuckle.AspNetCore.Swagger.ISwaggerProvider.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.Swagger.SwaggerEndpointOptions.PreSerializeFilters.get -> System.Collections.Generic.List> -Swashbuckle.AspNetCore.Swagger.SwaggerOptions.PreSerializeFilters.get -> System.Collections.Generic.List> + \ No newline at end of file diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt index 5d4ff3cf46..29bfed397f 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Shipped.txt @@ -1,4 +1,4 @@ -Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions +Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions Microsoft.Extensions.DependencyInjection.SwaggerGenServiceCollectionExtensions static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddDocumentAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddDocumentFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void @@ -9,17 +9,23 @@ static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddP static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddRequestBodyAsyncFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddRequestBodyFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSchemaFilterInstance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, TFilter filterInstance) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityDefinition(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.IOpenApiSecurityScheme securityScheme) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityRequirement(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func securityRequirement) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddServer(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, Microsoft.OpenApi.OpenApiServer server) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.CustomOperationIds(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func operationIdSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.CustomSchemaIds(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaIdSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DescribeAllParametersInCamelCase(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocInclusionPredicate(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func predicate) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.DocumentFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IgnoreObsoleteActions(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IgnoreObsoleteProperties(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string filePath, bool includeControllerXmlComments = false) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func xmlDocFactory, bool includeControllerXmlComments = false) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.IncludeXmlComments(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Reflection.Assembly assembly, bool includeControllerXmlComments = false) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.InferSecuritySchemes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func, System.Collections.Generic.IDictionary> securitySchemesSelector = null) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Type type, System.Func schemaFactory) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaFactory) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.NonNullableReferenceTypesAsRequired(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationAsyncFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.OperationFilter(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, params object[] arguments) -> void @@ -35,6 +41,7 @@ static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.Sele static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SelectSubTypesUsing(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func> customSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SortSchemasWith(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Collections.Generic.IComparer schemaComparer) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SupportNonNullableReferenceTypes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void +static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SwaggerDoc(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.OpenApiInfo info) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.TagActionsBy(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func> tagsSelector) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.UseAllOfForInheritance(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.UseAllOfToExtendReferenceSchemas(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions) -> void @@ -57,6 +64,8 @@ static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.GetInlineAndMetada static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.IsDictionaryValueNonNullable(this System.Reflection.MemberInfo memberInfo) -> bool static Swashbuckle.AspNetCore.SwaggerGen.MemberInfoExtensions.IsNonNullableReferenceType(this System.Reflection.MemberInfo memberInfo) -> bool static Swashbuckle.AspNetCore.SwaggerGen.MethodInfoExtensions.GetUnderlyingGenericTypeMethod(this System.Reflection.MethodInfo constructedTypeMethod) -> System.Reflection.MethodInfo +static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyRouteConstraints(this Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo) -> void +static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyValidationAttributes(this Microsoft.OpenApi.IOpenApiSchema schema, System.Collections.Generic.IEnumerable customAttributes) -> void static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.HasAttribute(this System.Reflection.PropertyInfo property) -> bool static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.IsPubliclyReadable(this System.Reflection.PropertyInfo property) -> bool static Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions.IsPubliclyWritable(this System.Reflection.PropertyInfo property) -> bool @@ -123,15 +132,25 @@ Swashbuckle.AspNetCore.SwaggerGen.FilterDescriptor.Type.get -> System.Type Swashbuckle.AspNetCore.SwaggerGen.FilterDescriptor.Type.set -> void Swashbuckle.AspNetCore.SwaggerGen.IDictionary Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter +Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter +Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter +Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter +Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter +Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter +Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter +Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter +Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter +Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator +Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver.GetDataContractForType(System.Type type) -> Swashbuckle.AspNetCore.SwaggerGen.DataContract Swashbuckle.AspNetCore.SwaggerGen.JsonSerializerDataContractResolver @@ -144,13 +163,17 @@ Swashbuckle.AspNetCore.SwaggerGen.MethodInfoExtensions Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.ApiDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription +Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.MethodInfo.get -> System.Reflection.MethodInfo +Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.OperationFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription apiDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaRegistry, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.MethodInfo methodInfo) -> void Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ApiParameterDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription +Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.DocumentName.get -> string +Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription apiParameterDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.PropertyInfo propertyInfo = null, System.Reflection.ParameterInfo parameterInfo = null) -> void Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterInfo.get -> System.Reflection.ParameterInfo Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.PropertyInfo.get -> System.Reflection.PropertyInfo Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator @@ -158,8 +181,10 @@ Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.PropertyInfoExtensions Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.BodyParameterDescription.get -> Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription +Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.FormParameterDescriptions.get -> System.Collections.Generic.IEnumerable +Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.RequestBodyFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription bodyParameterDescription, System.Collections.Generic.IEnumerable formParameterDescriptions, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document) -> void Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.SchemaGenerator.get -> Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext @@ -171,8 +196,10 @@ Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.SchemaGenerator.get -> Swa Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.SchemaRepository.get -> Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext.Type.get -> System.Type Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator +Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.SchemaGenerator(Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions generatorOptions, Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver serializerDataContractResolver) -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions +Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.get -> System.Collections.Generic.IDictionary> Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.set -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.DiscriminatorNameSelector.get -> System.Func Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.DiscriminatorNameSelector.set -> void @@ -200,14 +227,19 @@ Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseInlineDefinitionsFor Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseOneOfForPolymorphism.get -> bool Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.UseOneOfForPolymorphism.set -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.AddDefinition(string schemaId, Microsoft.OpenApi.OpenApiSchema schema) -> Microsoft.OpenApi.OpenApiSchemaReference Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.DocumentName.get -> string Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.RegisterType(System.Type type, string schemaId) -> void Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.SchemaRepository(string documentName = null) -> void +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.Schemas.get -> System.Collections.Generic.Dictionary +Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.TryLookupByType(System.Type type, out Microsoft.OpenApi.OpenApiSchemaReference referenceSchema) -> bool Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention.Apply(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel application) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerApplicationConvention.SwaggerApplicationConvention() -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetDocumentNames() -> System.Collections.Generic.IList +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.SwaggerGenerator(Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options, Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.SwaggerGenerator(Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options, Microsoft.AspNetCore.Mvc.ApiExplorer.IApiDescriptionGroupCollectionProvider apiDescriptionsProvider, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider authenticationSchemeProvider) -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException @@ -246,12 +278,17 @@ Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.RequestBodyFilters.get Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.RequestBodyFilters.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SchemaComparer.get -> System.Collections.Generic.IComparer Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SchemaComparer.set -> void +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.get -> System.Collections.Generic.IList> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.set -> void +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.get -> System.Collections.Generic.IDictionary Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.set -> void +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.get -> System.Func, System.Collections.Generic.IDictionary> Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.set -> void +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.get -> System.Collections.Generic.List Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SortKeySelector.get -> System.Func Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SortKeySelector.set -> void +Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.get -> System.Collections.Generic.IDictionary Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.set -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerGeneratorOptions() -> void Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.TagsSelector.get -> System.Func> @@ -276,10 +313,20 @@ Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions.SwaggerGeneratorOptions.set Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions.SwaggerGenOptions() -> void Swashbuckle.AspNetCore.SwaggerGen.TypeExtensions Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.XmlCommentsDocumentFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsNodeNameHelper Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsNodeNameHelper.XmlCommentsNodeNameHelper() -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.XmlCommentsOperationFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.XmlCommentsParameterFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.XmlCommentsRequestBodyFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void +Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.XmlCommentsSchemaFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsTextHelper diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt index 7e384c4c92..5f282702bb 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/PublicAPI/PublicAPI.Unshipped.txt @@ -1,47 +1 @@ -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityDefinition(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.IOpenApiSecurityScheme securityScheme) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddSecurityRequirement(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func securityRequirement) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.AddServer(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, Microsoft.OpenApi.OpenApiServer server) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.InferSecuritySchemes(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func, System.Collections.Generic.IDictionary> securitySchemesSelector = null) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Type type, System.Func schemaFactory) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.MapType(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, System.Func schemaFactory) -> void -static Microsoft.Extensions.DependencyInjection.SwaggerGenOptionsExtensions.SwaggerDoc(this Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenOptions swaggerGenOptions, string name, Microsoft.OpenApi.OpenApiInfo info) -> void -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyRouteConstraints(this Microsoft.OpenApi.IOpenApiSchema schema, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo) -> void -static Swashbuckle.AspNetCore.SwaggerGen.OpenApiSchemaExtensions.ApplyValidationAttributes(this Microsoft.OpenApi.IOpenApiSchema schema, System.Collections.Generic.IEnumerable customAttributes) -> void -Swashbuckle.AspNetCore.SwaggerGen.IDocumentAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.SwaggerGen.IDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.IOperationAsyncFilter.ApplyAsync(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.IParameterAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.SwaggerGen.IParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyAsyncFilter.ApplyAsync(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.SwaggerGen.IRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema -Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext.OperationFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescription apiDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaRegistry, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.MethodInfo methodInfo) -> void -Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext.ParameterFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription apiParameterDescription, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document, System.Reflection.PropertyInfo propertyInfo = null, System.Reflection.ParameterInfo parameterInfo = null) -> void -Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.Document.get -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext.RequestBodyFilterContext(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription bodyParameterDescription, System.Collections.Generic.IEnumerable formParameterDescriptions, Swashbuckle.AspNetCore.SwaggerGen.ISchemaGenerator schemaGenerator, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, Microsoft.OpenApi.OpenApiDocument document) -> void -Swashbuckle.AspNetCore.SwaggerGen.SchemaGenerator.GenerateSchema(System.Type modelType, Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository schemaRepository, System.Reflection.MemberInfo memberInfo = null, System.Reflection.ParameterInfo parameterInfo = null, Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterRouteInfo routeInfo = null) -> Microsoft.OpenApi.IOpenApiSchema -Swashbuckle.AspNetCore.SwaggerGen.SchemaGeneratorOptions.CustomTypeMappings.get -> System.Collections.Generic.IDictionary> -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.AddDefinition(string schemaId, Microsoft.OpenApi.OpenApiSchema schema) -> Microsoft.OpenApi.OpenApiSchemaReference -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.Schemas.get -> System.Collections.Generic.Dictionary -Swashbuckle.AspNetCore.SwaggerGen.SchemaRepository.TryLookupByType(System.Type type, out Microsoft.OpenApi.OpenApiSchemaReference referenceSchema) -> bool -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(string documentName, string host = null, string basePath = null) -> Microsoft.OpenApi.OpenApiDocument -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwaggerAsync(string documentName, string host = null, string basePath = null) -> System.Threading.Tasks.Task -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecurityRequirements.get -> System.Collections.Generic.IList> -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemes.get -> System.Collections.Generic.IDictionary -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SecuritySchemesSelector.get -> System.Func, System.Collections.Generic.IDictionary> -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.Servers.get -> System.Collections.Generic.List -Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions.SwaggerDocs.get -> System.Collections.Generic.IDictionary -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.Apply(Microsoft.OpenApi.OpenApiDocument swaggerDoc, Swashbuckle.AspNetCore.SwaggerGen.DocumentFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsDocumentFilter.XmlCommentsDocumentFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.Apply(Microsoft.OpenApi.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsOperationFilter.XmlCommentsOperationFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.Apply(Microsoft.OpenApi.IOpenApiParameter parameter, Swashbuckle.AspNetCore.SwaggerGen.ParameterFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter.XmlCommentsParameterFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.Apply(Microsoft.OpenApi.IOpenApiRequestBody requestBody, Swashbuckle.AspNetCore.SwaggerGen.RequestBodyFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsRequestBodyFilter.XmlCommentsRequestBodyFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.Apply(Microsoft.OpenApi.IOpenApiSchema schema, Swashbuckle.AspNetCore.SwaggerGen.SchemaFilterContext context) -> void -Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsSchemaFilter.XmlCommentsSchemaFilter(System.Collections.Generic.IReadOnlyDictionary xmlDocMembers, Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorOptions options) -> void + \ No newline at end of file From 5bb0e7684ad9ef4724e97e4f1f4faf43ac08ed50 Mon Sep 17 00:00:00 2001 From: martincostello Date: Tue, 11 Nov 2025 17:59:55 +0000 Subject: [PATCH 612/680] Prepare for OpenAPI.NET 3.0 Cherry-pick some changes from #3646 to reduce the diff. --- Directory.Packages.props | 8 ++++---- .../XmlCommentsBenchmark.cs | 2 +- .../RequestValidator.cs | 2 +- .../ResponseValidator.cs | 2 +- .../SwaggerGenerator/SwaggerGenerator.cs | 11 +++++++++-- .../XmlComments/XmlCommentsRequestBodyFilter.cs | 10 ++++++++-- .../SwaggerGenerator/SwaggerGeneratorTests.cs | 12 ++++++------ .../VerifyTests.cs | 12 ++++++------ 8 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 71a27c41b2..7e329063c0 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,7 @@ - + @@ -16,11 +16,11 @@ - - + + - + diff --git a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs index ccdb2cbb76..6f1bab55a9 100644 --- a/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs +++ b/perf/Swashbuckle.AspNetCore.Benchmarks/XmlCommentsBenchmark.cs @@ -117,7 +117,7 @@ public void Setup() { Content = new Dictionary() { - ["application/json"] = new() + ["application/json"] = new OpenApiMediaType() { Schema = new OpenApiSchema() { diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs index e557f15449..df13c9ec42 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/RequestValidator.cs @@ -110,7 +110,7 @@ private void ValidateContent(IOpenApiRequestBody requestBodySpec, OpenApiDocumen return; } - if (!requestBodySpec.Content.TryGetValue(content.Headers.ContentType.MediaType, out OpenApiMediaType mediaTypeSpec)) + if (!requestBodySpec.Content.TryGetValue(content.Headers.ContentType.MediaType, out var mediaTypeSpec)) { throw new RequestDoesNotMatchSpecException($"Content media type '{content.Headers.ContentType.MediaType}' is not specified"); } diff --git a/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs b/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs index 6954c352d6..06e001ce95 100644 --- a/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs +++ b/src/Swashbuckle.AspNetCore.ApiTesting/ResponseValidator.cs @@ -76,7 +76,7 @@ private void ValidateContent( throw new RequestDoesNotMatchSpecException("Expected content is not present"); } - if (!contentSpecs.TryGetValue(content.Headers.ContentType.MediaType, out OpenApiMediaType mediaTypeSpec)) + if (!contentSpecs.TryGetValue(content.Headers.ContentType.MediaType, out var mediaTypeSpec)) { throw new ResponseDoesNotMatchSpecException($"Content media type '{content.Headers.ContentType.MediaType}' is not specified"); } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs index 2f57b2dbc4..f35e0a54b5 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SwaggerGenerator/SwaggerGenerator.cs @@ -456,7 +456,11 @@ private async Task GenerateOpenApiOperationFromMetadataAsync( { foreach (var contentType in requestContentTypes) { - var contentTypeValue = operation.RequestBody.Content[contentType]; + if (operation.RequestBody.Content[contentType] is not OpenApiMediaType contentTypeValue) + { + continue; + } + var fromFormParameters = apiDescription.ParameterDescriptions.Where((p) => p.IsFromForm()).ToList(); ApiParameterDescription bodyParameterDescription = null; if (fromFormParameters.Count > 0) @@ -518,7 +522,10 @@ private async Task GenerateOpenApiOperationFromMetadataAsync( { foreach (var content in responseContentTypes) { - content.Schema = GenerateSchema(responseModel.Type, schemaRepository); + if (content is OpenApiMediaType mediaType) + { + mediaType.Schema = GenerateSchema(responseModel.Type, schemaRepository); + } } } } diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs index 4eccf74285..ffa21d19be 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/XmlComments/XmlCommentsRequestBodyFilter.cs @@ -104,7 +104,10 @@ private void ApplyPropertyTagsForBody(IOpenApiRequestBody requestBody, RequestBo foreach (var mediaType in requestBody.Content.Values) { - mediaType.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, mediaType.Schema, example); + if (mediaType is OpenApiMediaType concrete) + { + concrete.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, concrete.Schema, example); + } } } @@ -160,7 +163,10 @@ private void ApplyParamTagsForBody(IOpenApiRequestBody requestBody, RequestBodyF foreach (var mediaType in requestBody.Content.Values) { - mediaType.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, mediaType.Schema, example); + if (mediaType is OpenApiMediaType concrete) + { + concrete.Example = XmlCommentsExampleHelper.Create(context.SchemaRepository, concrete.Schema, example); + } } } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs index a48e8afa91..69b88d73f5 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SwaggerGenerator/SwaggerGeneratorTests.cs @@ -210,7 +210,7 @@ public void GetSwagger_GenerateProducesSchemas_ForProvidedOpenApiOperation() { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -266,7 +266,7 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationAndApp { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } }, Parameters = @@ -2318,7 +2318,7 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSe { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -2383,7 +2383,7 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIF { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -2445,7 +2445,7 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithIF { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -2509,7 +2509,7 @@ public void GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperationWithSt { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs index a8ae6dd7ab..02bc8e8ac1 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/VerifyTests.cs @@ -141,7 +141,7 @@ public async Task ActionWithProducesAttributeAndProvidedOpenApiOperation() { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -192,7 +192,7 @@ public async Task ActionWithConsumesAttributeAndProvidedOpenApiOperation() { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -1266,7 +1266,7 @@ public async Task GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperation { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -1323,7 +1323,7 @@ public async Task GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperation { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -1374,7 +1374,7 @@ public async Task GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperation { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } @@ -1425,7 +1425,7 @@ public async Task GetSwagger_GenerateConsumesSchemas_ForProvidedOpenApiOperation { Content = new Dictionary() { - ["application/someMediaType"] = new() + ["application/someMediaType"] = new OpenApiMediaType() } } } From 11db6ef2b58432f85f8ec6fcfefb1ca824b172cf Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Wed, 12 Nov 2025 14:20:50 +0000 Subject: [PATCH 613/680] Fix exception sorting operation tags (#3652) Fix `SwaggerGeneratorException` caused by an `InvalidOperationException` when attempting to compare `OpenApiTagReference` instances in a `SortedSet`. Resolves #3650. --- src/Shared/OpenApiTagComparer.cs | 124 ++++++++++++++++++ .../AnnotationsDocumentFilter.cs | 2 +- .../AnnotationsOperationFilter.cs | 2 +- .../Swashbuckle.AspNetCore.Annotations.csproj | 4 + .../AnnotationsOperationFilterTests.cs | 2 +- .../FakeControllerWithSwaggerAnnotations.cs | 2 +- 6 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/Shared/OpenApiTagComparer.cs diff --git a/src/Shared/OpenApiTagComparer.cs b/src/Shared/OpenApiTagComparer.cs new file mode 100644 index 0000000000..2ba16c29b9 --- /dev/null +++ b/src/Shared/OpenApiTagComparer.cs @@ -0,0 +1,124 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + + * MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +// Adapted from https://github.com/microsoft/OpenAPI.NET/blob/3b61b45991dded1aaecb16330430628d26a406de/src/Microsoft.OpenApi/OpenApiTagComparer.cs#L1 + +namespace Microsoft.OpenApi; + +/// +/// This comparer is used to maintain a globally unique list of tags encountered +/// in a particular OpenAPI document. +/// +internal sealed class OpenApiTagComparer : + IComparer, + IComparer, + IEqualityComparer, + IEqualityComparer +{ + private static readonly Lazy _lazyInstance = new(() => new OpenApiTagComparer()); + + // Tag comparisons are case-sensitive by default. Although the OpenAPI specification + // only outlines case sensitivity for property names, we extend this principle to + // property values for tag names as well. + // See https://spec.openapis.org/oas/v3.1.0#format. + private static readonly StringComparer StringComparer = StringComparer.Ordinal; + + /// + /// Default instance for the comparer. + /// + internal static OpenApiTagComparer Instance => _lazyInstance.Value; + + public int Compare(IOpenApiTag x, IOpenApiTag y) + { + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x is OpenApiTagReference referenceX && y is OpenApiTagReference referenceY) + { + return StringComparer.Compare(referenceX.Name ?? referenceX.Reference.Id, referenceY.Name ?? referenceY.Reference.Id); + } + + return StringComparer.Compare(x.Name, y.Name); + } + + public int Compare(OpenApiTagReference x, OpenApiTagReference y) + { + if (x is null) + { + return -1; + } + + if (y is null) + { + return 1; + } + + if (ReferenceEquals(x, y)) + { + return 0; + } + + return StringComparer.Compare(x.Name ?? x.Reference.Id, y.Name ?? y.Reference.Id); + } + + /// + public bool Equals(IOpenApiTag x, IOpenApiTag y) => Compare(x, y) == 0; + + /// + public bool Equals(OpenApiTagReference x, OpenApiTagReference y) => Compare(x, y) == 0; + + /// + public int GetHashCode(IOpenApiTag obj) + { + string value = obj?.Name; + + if (value is null && obj is OpenApiTagReference reference) + { + value = reference.Reference.Id; + } + + return string.IsNullOrEmpty(value) ? 0 : StringComparer.GetHashCode(value); + } + + /// + public int GetHashCode(OpenApiTagReference obj) + { + string value = obj?.Name ?? obj?.Reference?.Id; + + return string.IsNullOrEmpty(value) ? 0 : StringComparer.GetHashCode(value); + } +} diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs index 412780804c..8bf1cc5ec4 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsDocumentFilter.cs @@ -8,7 +8,7 @@ public class AnnotationsDocumentFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { - swaggerDoc.Tags ??= new SortedSet(); + swaggerDoc.Tags ??= new SortedSet(OpenApiTagComparer.Instance); // Collect (unique) controller names and custom attributes in a dictionary var controllerNamesAndAttributes = context.ApiDescriptions diff --git a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs index 25c960fccc..85ef8f105d 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs +++ b/src/Swashbuckle.AspNetCore.Annotations/AnnotationsOperationFilter.cs @@ -69,7 +69,7 @@ private static void ApplySwaggerOperationAttribute( if (swaggerOperationAttribute.Tags is { } tags) { - operation.Tags = new SortedSet(tags.Select(tagName => new OpenApiTagReference(tagName))); + operation.Tags = new SortedSet(tags.Select(tagName => new OpenApiTagReference(tagName)), OpenApiTagComparer.Instance); } } diff --git a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj index 80865dbfc1..5a4c809e69 100644 --- a/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj +++ b/src/Swashbuckle.AspNetCore.Annotations/Swashbuckle.AspNetCore.Annotations.csproj @@ -24,5 +24,9 @@ + + + + diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs index 55e6009d63..389016afbc 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/AnnotationsOperationFilterTests.cs @@ -28,7 +28,7 @@ public void Apply_EnrichesOperationMetadata_IfActionDecoratedWithSwaggerOperatio Assert.Equal("Summary for ActionWithSwaggerOperationAttribute", operation.Summary); Assert.Equal("Description for ActionWithSwaggerOperationAttribute", operation.Description); Assert.Equal("actionWithSwaggerOperationAttribute", operation.OperationId); - Assert.Equal(["foobar"], [.. operation.Tags.Select(t => t.Reference.Id)]); + Assert.Equal(["bar", "foo"], [.. operation.Tags.Select(t => t.Reference.Id)]); } [Fact] diff --git a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs index ecc0efbbfc..5065d38d6b 100644 --- a/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs +++ b/test/Swashbuckle.AspNetCore.Annotations.Test/Fixtures/FakeControllerWithSwaggerAnnotations.cs @@ -10,7 +10,7 @@ internal class FakeControllerWithSwaggerAnnotations [SwaggerOperation("Summary for ActionWithSwaggerOperationAttribute", Description = "Description for ActionWithSwaggerOperationAttribute", OperationId = "actionWithSwaggerOperationAttribute", - Tags = new[] { "foobar" } + Tags = ["foo", "bar"] )] public void ActionWithSwaggerOperationAttribute() { } From b96186f7e0d1e58b3a434bac431b782117f4ac61 Mon Sep 17 00:00:00 2001 From: martincostello Date: Wed, 12 Nov 2025 15:10:01 +0000 Subject: [PATCH 614/680] Improve version table - Use named links to make it easier to add things and to make the table easier to read when making edits. - Fix some incorrect links. --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 367b800163..d019c0aed5 100644 --- a/README.md +++ b/README.md @@ -30,15 +30,57 @@ for more details. ## Compatibility -| Swashbuckle Version | ASP.NET Core | OpenAPI/Swagger Spec. | Microsoft.OpenApi | swagger-ui | Redoc | -|---------------------|------------------------|-----------------------|-----------------------|------------|-------| -| [![CI Swashbuckle.AspNetCore version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Build.props&query=(%2F%2FProject%2FPropertyGroup%2FVersionPrefix)%5B1%5D&prefix=v&suffix=-*&logo=github&label=CI)](https://www.myget.org/gallery/domaindrivendev) | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/Directory.Packages.props#L17-L18) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6) | -| [![Latest v10 Swashbuckle.AspNetCore version](https://img.shields.io/github/v/release/domaindrivendev/Swashbuckle.AspNetCore?display_name=tag&logo=github&label=v10)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/latest) | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | -| [![Last v9 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v9-v9.0.6-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6) | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.1) | -| [![Last v8 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v8-v8.1.4-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v8.1.4) | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.23) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.5.0) | -| [![Last v7 Swashbuckle.AspNetCore version](https://img.shields.io/badge/v7-v7.3.2-blue?logo=github)](https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v7.3.2) | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version](https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi)](https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.22) | [![swagger-ui version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui)](https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1) | [![Redoc version](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc)](https://github.com/Redocly/redoc/releases/tag/v2.4.0) | - -## Getting Started +| **Swashbuckle Version** | **ASP.NET Core** | **OpenAPI/Swagger Versions** | **Microsoft.OpenApi** | **swagger-ui** | **Redoc** | +|:-----------------------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------------|:---------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------| +| [![CI Swashbuckle.AspNetCore version][swashbuckle-version-vnext-badge]][swashbuckle-version-vnext-release] | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version][openapi-version-vnext-badge]][openapi-version-vnext-release] | [![swagger-ui version][swaggerui-version-vnext-badge]][swaggerui-version-vnext-release] | [![Redoc version][redoc-version-vnext-badge]][redoc-version-vnext-release] | +| [![Latest Swashbuckle.AspNetCore version][swashbuckle-version-latest-badge]][swashbuckle-version-latest-release] | >= 8.0.0 | 3.1, **3.0**, 2.0 | [![Microsoft.OpenApi version][openapi-version-latest-badge]][openapi-version-latest-release] | [![swagger-ui version][swaggerui-version-latest-badge]][swaggerui-version-latest-release] | [![Redoc version][redoc-version-latest-badge]][redoc-version-latest-release] | +| [![Last v9 Swashbuckle.AspNetCore version][swashbuckle-version-v9-badge]][swashbuckle-version-v9-release] | >= 8.0.0 | **3.0**, 2.0 | [![Microsoft.OpenApi version][openapi-version-v9-badge]][openapi-version-v9-release] | [![swagger-ui version][swaggerui-version-v9-badge]][swaggerui-version-v9-release] | [![Redoc version][redoc-version-v9-badge]][redoc-version-v9-release] | +| [![Last v8 Swashbuckle.AspNetCore version][swashbuckle-version-v8-badge]][swashbuckle-version-v8-release] | >= 8.0.0, 2.3.x | **3.0**, 2.0 | [![Microsoft.OpenApi version][openapi-version-v8-badge]][openapi-version-v8-release] | [![swagger-ui version][swaggerui-version-v8-badge]][swaggerui-version-v8-release] | [![Redoc version][redoc-version-v8-badge]][redoc-version-v8-release] | +| [![Last v7 Swashbuckle.AspNetCore version][swashbuckle-version-v7-badge]][swashbuckle-version-v7-release] | >= 8.0.0, 6.0.x, 2.1.x | **3.0**, 2.0 | [![Microsoft.OpenApi version][openapi-version-v7-badge]][openapi-version-v7-release] | [![swagger-ui version][swaggerui-version-v7-badge]][swaggerui-version-v7-release] | [![Redoc version][redoc-version-v7-badge]][redoc-version-v7-release] | + +[swashbuckle-version-vnext-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2FDirectory.Build.props&query=(%2F%2FProject%2FPropertyGroup%2FVersionPrefix)%5B1%5D&prefix=v&suffix=-*&logo=github&label=CI +[swashbuckle-version-vnext-release]: https://www.myget.org/gallery/domaindrivendev +[swashbuckle-version-latest-badge]: https://img.shields.io/github/v/release/domaindrivendev/Swashbuckle.AspNetCore?display_name=tag&logo=github&label=v10 +[swashbuckle-version-latest-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/latest +[swashbuckle-version-v9-badge]: https://img.shields.io/badge/v9-v9.0.6-blue?logo=github +[swashbuckle-version-v9-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6 +[swashbuckle-version-v8-badge]: https://img.shields.io/badge/v8-v8.1.4-blue?logo=github +[swashbuckle-version-v8-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v8.1.4 +[swashbuckle-version-v7-badge]: https://img.shields.io/badge/v7-v7.3.2-blue?logo=github +[swashbuckle-version-v7-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v7.3.2 + +[openapi-version-vnext-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.Swagger%2FSwashbuckle.AspNetCore.Swagger.csproj&query=%2F%2FPackageReference%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40VersionOverride&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj#L20 +[openapi-version-latest-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.Swagger%2FSwashbuckle.AspNetCore.Swagger.csproj&query=%2F%2FPackageReference%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40VersionOverride&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-latest-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/v2.3.0 +[openapi-version-v9-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-v9-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25 +[openapi-version-v8-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-v8-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/1.6.23 +[openapi-version-v7-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-v7-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/1.6.22 + +[swaggerui-version-vnext-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6 +[swaggerui-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-latest-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.30.2 +[swaggerui-version-v9-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-v9-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2 +[swaggerui-version-v8-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-v8-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.22.0 +[swaggerui-version-v7-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-v7-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.20.1 + +[redoc-version-vnext-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6 +[redoc-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-latest-release]: https://github.com/Redocly/redoc/releases/tag/v2.5.2 +[redoc-version-v9-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-v9-release]: https://github.com/Redocly/redoc/releases/tag/v2.5.1 +[redoc-version-v8-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv8.1.4%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-v8-release]: https://github.com/Redocly/redoc/releases/tag/v2.5.0 +[redoc-version-v7-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-v7-release]: https://github.com/Redocly/redoc/releases/tag/v2.4.0 First install the kitchen-sink NuGet package into your ASP.NET Core application: From 1d4a0e94ed7c5fdf6bfbbd70455d2afb4593beef Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Wed, 12 Nov 2025 15:30:05 +0000 Subject: [PATCH 615/680] Update migration guide Link to the specific documentation. Relates to #3648 and #3651. --- docs/migrating-to-v10.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/migrating-to-v10.md b/docs/migrating-to-v10.md index 76c859f172..3ba302518a 100644 --- a/docs/migrating-to-v10.md +++ b/docs/migrating-to-v10.md @@ -78,7 +78,7 @@ Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the foll - Replace usage of the `OpenApiSchema.Type` property using a string (e.g. `"string"` or `"boolean"`) with the `JsonSchemaType` flags enumeration. - Replace usage of the `OpenApiSchema.Nullable` property by OR-ing the `JsonSchemaType.Null` value to `OpenApiSchema.Type` (e.g. `schema.Type |= JsonSchemaType.Null;`). - Remove any use of the [now-deprecated `WithOpenApi()` extension method][withopenapi-deprecation] in [Microsoft.AspNetCore.OpenApi][microsoft-aspnetcore-openapi-package]. -- Updating any use of `AddSecurityRequirement()` to use a `Func`. +- Updating any use of `AddSecurityRequirement()` to use a `Func` ([documentation][security]). - Updating some collections to use specific concrete types instead of interfaces if collection expressions were used, for example using `HashSet` with `OpenApiDocument.Tags` which is now an `ISet`. [breaking-changes-aspnetcore]: https://learn.microsoft.com/aspnet/core/release-notes/aspnetcore-10.0?#openapi-31-breaking-changes "OpenAPI 3.1 breaking changes" @@ -87,6 +87,7 @@ Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the foll [microsoft-openapi-package]: https://www.nuget.org/packages/Microsoft.OpenApi/ "Microsoft.OpenApi NuGet package" [microsoft-openapi-v2-migration-guide]: https://github.com/microsoft/OpenAPI.NET/blob/main/docs/upgrade-guide-2.md "Microsoft OpenAPI.NET v2 migration guide" [openapi-specification]: https://swagger.io/specification/ "OpenAPI Specification" +[security]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/v10.0.0/docs/configure-and-customize-swaggergen.md#add-security-definitions-and-requirements "Add Security Definitions and Requirements" +[swashbuckle-aspnetcore-906]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6 "Swashbuckle.AspNetCore v9.0.6" [swashbuckle-aspnetcore-10]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/3283 "Support .NET 10" [withopenapi-deprecation]: https://github.com/aspnet/Announcements/issues/519 "[Breaking change]: Deprecation of WithOpenApi extension method" -[swashbuckle-aspnetcore-906]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/releases/tag/v9.0.6 "Swashbuckle.AspNetCore v9.0.6" From e28f03117bb0c4a91fd1d432bcc7fa45030a8942 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:21:20 +0000 Subject: [PATCH 616/680] Bump version (#3655) * Bump version Bump version to 10.0.2 for the next release. Signed-off-by: github-actions[bot] * Update badge links Update badges for v10.0.1. --------- Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: Martin Costello --- Directory.Build.props | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7f1dca9fee..d22989a92c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -29,7 +29,7 @@ package-readme.md See $(PackageProjectUrl)/releases for details. false - 10.0.0 + 10.0.1 true git $(PackageProjectUrl).git @@ -37,7 +37,7 @@ snupkg true true - 10.0.1 + 10.0.2 false diff --git a/README.md b/README.md index d019c0aed5..1ab4167ee1 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ for more details. [openapi-version-vnext-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.Swagger%2FSwashbuckle.AspNetCore.Swagger.csproj&query=%2F%2FPackageReference%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40VersionOverride&logo=openapiinitiative&label=Microsoft.OpenApi [openapi-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.Swagger/Swashbuckle.AspNetCore.Swagger.csproj#L20 -[openapi-version-latest-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.Swagger%2FSwashbuckle.AspNetCore.Swagger.csproj&query=%2F%2FPackageReference%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40VersionOverride&logo=openapiinitiative&label=Microsoft.OpenApi +[openapi-version-latest-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.1%2Fsrc%2FSwashbuckle.AspNetCore.Swagger%2FSwashbuckle.AspNetCore.Swagger.csproj&query=%2F%2FPackageReference%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40VersionOverride&logo=openapiinitiative&label=Microsoft.OpenApi [openapi-version-latest-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/v2.3.0 [openapi-version-v9-badge]: https://img.shields.io/badge/dynamic/xml?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2FDirectory.Packages.props&query=%2F%2FPackageVersion%5B%40Include%3D'Microsoft.OpenApi'%5D%2F%40Version&logo=openapiinitiative&label=Microsoft.OpenApi [openapi-version-v9-release]: https://github.com/microsoft/OpenAPI.NET/releases/tag/v1.6.25 @@ -62,7 +62,7 @@ for more details. [swaggerui-version-vnext-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui [swaggerui-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.SwaggerUI/package.json#L6 -[swaggerui-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui +[swaggerui-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.1%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui [swaggerui-version-latest-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.30.2 [swaggerui-version-v9-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.SwaggerUI%2Fpackage.json&query=%24.dependencies.swagger-ui-dist&style=flat&label=swagger-ui [swaggerui-version-v9-release]: https://github.com/swagger-api/swagger-ui/releases/tag/v5.29.2 @@ -73,7 +73,7 @@ for more details. [redoc-version-vnext-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2FHEAD%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc [redoc-version-vnext-release]: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/HEAD/src/Swashbuckle.AspNetCore.ReDoc/package.json#L6 -[redoc-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.0%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc +[redoc-version-latest-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv10.0.1%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc [redoc-version-latest-release]: https://github.com/Redocly/redoc/releases/tag/v2.5.2 [redoc-version-v9-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv9.0.6%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc [redoc-version-v9-release]: https://github.com/Redocly/redoc/releases/tag/v2.5.1 From f0e11161c3213bb693255680dddd38e63817dc85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:39:54 +0000 Subject: [PATCH 617/680] Bump anchore/sbom-action from 0.20.9 to 0.20.10 (#3665) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 132b29028c..0ec4a74314 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,7 +116,7 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} - name: Generate SBOM - uses: anchore/sbom-action@8e94d75ddd33f69f691467e42275782e4bfefe84 # v0.20.9 + uses: anchore/sbom-action@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 if: runner.os == 'Windows' with: artifact-name: Swashbuckle.AspNetCore.spdx.json From 493702f4aa5adcf2b5132004f63e0892023b7515 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:40:19 +0000 Subject: [PATCH 618/680] Bump github/codeql-action from 4.31.2 to 4.31.4 (#3664) --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 521a5833d3..9306ae648b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 48aa0f83d3..3b13f8b1dc 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2 + uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 with: sarif_file: results.sarif From 9248c3daeb3ce40f9ba2d8051cb2b99375e457e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:46:36 +0000 Subject: [PATCH 619/680] Bump actions/checkout from 5.0.0 to 5.0.1 (#3663) --- .github/workflows/actions-lint.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/bump-version.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-documentation.yml | 192 ++++++++++----------- 8 files changed, 103 insertions(+), 103 deletions(-) diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml index 5e5d4ef39e..f331b1dcda 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/actions-lint.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ec4a74314..03836b8e94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: } - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index 79424c58e4..e3fbdb19c2 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9306ae648b..d650713850 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 027d57ef32..b0718e23b2 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 3b13f8b1dc..1976ae506b 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bcd12c2bfe..12ca97e76a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/update-documentation.yml b/.github/workflows/update-documentation.yml index 7888d43868..c8395e1c3b 100644 --- a/.github/workflows/update-documentation.yml +++ b/.github/workflows/update-documentation.yml @@ -1,96 +1,96 @@ -name: update-documentation - -on: - push: - branches: [ master ] - paths: [ '**/*.md', 'test/WebSites/DocumentationSnippets/**' ] - workflow_dispatch: - -permissions: {} - -jobs: - update-docs: - name: update-docs - runs-on: ubuntu-latest - if: github.event.repository.fork == false - - permissions: - contents: write - pull-requests: write - - steps: - - - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - filter: 'tree:0' - persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits - show-progress: false - - - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 - - - name: Update documentation - id: update-docs - shell: pwsh - env: - DOTNET_CLI_TELEMETRY_OPTOUT: true - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - GIT_COMMIT_USER_EMAIL: '41898282+github-actions[bot]@users.noreply.github.com' - GIT_COMMIT_USER_NAME: 'github-actions[bot]' - run: | - $ErrorActionPreference = "Stop" - $ProgressPreference = "SilentlyContinue" - - dotnet tool restore - dotnet mdsnippets - - $GitStatus = (git status --porcelain) - if ([string]::IsNullOrEmpty($GitStatus)) { - Write-Output "No changes to commit." - exit 0 - } - - $BranchName = "docs/update-docs" - "branchName=$BranchName" >> ${env:GITHUB_OUTPUT} - - git config user.email ${env:GIT_COMMIT_USER_EMAIL} | Out-Null - git config user.name ${env:GIT_COMMIT_USER_NAME} | Out-Null - git remote set-url "${env:GITHUB_SERVER_URL}/${env:GITHUB_REPOSITORY}.git" | Out-Null - git fetch origin | Out-Null - git rev-parse --verify --quiet ("remotes/origin/" + $BranchName) | Out-Null - - if ($LASTEXITCODE -eq 0) { - Write-Output "Branch $BranchName already exists." - exit 0 - } - - git checkout -b $BranchName - git add . - git commit -m "Update the code-snippets in the documentation" -s - git push -u origin $BranchName - "updated-docs=true" >> ${env:GITHUB_OUTPUT} - - - name: Create pull request - if: steps.update-docs.outputs.updated-docs == 'true' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - BASE_BRANCH_NAME: ${{ github.event.repository.default_branch }} - HEAD_BRANCH_NAME: ${{ steps.update-docs.outputs.branchName }} - with: - script: | - const { repo, owner } = context.repo; - const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; - const { data: pr } = await github.rest.pulls.create({ - title: 'Update the code-snippets in the documentation', - owner, - repo, - head: process.env.HEAD_BRANCH_NAME, - base: process.env.BASE_BRANCH_NAME, - body: [ - 'This PR updates the code-snippets in the documentation.', - '', - `This pull request was generated by [GitHub Actions](${workflowUrl}).` - ].join('\n') - }); - core.notice(`Created pull request ${owner}/${repo}#${pr.number}: ${pr.html_url}`); +name: update-documentation + +on: + push: + branches: [ master ] + paths: [ '**/*.md', 'test/WebSites/DocumentationSnippets/**' ] + workflow_dispatch: + +permissions: {} + +jobs: + update-docs: + name: update-docs + runs-on: ubuntu-latest + if: github.event.repository.fork == false + + permissions: + contents: write + pull-requests: write + + steps: + + - name: Checkout code + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + filter: 'tree:0' + persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits + show-progress: false + + - name: Setup .NET SDK + uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + + - name: Update documentation + id: update-docs + shell: pwsh + env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + GIT_COMMIT_USER_EMAIL: '41898282+github-actions[bot]@users.noreply.github.com' + GIT_COMMIT_USER_NAME: 'github-actions[bot]' + run: | + $ErrorActionPreference = "Stop" + $ProgressPreference = "SilentlyContinue" + + dotnet tool restore + dotnet mdsnippets + + $GitStatus = (git status --porcelain) + if ([string]::IsNullOrEmpty($GitStatus)) { + Write-Output "No changes to commit." + exit 0 + } + + $BranchName = "docs/update-docs" + "branchName=$BranchName" >> ${env:GITHUB_OUTPUT} + + git config user.email ${env:GIT_COMMIT_USER_EMAIL} | Out-Null + git config user.name ${env:GIT_COMMIT_USER_NAME} | Out-Null + git remote set-url "${env:GITHUB_SERVER_URL}/${env:GITHUB_REPOSITORY}.git" | Out-Null + git fetch origin | Out-Null + git rev-parse --verify --quiet ("remotes/origin/" + $BranchName) | Out-Null + + if ($LASTEXITCODE -eq 0) { + Write-Output "Branch $BranchName already exists." + exit 0 + } + + git checkout -b $BranchName + git add . + git commit -m "Update the code-snippets in the documentation" -s + git push -u origin $BranchName + "updated-docs=true" >> ${env:GITHUB_OUTPUT} + + - name: Create pull request + if: steps.update-docs.outputs.updated-docs == 'true' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + BASE_BRANCH_NAME: ${{ github.event.repository.default_branch }} + HEAD_BRANCH_NAME: ${{ steps.update-docs.outputs.branchName }} + with: + script: | + const { repo, owner } = context.repo; + const workflowUrl = `${process.env.GITHUB_SERVER_URL}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`; + const { data: pr } = await github.rest.pulls.create({ + title: 'Update the code-snippets in the documentation', + owner, + repo, + head: process.env.HEAD_BRANCH_NAME, + base: process.env.BASE_BRANCH_NAME, + body: [ + 'This PR updates the code-snippets in the documentation.', + '', + `This pull request was generated by [GitHub Actions](${workflowUrl}).` + ].join('\n') + }); + core.notice(`Created pull request ${owner}/${repo}#${pr.number}: ${pr.html_url}`); From af2bb522c831580cde2f0b8eab0a12375bfc386a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 15:44:22 +0000 Subject: [PATCH 620/680] Bump js-yaml from 4.1.0 to 4.1.1 in /src/Swashbuckle.AspNetCore.ReDoc (#3667) --- src/Swashbuckle.AspNetCore.ReDoc/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json index 1e30389428..a1c2307b2b 100644 --- a/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json +++ b/src/Swashbuckle.AspNetCore.ReDoc/package-lock.json @@ -432,9 +432,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" From 0c6ea3d80222ce0396b5a5330642108922fe5a9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 18:31:10 +0000 Subject: [PATCH 621/680] Bump actions/checkout from 5.0.1 to 6.0.0 (#3669) Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.1 to 6.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/93cb6efe18208431cddfb8368fd83d5badbf9bfd...1af3b93b6815bc44a9784bd300feb67ff0d1eeb3) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/actions-lint.yml | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/bump-version.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/ossf-scorecard.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-documentation.yml | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/actions-lint.yml index f331b1dcda..5e583ef1e6 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/actions-lint.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 03836b8e94..45c8caf592 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,7 @@ jobs: } - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index e3fbdb19c2..6c3807ef8e 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d650713850..1938d1d6fa 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b0718e23b2..6510d9079b 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index 1976ae506b..c2c993c872 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12ca97e76a..d01cf8fde8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: false diff --git a/.github/workflows/update-documentation.yml b/.github/workflows/update-documentation.yml index c8395e1c3b..4cf6f70851 100644 --- a/.github/workflows/update-documentation.yml +++ b/.github/workflows/update-documentation.yml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: filter: 'tree:0' persist-credentials: true # zizmor: ignore[artipacked] Needed to push commits From d12336c2cf16efd105429433d11388611a0e8c67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:13:09 +0000 Subject: [PATCH 622/680] Bump the xunit group with 1 update (#3670) Bumps Verify.XunitV3 from 31.5.3 to 31.7.1 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.7.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7e329063c0..603b15b614 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 6202ca925303acba1f7822892d0a4d57217defae Mon Sep 17 00:00:00 2001 From: markuspalme Date: Sun, 23 Nov 2025 19:18:03 +0100 Subject: [PATCH 623/680] Add clarifying example in migration guide to v10 (#3672) Added a concrete example on how to check for concrete types in order to mutate model properties in the migration guide. --- docs/migrating-to-v10.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/migrating-to-v10.md b/docs/migrating-to-v10.md index 3ba302518a..c7ad61af54 100644 --- a/docs/migrating-to-v10.md +++ b/docs/migrating-to-v10.md @@ -73,7 +73,18 @@ Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the foll - Update any NuGet package references for Swashbuckle.AspNetCore and Microsoft.OpenApi to v10.0.0+ and v2.3.0+ respectively. - Update any `using` directives that reference types from the `Microsoft.OpenApi.Models` namespace to use the new namespace `Microsoft.OpenApi`. -- Update model references (e.g. `OpenApiSchema`) to use the new interfaces (e.g. `IOpenApiSchema`) and the relevant concrete types to mutate them (e.g. `OpenApiSchema`). +- Update model references (e.g. `OpenApiSchema`) to use the new interfaces (e.g. `IOpenApiSchema`) and use the relevant concrete types to mutate them (e.g. `OpenApiSchema`). An example of this is shown below for an `ISchemaFilter` implementation: + + ```csharp + public void Apply(IOpenApiSchema schema, SchemaFilterContext context) + { + if (schema is OpenApiSchema openApiSchema) + { + // The properties are only mutable on the concrete type + openApiSchema.Type = JsonSchemaType.String; + } + } + ``` - Update any use of `.Reference` properties (e.g. `OpenApiSchema.ReferenceV3`) to use the new `*Reference` class instead (e.g. `OpenApiSchemaReference`). - Replace usage of the `OpenApiSchema.Type` property using a string (e.g. `"string"` or `"boolean"`) with the `JsonSchemaType` flags enumeration. - Replace usage of the `OpenApiSchema.Nullable` property by OR-ing the `JsonSchemaType.Null` value to `OpenApiSchema.Type` (e.g. `schema.Type |= JsonSchemaType.Null;`). From f8cc8558775286fe47d876634fbcff6df7ee20fd Mon Sep 17 00:00:00 2001 From: Martin Costello Date: Sun, 23 Nov 2025 18:39:19 +0000 Subject: [PATCH 624/680] Add markdown linter (#3673) - Add markdownlint. - Fix markdownlint warnings. - Update actionlint to the latest version. - Update zizmor to the latest version. --- .../workflows/{actions-lint.yml => lint.yml} | 107 ++++++++++-------- .github/zizmor.yml | 4 + CONTRIBUTING.md | 11 +- README.md | 12 +- Swashbuckle.AspNetCore.slnx | 2 +- docs/migrating-to-v10.md | 1 + 6 files changed, 75 insertions(+), 62 deletions(-) rename .github/workflows/{actions-lint.yml => lint.yml} (72%) diff --git a/.github/workflows/actions-lint.yml b/.github/workflows/lint.yml similarity index 72% rename from .github/workflows/actions-lint.yml rename to .github/workflows/lint.yml index 5e583ef1e6..9a7dd0b133 100644 --- a/.github/workflows/actions-lint.yml +++ b/.github/workflows/lint.yml @@ -1,51 +1,58 @@ -name: actions-lint - -on: - push: - branches: [ master ] - paths-ignore: - - '**/*.gitattributes' - - '**/*.gitignore' - - '**/*.md' - pull_request: - branches: [ master ] - workflow_dispatch: - -permissions: {} - -env: - FORCE_COLOR: 3 - TERM: xterm - ZIZMOR_VERSION: '1.14.1' - -jobs: - lint: - runs-on: ubuntu-latest - - permissions: - actions: read - contents: read - security-events: write - - steps: - - - name: Checkout code +name: lint + +on: + push: + branches: [ master ] + paths-ignore: + - '**/*.gitattributes' + - '**/*.gitignore' + - '**/*.md' + pull_request: + branches: [ master ] + workflow_dispatch: + +permissions: {} + +env: + FORCE_COLOR: 3 + TERM: xterm + ZIZMOR_VERSION: '1.16.3' + +jobs: + lint: + runs-on: ubuntu-latest + + permissions: + actions: read + contents: read + security-events: write + + steps: + + - name: Checkout code uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - with: - filter: 'tree:0' - persist-credentials: false - show-progress: false - - - name: Add actionlint problem matcher - run: echo "::add-matcher::.github/actionlint-matcher.json" - - - name: Lint workflows - uses: docker://rhysd/actionlint:1.7.7@sha256:887a259a5a534f3c4f36cb02dca341673c6089431057242cdc931e9f133147e9 - with: - args: -color - - - name: Lint workflows with zizmor - uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 - with: - persona: pedantic - version: ${{ env.ZIZMOR_VERSION }} + with: + filter: 'tree:0' + persist-credentials: false + show-progress: false + + - name: Add actionlint problem matcher + run: echo "::add-matcher::.github/actionlint-matcher.json" + + - name: Lint workflows + uses: docker://rhysd/actionlint:1.7.9@sha256:a0383f60d92601e2694e24b24d37df7b6a40bed7cedbc447611c50009bf02d94 + with: + args: -color + + - name: Lint workflows with zizmor + uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 + with: + persona: pedantic + version: ${{ env.ZIZMOR_VERSION }} + + - name: Lint markdown + uses: DavidAnson/markdownlint-cli2-action@30a0e04f1870d58f8d717450cc6134995f993c63 # v21.0.0 + with: + config: '.markdownlint.json' + globs: | + **/*.md diff --git a/.github/zizmor.yml b/.github/zizmor.yml index ad6d776482..ab690df920 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -1,5 +1,9 @@ rules: anonymous-definition: disable: true + concurrency-limits: + disable: true + dependabot-cooldown: + disable: true undocumented-permissions: disable: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1211fe76c..ec426ae8bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,22 +1,21 @@ -Contributing to Swashbuckle.AspNetCore -========= +# Contributing to Swashbuckle.AspNetCore Contributions to Swashbuckle.AspNetCore are welcomed in the form of constructive, reproducible bug reports, feature requests that align to the project's goals, or better still a PR that's accompanied with passing tests. If you have general questions or feedback about using Swashbuckle.AspNetCore, PLEASE DON'T CREATE AN ISSUE AND POST TO STACKOVERFLOW INSTEAD. -## Bug Reports ## +## Bug Reports If you're reporting a bug, please include a clear description of the issue, the version of Swashbuckle.AspNetCore you're using, and a set of clear repro steps. Please remember that Swashbuckle.AspNetCore is a free and open-source project provided to the community with zero financial gain to the author(s). Any issues deemed to have a negative or arrogant tone will be closed without response. -## Feature Requests ## +## Feature Requests -Fundamentally, Swashbuckle.AspNetCore is a library that attempts to generate an accurate description for APIs built on ASP.NET Core, using [Swagger 2.0](https://swagger.io/docs/specification/2-0/basic-structure/), according to the routes, controllers and models that have been implemented. So, the resulting API documentation is driven by "actual" behavior as opposed to "intended" behavior. This is an important distinction to consider when submitting feature requests. For example, a feature that leverages built-in attributes that affect runtime behavior (e.g. AuthorizeAttribute, RequiredAttribute etc.) would be more aligned to the project goals than one that introduces custom attributes that drive documentation and nothing else. +Fundamentally, Swashbuckle.AspNetCore is a library that attempts to generate an accurate description for APIs built on ASP.NET Core, using [OpenAPI 3.0](https://swagger.io/docs/specification/v3_0/basic-structure/) (or [Swagger 2.0](https://swagger.io/docs/specification/2-0/basic-structure/)), according to the routes, controllers and models that have been implemented. So, the resulting API documentation is driven by "actual" behavior as opposed to "intended" behavior. This is an important distinction to consider when submitting feature requests. For example, a feature that leverages built-in attributes that affect runtime behavior (e.g. AuthorizeAttribute, RequiredAttribute etc.) would be more aligned to the project goals than one that introduces custom attributes that drive documentation and nothing else. It's also worth noting that Swashbuckle.AspNetCore leverages the [swagger-ui project](https://github.com/swagger-api/swagger-ui) but doesn't actually implement any UI code. If you have a bug report or feature request that's UI-specific, PLEASE DON'T CREATE AN ISSUE HERE AND POST TO THE SWAGGER-UI REPO INSTEAD. -## Pull Requests ## +## Pull Requests If you've identified a feature/bug fix that aligns to the project goals, or even just an addition to the docs, please submit a Pull Request (PR). If applicable, include tests and ensure all tests are passing locally before you commit. diff --git a/README.md b/README.md index 1ab4167ee1..f0df85fee4 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ for more details. [redoc-version-v7-badge]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdomaindrivendev%2FSwashbuckle.AspNetCore%2Frefs%2Ftags%2Fv7.3.2%2Fsrc%2FSwashbuckle.AspNetCore.ReDoc%2Fpackage.json&query=%24.dependencies.redoc&style=flat&label=Redoc [redoc-version-v7-release]: https://github.com/Redocly/redoc/releases/tag/v2.4.0 +## Getting Started + First install the kitchen-sink NuGet package into your ASP.NET Core application: ```terminal @@ -295,7 +297,7 @@ Additionally, there are add-on packages (CLI tools, [an alternate UI using Redoc ### "Core" Packages | **Package** | **NuGet** | **Description** | -|-------------|--------------------|-----------------| +| ----------- | --------- | --------------- | | [Swashbuckle.AspNetCore.Swagger][swashbuckle-aspnetcore-swagger] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Swagger?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swagger] | Exposes OpenAPI JSON endpoints. It expects an implementation of `ISwaggerProvider` to be registered in the DI container, which it queries to retrieve `OpenApiDocument` instance(s) that are then exposed as serialized JSON. | | [Swashbuckle.AspNetCore.SwaggerGen][swashbuckle-aspnetcore-swaggergen] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.SwaggerGen?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swaggergen] | Injects an implementation of `ISwaggerProvider` that can be used by the above component. This particular implementation generates `OpenApiDocument` instance(s) from your application endpoints (controllers, minimal endpoints etc.). | | [Swashbuckle.AspNetCore.SwaggerUI][swashbuckle-aspnetcore-swaggerui] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.SwaggerUI?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-swaggerui] | Exposes an embedded version of [swagger-ui][swagger-ui]. You specify the API endpoints where it can obtain OpenAPI documents from, and it uses them to power interactive documentation for your API. | @@ -303,7 +305,7 @@ Additionally, there are add-on packages (CLI tools, [an alternate UI using Redoc ### Additional Packages | **Package** | **NuGet** | **Description** | -|-------------|--------------------|-----------------| +| ----------- | --------- | --------------- | | [Swashbuckle.AspNetCore.Annotations][swashbuckle-aspnetcore-annotations] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Annotations?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-annotations] | Includes a set of custom attributes that can be applied to controllers/endpoints, actions and models to enrich the generated documentation. | | [Swashbuckle.AspNetCore.Cli][swashbuckle-aspnetcore-cli] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Cli?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-cli] | Provides a command line interface (CLI) for retrieving OpenAPI documents directly from an application start-up assembly and then writing to a file. | | [Swashbuckle.AspNetCore.ReDoc][swashbuckle-aspnetcore-redoc] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.ReDoc?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-redoc] | Exposes an embedded version of the [Redoc UI][redoc] (an alternative to [swagger-ui][swagger-ui]). | @@ -313,7 +315,7 @@ Additionally, there are add-on packages (CLI tools, [an alternate UI using Redoc These packages are provided by the .NET open-source community. | **Package** | **NuGet** | **Description** | -|-------------|--------------------|-----------------| +| ----------- | --------- | --------------- | | [Swashbuckle.AspNetCore.Filters][swashbuckle-aspnetcore-filters] | [![NuGet](https://img.shields.io/nuget/v/Swashbuckle.AspNetCore.Filters?logo=nuget&label=Latest&color=blue)][swashbuckle-aspnetcore-filters] | Some useful Swashbuckle.AspNetCore filters which add additional documentation, e.g. request and response examples, authorization information, etc. See its README for more details. | | [Unchase.Swashbuckle.AspNetCore.Extensions][unchase-swashbuckle-aspnetcore-extensions] | [![NuGet](https://img.shields.io/nuget/v/Unchase.Swashbuckle.AspNetCore.Extensions?logo=nuget&label=Latest&color=blue)][unchase-swashbuckle-aspnetcore-extensions] | Some useful extensions (filters), which add additional documentation, e.g. hide `PathItems` for unaccepted roles, fix enumerations for client code generation, etc. See its README for more details. | | [MicroElements.Swashbuckle.FluentValidation][microelements-swashbuckle-fluentvalidation] | [![NuGet](https://img.shields.io/nuget/v/MicroElements.Swashbuckle.FluentValidation?logo=nuget&label=Latest&color=blue)][microelements-swashbuckle-fluentvalidation] | Use [FluentValidation][fluentvalidation] rules instead of ComponentModel attributes to augment generated OpenAPI schemas. | @@ -325,8 +327,8 @@ The steps described above will get you up and running with minimal set up. Howev Check out the table below for the full list of possible configuration options. -| **Component** | **Configuration and Customization** | -| ------------- | ----------------------------------- | +| **Component** | **Configuration and Customization** | +| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Swashbuckle.AspNetCore.Swagger** | [Change the Path for OpenAPI JSON Endpoints](docs/configure-and-customize-swagger.md#change-the-path-for-openapi-json-endpoints) | | | [Modify OpenAPI with Request Context](docs/configure-and-customize-swagger.md#modify-openapi-with-request-context) | | | [Serialize OpenAPI JSON in the 3.1 format](docs/configure-and-customize-swagger.md#serialize-openapi-in-the-31-format) | diff --git a/Swashbuckle.AspNetCore.slnx b/Swashbuckle.AspNetCore.slnx index 63e77ce466..78c16b450f 100644 --- a/Swashbuckle.AspNetCore.slnx +++ b/Swashbuckle.AspNetCore.slnx @@ -27,10 +27,10 @@ - + diff --git a/docs/migrating-to-v10.md b/docs/migrating-to-v10.md index c7ad61af54..354e55b2b2 100644 --- a/docs/migrating-to-v10.md +++ b/docs/migrating-to-v10.md @@ -85,6 +85,7 @@ Migrating to Swashbuckle.AspNetCore v10+ will likely involve changes in the foll } } ``` + - Update any use of `.Reference` properties (e.g. `OpenApiSchema.ReferenceV3`) to use the new `*Reference` class instead (e.g. `OpenApiSchemaReference`). - Replace usage of the `OpenApiSchema.Type` property using a string (e.g. `"string"` or `"boolean"`) with the `JsonSchemaType` flags enumeration. - Replace usage of the `OpenApiSchema.Nullable` property by OR-ing the `JsonSchemaType.Null` value to `OpenApiSchema.Type` (e.g. `schema.Type |= JsonSchemaType.Null;`). From 226449c9e0e1f0829d4f0e6203c13d15285bf38a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:24:52 +0000 Subject: [PATCH 625/680] Bump zizmorcore/zizmor-action from 0.2.0 to 0.3.0 (#3675) Bumps [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) from 0.2.0 to 0.3.0. - [Release notes](https://github.com/zizmorcore/zizmor-action/releases) - [Commits](https://github.com/zizmorcore/zizmor-action/compare/e673c3917a1aef3c65c972347ed84ccd013ecda4...e639db99335bc9038abc0e066dfcd72e23d26fb4) --- updated-dependencies: - dependency-name: zizmorcore/zizmor-action dependency-version: 0.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 116 ++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9a7dd0b133..83330bdf54 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,58 +1,58 @@ -name: lint - -on: - push: - branches: [ master ] - paths-ignore: - - '**/*.gitattributes' - - '**/*.gitignore' - - '**/*.md' - pull_request: - branches: [ master ] - workflow_dispatch: - -permissions: {} - -env: - FORCE_COLOR: 3 - TERM: xterm - ZIZMOR_VERSION: '1.16.3' - -jobs: - lint: - runs-on: ubuntu-latest - - permissions: - actions: read - contents: read - security-events: write - - steps: - - - name: Checkout code - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 - with: - filter: 'tree:0' - persist-credentials: false - show-progress: false - - - name: Add actionlint problem matcher - run: echo "::add-matcher::.github/actionlint-matcher.json" - - - name: Lint workflows - uses: docker://rhysd/actionlint:1.7.9@sha256:a0383f60d92601e2694e24b24d37df7b6a40bed7cedbc447611c50009bf02d94 - with: - args: -color - - - name: Lint workflows with zizmor - uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 - with: - persona: pedantic - version: ${{ env.ZIZMOR_VERSION }} - - - name: Lint markdown - uses: DavidAnson/markdownlint-cli2-action@30a0e04f1870d58f8d717450cc6134995f993c63 # v21.0.0 - with: - config: '.markdownlint.json' - globs: | - **/*.md +name: lint + +on: + push: + branches: [ master ] + paths-ignore: + - '**/*.gitattributes' + - '**/*.gitignore' + - '**/*.md' + pull_request: + branches: [ master ] + workflow_dispatch: + +permissions: {} + +env: + FORCE_COLOR: 3 + TERM: xterm + ZIZMOR_VERSION: '1.16.3' + +jobs: + lint: + runs-on: ubuntu-latest + + permissions: + actions: read + contents: read + security-events: write + + steps: + + - name: Checkout code + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + with: + filter: 'tree:0' + persist-credentials: false + show-progress: false + + - name: Add actionlint problem matcher + run: echo "::add-matcher::.github/actionlint-matcher.json" + + - name: Lint workflows + uses: docker://rhysd/actionlint:1.7.9@sha256:a0383f60d92601e2694e24b24d37df7b6a40bed7cedbc447611c50009bf02d94 + with: + args: -color + + - name: Lint workflows with zizmor + uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0 + with: + persona: pedantic + version: ${{ env.ZIZMOR_VERSION }} + + - name: Lint markdown + uses: DavidAnson/markdownlint-cli2-action@30a0e04f1870d58f8d717450cc6134995f993c63 # v21.0.0 + with: + config: '.markdownlint.json' + globs: | + **/*.md From eddc5858eae92d26ad5eed1c3810ce2fda4d5930 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:25:39 +0000 Subject: [PATCH 626/680] Bump swagger-ui-dist in /src/Swashbuckle.AspNetCore.SwaggerUI (#3676) Bumps [swagger-ui-dist](https://github.com/swagger-api/swagger-ui) from 5.30.2 to 5.30.3. - [Release notes](https://github.com/swagger-api/swagger-ui/releases) - [Changelog](https://github.com/swagger-api/swagger-ui/blob/master/.releaserc) - [Commits](https://github.com/swagger-api/swagger-ui/compare/v5.30.2...v5.30.3) --- updated-dependencies: - dependency-name: swagger-ui-dist dependency-version: 5.30.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json | 8 ++++---- src/Swashbuckle.AspNetCore.SwaggerUI/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json index 21e15498a4..4c4179e1b2 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "hasInstallScript": true, "dependencies": { - "swagger-ui-dist": "5.30.2" + "swagger-ui-dist": "5.30.3" } }, "node_modules/@scarf/scarf": { @@ -20,9 +20,9 @@ "license": "Apache-2.0" }, "node_modules/swagger-ui-dist": { - "version": "5.30.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.2.tgz", - "integrity": "sha512-HWCg1DTNE/Nmapt+0m2EPXFwNKNeKK4PwMjkwveN/zn1cV2Kxi9SURd+m0SpdcSgWEK/O64sf8bzXdtUhigtHA==", + "version": "5.30.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.30.3.tgz", + "integrity": "sha512-giQl7/ToPxCqnUAx2wpnSnDNGZtGzw1LyUw6ZitIpTmdrvpxKFY/94v1hihm0zYNpgp1/VY0jTDk//R0BBgnRQ==", "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" diff --git a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json index 7c200754c9..146d81809f 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerUI/package.json +++ b/src/Swashbuckle.AspNetCore.SwaggerUI/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "swagger-ui-dist": "5.30.2" + "swagger-ui-dist": "5.30.3" }, "scripts": { "postinstall": "node scripts/remove-source-map-links.js" From c6d0ec16e30da0e78ba246c2e7a69394ac74fd07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:26:16 +0000 Subject: [PATCH 627/680] Bump github/codeql-action from 4.31.4 to 4.31.5 (#3677) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.4 to 4.31.5. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/e12f0178983d466f2f6028f5cc7a6d786fd97f4b...fdbfb4d2750291e159f0156def62b853c2798ca2) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.31.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/ossf-scorecard.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1938d1d6fa..2fd81bc59b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,12 +30,12 @@ jobs: show-progress: false - name: Initialize CodeQL - uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 + uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: languages: csharp build-mode: none - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 + uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: category: '/language:csharp' diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index c2c993c872..936e43ea0a 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -41,6 +41,6 @@ jobs: retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4 + uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: sarif_file: results.sarif From d9bd0b1343506c7cfc9d92e2c7d84a7d80bb8306 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:28:47 +0000 Subject: [PATCH 628/680] Bump actions/setup-dotnet from 5.0.0 to 5.0.1 (#3678) Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/d4c94342e560b34958eacfc5d055d21461ed1c5d...2016bd2012dba4e32de620c46fe006a3ac9f0602) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-version: 5.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 10 +++++----- .github/workflows/update-documentation.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 45c8caf592..b7465f187c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,7 @@ jobs: show-progress: false - name: Setup .NET SDKs - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: | 8.0.x @@ -85,7 +85,7 @@ jobs: package-manager-cache: false - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 id: setup-dotnet - name: Build, Package and Test @@ -165,7 +165,7 @@ jobs: name: packages-Windows - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} @@ -225,7 +225,7 @@ jobs: name: packages-Windows - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} @@ -255,7 +255,7 @@ jobs: name: packages-Windows - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: ${{ needs.build.outputs.dotnet-sdk-version }} diff --git a/.github/workflows/update-documentation.yml b/.github/workflows/update-documentation.yml index 4cf6f70851..2070fcd7fa 100644 --- a/.github/workflows/update-documentation.yml +++ b/.github/workflows/update-documentation.yml @@ -28,7 +28,7 @@ jobs: show-progress: false - name: Setup .NET SDK - uses: actions/setup-dotnet@d4c94342e560b34958eacfc5d055d21461ed1c5d # v5.0.0 + uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 - name: Update documentation id: update-docs From 660325f0c7f154149fed928db809ed20c6be7f89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:17:55 +0000 Subject: [PATCH 629/680] Bump the xunit group with 1 update (#3679) Bumps Verify.XunitV3 from 31.7.1 to 31.7.2 --- updated-dependencies: - dependency-name: Verify.XunitV3 dependency-version: 31.7.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: xunit ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 603b15b614..094b7725ab 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -22,7 +22,7 @@ - + From 69e32100cb87f2e1bc678c1d22926ac3d7d99d7a Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 27 Nov 2025 11:30:59 +0000 Subject: [PATCH 630/680] Remove redundant references Remove redundant project references. --- test/WebSites/Basic/Basic.csproj | 4 +--- test/WebSites/CliExample/CliExample.csproj | 3 +-- .../CliExampleWithFactory/CliExampleWithFactory.csproj | 3 +-- test/WebSites/ConfigFromFile/ConfigFromFile.csproj | 3 +-- .../CustomDocumentSerializer/CustomDocumentSerializer.csproj | 3 +-- test/WebSites/CustomUIConfig/CustomUIConfig.csproj | 1 - test/WebSites/CustomUIIndex/CustomUIIndex.csproj | 1 - .../DocumentationSnippets/DocumentationSnippets.csproj | 2 -- test/WebSites/GenericControllers/GenericControllers.csproj | 1 - test/WebSites/MinimalApp/MinimalApp.csproj | 3 +-- .../MinimalAppWithHostedServices.csproj | 3 +-- test/WebSites/MultipleVersions/MultipleVersions.csproj | 1 - test/WebSites/NswagClientExample/NswagClientExample.csproj | 4 +--- test/WebSites/OAuth2Integration/OAuth2Integration.csproj | 1 - test/WebSites/ReDoc/ReDoc.csproj | 1 - test/WebSites/TopLevelSwaggerDoc/TopLevelSwaggerDoc.csproj | 1 - test/WebSites/WebApi/WebApi.csproj | 1 - 17 files changed, 8 insertions(+), 28 deletions(-) diff --git a/test/WebSites/Basic/Basic.csproj b/test/WebSites/Basic/Basic.csproj index f894351d83..5b0eb32e95 100644 --- a/test/WebSites/Basic/Basic.csproj +++ b/test/WebSites/Basic/Basic.csproj @@ -7,9 +7,7 @@ - - - + diff --git a/test/WebSites/CliExample/CliExample.csproj b/test/WebSites/CliExample/CliExample.csproj index db6f671266..dfb5bf8da0 100644 --- a/test/WebSites/CliExample/CliExample.csproj +++ b/test/WebSites/CliExample/CliExample.csproj @@ -7,10 +7,9 @@ + - -