Skip to content

Commit

Permalink
Fixed nullabillity problems wih dictionaries
Browse files Browse the repository at this point in the history
  • Loading branch information
Eneuman committed Jul 17, 2021
1 parent 2fc1ac3 commit a8bb26f
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 @@ -85,6 +85,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 @@ -491,7 +491,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 @@ -565,10 +565,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 @@ -585,7 +609,6 @@ public void GenerateSchema_SupportsOption_SupportNonNullableReferenceTypes_Nulla
Assert.Equal(expectedNullable, propertySchema.Nullable);
}


[Fact]
public void GenerateSchema_HandlesTypesWithNestedTypes()
{
Expand Down Expand Up @@ -719,7 +742,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 a8bb26f

Please sign in to comment.