From 4cc5dd7bba70ce383470b19802407e4aa4709edc Mon Sep 17 00:00:00 2001 From: Keah Peters Date: Tue, 14 May 2024 14:48:12 +0100 Subject: [PATCH 1/3] Fix to make required and nullable properties nullable in schema --- .../SchemaGenerator/SchemaGenerator.cs | 19 ++++--------------- .../JsonSerializerSchemaGeneratorTests.cs | 14 +++++++++----- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs index a03b312e08..3ca1f7ddd0 100644 --- a/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs +++ b/src/Swashbuckle.AspNetCore.SwaggerGen/SchemaGenerator/SchemaGenerator.cs @@ -68,24 +68,13 @@ private OpenApiSchema GenerateSchemaForMember( { var requiredAttribute = customAttributes.OfType().FirstOrDefault(); - schema.ReadOnly = dataProperty.IsReadOnly; - schema.WriteOnly = dataProperty.IsWriteOnly; - -#if NET7_0_OR_GREATER - var hasRequiredMemberAttribute = customAttributes.OfType().Any(); - schema.Nullable = _generatorOptions.SupportNonNullableReferenceTypes - ? dataProperty.IsNullable && requiredAttribute == null && !hasRequiredMemberAttribute && !memberInfo.IsNonNullableReferenceType() - : dataProperty.IsNullable && requiredAttribute == null && !hasRequiredMemberAttribute; - - schema.MinLength = modelType == typeof(string) && (hasRequiredMemberAttribute || requiredAttribute is { AllowEmptyStrings: false }) ? 1 : null; -#else - schema.Nullable = _generatorOptions.SupportNonNullableReferenceTypes - ? dataProperty.IsNullable && requiredAttribute==null && !memberInfo.IsNonNullableReferenceType() - : dataProperty.IsNullable && requiredAttribute==null; + ? dataProperty.IsNullable && requiredAttribute == null && !memberInfo.IsNonNullableReferenceType() + : dataProperty.IsNullable && requiredAttribute == null; + schema.ReadOnly = dataProperty.IsReadOnly; + schema.WriteOnly = dataProperty.IsWriteOnly; schema.MinLength = modelType == typeof(string) && requiredAttribute is { AllowEmptyStrings: false } ? 1 : null; -#endif } var defaultValueAttribute = customAttributes.OfType().FirstOrDefault(); diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index 4bc5ac4c4f..4507447b57 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -367,9 +367,10 @@ public void GenerateSchema_SetsReadOnlyAndWriteOnlyFlags_IfPropertyIsRestricted( } #if NET7_0_OR_GREATER - public class TypeWithRequiredProperty + public class TypeWithRequiredProperties { - public required string RequiredProperty { get; set; } + public required string RequiredString { get; set; } + public required int RequiredInt { get; set; } } public class TypeWithRequiredPropertyAndValidationAttribute @@ -383,10 +384,13 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeyword() { var schemaRepository = new SchemaRepository(); - var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRequiredProperty), schemaRepository); + var referenceSchema = Subject().GenerateSchema(typeof(TypeWithRequiredProperties), schemaRepository); var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; - Assert.Equal(new[] { "RequiredProperty" }, schema.Required.ToArray()); + Assert.True(schema.Properties["RequiredString"].Nullable); + Assert.Equal(new[] { "RequiredString" }, schema.Required.ToArray()); + Assert.False(schema.Properties["RequiredInt"].Nullable); + Assert.Equal(new[] { "RequiredInt" }, schema.Required.ToArray()); } [Fact] @@ -398,7 +402,7 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeywordAndValidatio var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.Equal(1, schema.Properties["RequiredProperty"].MinLength); - Assert.False(schema.Properties["RequiredProperty"].Nullable); + Assert.True(schema.Properties["RequiredProperty"].Nullable); Assert.Equal(new[] { "RequiredProperty" }, schema.Required.ToArray()); } #endif From 12f1ac1141c8c902e07ddbcb96f8df3bdab03bf9 Mon Sep 17 00:00:00 2001 From: Keah Peters Date: Tue, 14 May 2024 15:11:07 +0100 Subject: [PATCH 2/3] Fix unit test --- .../SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index 4507447b57..5be543287d 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -388,9 +388,9 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeyword() var schema = schemaRepository.Schemas[referenceSchema.Reference.Id]; Assert.True(schema.Properties["RequiredString"].Nullable); - Assert.Equal(new[] { "RequiredString" }, schema.Required.ToArray()); + Assert.Contains("RequiredString", schema.Required.ToArray()); Assert.False(schema.Properties["RequiredInt"].Nullable); - Assert.Equal(new[] { "RequiredInt" }, schema.Required.ToArray()); + Assert.Contains("RequiredInt", schema.Required.ToArray()); } [Fact] From b2a106ef59efd577c62722a89dcb468b726bac58 Mon Sep 17 00:00:00 2001 From: Keah Peters Date: Tue, 14 May 2024 15:31:18 +0100 Subject: [PATCH 3/3] Added test for type with required nullable properties --- .../JsonSerializerSchemaGeneratorTests.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs index 5be543287d..bbe9122072 100644 --- a/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs +++ b/test/Swashbuckle.AspNetCore.SwaggerGen.Test/SchemaGenerator/JsonSerializerSchemaGeneratorTests.cs @@ -405,6 +405,28 @@ public void GenerateSchema_SetsRequired_IfPropertyHasRequiredKeywordAndValidatio Assert.True(schema.Properties["RequiredProperty"].Nullable); Assert.Equal(new[] { "RequiredProperty" }, schema.Required.ToArray()); } + +#nullable enable + public class TypeWithNullableReferenceTypes + { + public required string? RequiredNullableString { get; set; } + public required string RequiredNonNullableString { get; set; } + } + + [Fact] + public void GenerateSchema_SetsRequiredAndNullable_IfPropertyHasRequiredKeywordAndIsNullable() + { + var schemaRepository = new SchemaRepository(); + + 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); + Assert.Contains("RequiredNullableString", schema.Required.ToArray()); + Assert.False(schema.Properties["RequiredNonNullableString"].Nullable); + Assert.Contains("RequiredNonNullableString", schema.Required.ToArray()); + } +#nullable disable #endif [Theory]