Skip to content

Commit

Permalink
Merge pull request #2166 from Eneuman/bug/nullabillity-in-generics
Browse files Browse the repository at this point in the history
Fixed nullabillity problems with dictionaries
  • Loading branch information
domaindrivendev authored Mar 2, 2022
2 parents 07d33a7 + a8bb26f commit 43b1b34
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,31 @@ public static bool IsNonNullableReferenceType(this MemberInfo memberInfo)
return false;
}

public static bool IsDictionaryValueNonNullable(this MemberInfo memberInfo)
{
var memberType = memberInfo.MemberType == MemberTypes.Field
? ((FieldInfo)memberInfo).FieldType
: ((PropertyInfo)memberInfo).PropertyType;

if (memberType.IsValueType) return false;

var nullableAttribute = memberInfo.GetNullableAttribute();

if (nullableAttribute == null)
{
return memberInfo.GetNullableFallbackValue();
}

if (nullableAttribute.GetType().GetField(NullableFlagsFieldName) is FieldInfo field &&
field.GetValue(nullableAttribute) is byte[] flags &&
flags.Length == 3 && flags[2] == 1)
{
return true;
}

return false;
}

private static object GetNullableAttribute(this MemberInfo memberInfo)
{
var nullableAttribute = memberInfo.GetCustomAttributes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ private OpenApiSchema GenerateSchemaForMember(
schema.Deprecated = true;
}

// NullableAttribute behaves diffrently for Dictionaries
if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
{
schema.AdditionalProperties.Nullable = !memberInfo.IsDictionaryValueNonNullable();
}

schema.ApplyValidationAttributes(customAttributes);

ApplyFilters(schema, modelType, schemaRepository, memberInfo: memberInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ public void GenerateSchema_SupportsOption_UseAllOfForPolymorphism()
Assert.NotNull(schema.OneOf[0].Reference);
var baseSchema = schemaRepository.Schemas[schema.OneOf[0].Reference.Id];
Assert.Equal("object", baseSchema.Type);
Assert.Equal(new[] { "BaseProperty"}, baseSchema.Properties.Keys);
Assert.Equal(new[] { "BaseProperty" }, baseSchema.Properties.Keys);
// The first sub type schema
Assert.NotNull(schema.OneOf[1].Reference);
var subType1Schema = schemaRepository.Schemas[schema.OneOf[1].Reference.Id];
Expand Down Expand Up @@ -586,10 +586,34 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes(
Assert.Equal(expectedNullable, propertySchema.Nullable);
}

[Theory]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.NullableDictionaryWithNonNullableContent), true, false)]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.NonNullableDictionaryWithNonNullableContent), false, false)]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.NonNullableDictionaryWithNullableContent), false, true)]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.NullableDictionaryWithNullableContent), true, true)]
public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_NullableAttribute_Compiler_Optimizations_Situations(
Type declaringType,
string propertyName,
bool expectedNullableProperty,
bool expectedNullableContent)
{
var subject = Subject(
configureGenerator: c => c.SupportNonNullableReferenceTypes = true
);
var schemaRepository = new SchemaRepository();

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);
}

[Theory]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.SubTypeWithOneNullableContent), nameof(TypeWithNullableContext.NullableString), true)]
[InlineData(typeof(TypeWithNullableContext), nameof(TypeWithNullableContext.SubTypeWithOneNonNullableContent), nameof(TypeWithNullableContext.NonNullableString), false)]
public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_NullableAttribute_Compiler_Optimizations_Situations(
public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypesInDictionary_NullableAttribute_Compiler_Optimizations_Situations(
Type declaringType,
string subType,
string propertyName,
Expand All @@ -606,7 +630,6 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla
Assert.Equal(expectedNullable, propertySchema.Nullable);
}


[Fact]
public void GenerateSchema_HandlesTypesWithNestedTypes()
{
Expand Down Expand Up @@ -740,7 +763,7 @@ public void GenerateSchema_HonorsSerializerAttribute_JsonPropertyName()
var referenceSchema = Subject().GenerateSchema(typeof(JsonPropertyNameAnnotatedType), schemaRepository);

var schema = schemaRepository.Schemas[referenceSchema.Reference.Id];
Assert.Equal( new[] { "string-with-json-property-name" }, schema.Properties.Keys.ToArray());
Assert.Equal(new[] { "string-with-json-property-name" }, schema.Properties.Keys.ToArray());
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public class TypeWithNullableContext
public List<SubTypeWithOneNullableContent>? NullableList { get; set; }
public List<SubTypeWithOneNonNullableContent> NonNullableList { get; set; } = default!;

public Dictionary<string, string>? NullableDictionaryWithNonNullableContent { get; set; }
public Dictionary<string, string> NonNullableDictionaryWithNonNullableContent { get; set; } = default!;
public Dictionary<string, string?> NonNullableDictionaryWithNullableContent { get; set; } = default!;
public Dictionary<string, string?>? NullableDictionaryWithNullableContent { get; set; }

public class SubTypeWithOneNullableContent
{
public string? NullableString { get; set; }
Expand Down

0 comments on commit 43b1b34

Please sign in to comment.