Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Havunen committed Feb 12, 2024
1 parent ea2879c commit 7078ab7
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,28 +186,47 @@ private IEnumerable<DataProperty> GetDataPropertiesFor(JsonObjectContract jsonOb

private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new Dictionary<Type, Tuple<DataType, string>>
{
[ typeof(bool) ] = Tuple.Create(DataType.Boolean, (string)null),
[ typeof(byte) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(sbyte) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(short) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(ushort) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(int) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(uint) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(long) ] = Tuple.Create(DataType.Integer, "int64"),
[ typeof(ulong) ] = Tuple.Create(DataType.Integer, "int64"),
[ typeof(float) ] = Tuple.Create(DataType.Number, "float"),
[ typeof(double) ] = Tuple.Create(DataType.Number, "double"),
[ typeof(decimal) ] = Tuple.Create(DataType.Number, "double"),
[ typeof(byte[]) ] = Tuple.Create(DataType.String, "byte"),
[ typeof(string) ] = Tuple.Create(DataType.String, (string)null),
[ typeof(char) ] = Tuple.Create(DataType.String, (string)null),
[ typeof(DateTime) ] = Tuple.Create(DataType.String, "date-time"),
[ typeof(DateTimeOffset) ] = Tuple.Create(DataType.String, "date-time"),
[ typeof(Guid) ] = Tuple.Create(DataType.String, "uuid"),
[ typeof(Uri) ] = Tuple.Create(DataType.String, "uri"),
[ typeof(TimeSpan) ] = Tuple.Create(DataType.String, "date-span"),
[ typeof(DateOnly) ] = Tuple.Create(DataType.String, "date"),
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time")
[typeof(bool)] = Tuple.Create(DataType.Boolean, (string)null),
[typeof(bool?)] = Tuple.Create(DataType.Boolean, (string)null),
[typeof(byte)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(byte?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(sbyte)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(sbyte?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(short)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(short?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(ushort)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(ushort?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(int)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(int?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(uint)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(uint?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(long)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(long?)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(ulong)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(ulong?)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(float)] = Tuple.Create(DataType.Number, "float"),
[typeof(float?)] = Tuple.Create(DataType.Number, "float"),
[typeof(double)] = Tuple.Create(DataType.Number, "double"),
[typeof(double?)] = Tuple.Create(DataType.Number, "double"),
[typeof(decimal)] = Tuple.Create(DataType.Number, "double"),
[typeof(decimal?)] = Tuple.Create(DataType.Number, "double"),
[typeof(byte[])] = Tuple.Create(DataType.String, "byte"),
[typeof(string)] = Tuple.Create(DataType.String, (string)null),
[typeof(char)] = Tuple.Create(DataType.String, (string)null),
[typeof(char?)] = Tuple.Create(DataType.String, (string)null),
[typeof(DateTime)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTime?)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTimeOffset)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTimeOffset?)] = Tuple.Create(DataType.String, "date-time"),
[typeof(Guid)] = Tuple.Create(DataType.String, "uuid"),
[typeof(Guid?)] = Tuple.Create(DataType.String, "uuid"),
[typeof(Uri)] = Tuple.Create(DataType.String, "uri"),
[typeof(TimeSpan)] = Tuple.Create(DataType.String, "date-span"),
[typeof(TimeSpan?)] = Tuple.Create(DataType.String, "date-span"),
[typeof(DateOnly)] = Tuple.Create(DataType.String, "date"),
[typeof(DateOnly?)] = Tuple.Create(DataType.String, "date"),
[typeof(TimeOnly)] = Tuple.Create(DataType.String, "time"),
[typeof(TimeOnly?)] = Tuple.Create(DataType.String, "time")
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,27 +214,45 @@ private IEnumerable<DataProperty> GetDataPropertiesFor(Type objectType, out Type

private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new Dictionary<Type, Tuple<DataType, string>>
{
[ typeof(bool) ] = Tuple.Create(DataType.Boolean, (string)null),
[ typeof(byte) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(sbyte) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(short) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(ushort) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(int) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(uint) ] = Tuple.Create(DataType.Integer, "int32"),
[ typeof(long) ] = Tuple.Create(DataType.Integer, "int64"),
[ typeof(ulong) ] = Tuple.Create(DataType.Integer, "int64"),
[ typeof(float) ] = Tuple.Create(DataType.Number, "float"),
[ typeof(double) ] = Tuple.Create(DataType.Number, "double"),
[ typeof(decimal) ] = Tuple.Create(DataType.Number, "double"),
[ typeof(byte[]) ] = Tuple.Create(DataType.String, "byte"),
[ typeof(string) ] = Tuple.Create(DataType.String, (string)null),
[ typeof(char) ] = Tuple.Create(DataType.String, (string)null),
[ typeof(DateTime) ] = Tuple.Create(DataType.String, "date-time"),
[ typeof(DateTimeOffset) ] = Tuple.Create(DataType.String, "date-time"),
[ typeof(Guid) ] = Tuple.Create(DataType.String, "uuid"),
[ typeof(Uri) ] = Tuple.Create(DataType.String, "uri"),
[ typeof(DateOnly) ] = Tuple.Create(DataType.String, "date"),
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time")
[typeof(bool)] = Tuple.Create(DataType.Boolean, (string)null),
[typeof(bool?)] = Tuple.Create(DataType.Boolean, (string)null),
[typeof(byte)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(byte?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(sbyte)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(sbyte?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(short)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(short?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(ushort)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(ushort?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(int)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(int?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(uint)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(uint?)] = Tuple.Create(DataType.Integer, "int32"),
[typeof(long)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(long?)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(ulong)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(ulong?)] = Tuple.Create(DataType.Integer, "int64"),
[typeof(float)] = Tuple.Create(DataType.Number, "float"),
[typeof(float?)] = Tuple.Create(DataType.Number, "float"),
[typeof(double)] = Tuple.Create(DataType.Number, "double"),
[typeof(double?)] = Tuple.Create(DataType.Number, "double"),
[typeof(decimal)] = Tuple.Create(DataType.Number, "double"),
[typeof(decimal?)] = Tuple.Create(DataType.Number, "double"),
[typeof(byte[])] = Tuple.Create(DataType.String, "byte"),
[typeof(string)] = Tuple.Create(DataType.String, (string)null),
[typeof(char)] = Tuple.Create(DataType.String, (string)null),
[typeof(char?)] = Tuple.Create(DataType.String, (string)null),
[typeof(DateTime)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTime?)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTimeOffset)] = Tuple.Create(DataType.String, "date-time"),
[typeof(DateTimeOffset?)] = Tuple.Create(DataType.String, "date-time"),
[typeof(Guid)] = Tuple.Create(DataType.String, "uuid"),
[typeof(Guid?)] = Tuple.Create(DataType.String, "uuid"),
[typeof(Uri)] = Tuple.Create(DataType.String, "uri"),
[typeof(DateOnly)] = Tuple.Create(DataType.String, "date"),
[typeof(DateOnly?)] = Tuple.Create(DataType.String, "date"),
[typeof(TimeOnly)] = Tuple.Create(DataType.String, "time"),
[typeof(TimeOnly?)] = Tuple.Create(DataType.String, "time")
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ public SchemaGenerator(SchemaGeneratorOptions generatorOptions, ISerializerDataC
SchemaRepository schemaRepository,
MemberInfo memberInfo = null,
ParameterInfo parameterInfo = null,
ApiParameterRouteInfo routeInfo = null)
ApiParameterRouteInfo routeInfo = null,
bool isEffectiveTypeNeeded = true)
{
if (memberInfo != null)
return GenerateSchemaForMember(modelType, schemaRepository, memberInfo);

if (parameterInfo != null)
return GenerateSchemaForParameter(modelType, schemaRepository, parameterInfo, routeInfo);

return GenerateSchemaForType(modelType, schemaRepository);
return GenerateSchemaForType(modelType, schemaRepository, isEffectiveTypeNeeded);
}

private OpenApiSchema GenerateSchemaForMember(
Expand Down Expand Up @@ -68,8 +69,8 @@ public SchemaGenerator(SchemaGeneratorOptions generatorOptions, ISerializerDataC
{
var requiredAttribute = customAttributes.OfType<RequiredAttribute>().FirstOrDefault();
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;
Expand Down Expand Up @@ -147,9 +148,9 @@ public SchemaGenerator(SchemaGeneratorOptions generatorOptions, ISerializerDataC
return schema;
}

private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository)
private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository schemaRepository, bool isEffectiveTypeNeeded = true)
{
var dataContract = GetDataContractFor(modelType);
var dataContract = GetDataContractFor(modelType, isEffectiveTypeNeeded);

var schema = _generatorOptions.UseOneOfForPolymorphism && IsBaseTypeWithKnownTypesDefined(dataContract, out var knownTypesDataContracts)
? GeneratePolymorphicSchema(dataContract, schemaRepository, knownTypesDataContracts)
Expand All @@ -163,9 +164,9 @@ private OpenApiSchema GenerateSchemaForType(Type modelType, SchemaRepository sch
return schema;
}

private DataContract GetDataContractFor(Type modelType)
private DataContract GetDataContractFor(Type modelType, bool isEffectiveTypeNeeded = true)
{
var effectiveType = Nullable.GetUnderlyingType(modelType) ?? modelType;
var effectiveType = isEffectiveTypeNeeded ? Nullable.GetUnderlyingType(modelType) ?? modelType : modelType;
return _serializerDataContractResolver.GetDataContractForType(effectiveType);
}

Expand Down Expand Up @@ -274,7 +275,8 @@ private OpenApiSchema CreatePrimitiveSchema(DataContract dataContract)
var schema = new OpenApiSchema
{
Type = dataContract.DataType.ToString().ToLower(CultureInfo.InvariantCulture),
Format = dataContract.DataFormat
Format = dataContract.DataFormat,
Nullable = Nullable.GetUnderlyingType(dataContract.UnderlyingType) != null
};

if (dataContract.UnderlyingType.IsEnum)
Expand All @@ -299,7 +301,7 @@ private OpenApiSchema CreateArraySchema(DataContract dataContract, SchemaReposit
return new OpenApiSchema
{
Type = "array",
Items = GenerateSchema(dataContract.ArrayItemType, schemaRepository),
Items = GenerateSchema(dataContract.ArrayItemType, schemaRepository, isEffectiveTypeNeeded: false),
UniqueItems = hasUniqueItems ? (bool?)true : null
};
}
Expand Down Expand Up @@ -487,4 +489,4 @@ private bool IsKnownSubType(DataContract dataContract, out DataContract baseType
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public interface ISchemaGenerator
SchemaRepository schemaRepository,
MemberInfo memberInfo = null,
ParameterInfo parameterInfo = null,
ApiParameterRouteInfo routeInfo = null);
ApiParameterRouteInfo routeInfo = null,
bool isEffectiveTypeNeeded = true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,22 +153,28 @@ public void GenerateSchema_GeneratesReferencedDictionarySchema_IfDictionaryTypeI
}

[Theory]
[InlineData(typeof(int[]), "integer", "int32")]
[InlineData(typeof(IEnumerable<string>), "string", null)]
[InlineData(typeof(DateTime?[]), "string", "date-time")]
[InlineData(typeof(int[][]), "array", null)]
[InlineData(typeof(IList), null, null)]
[InlineData(typeof(int[]), "integer", "int32", false)]
[InlineData(typeof(int?[]), "integer", "int32", true)]
[InlineData(typeof(List<int?>), "integer", "int32", true)]
[InlineData(typeof(Nullable<int>[]), "integer", "int32", true)]
[InlineData(typeof(IEnumerable<string>), "string", null, false)]
[InlineData(typeof(DateTime[]), "string", "date-time", false)]
[InlineData(typeof(DateTime?[]), "string", "date-time", true)]
[InlineData(typeof(int[][]), "array", null, false)]
[InlineData(typeof(IList), null, null, false)]
public void GenerateSchema_GeneratesArraySchema_IfEnumerableType(
Type type,
string expectedItemsType,
string expectedItemsFormat)
string expectedItemsFormat,
bool expectedItemsNullable)
{
var schema = Subject().GenerateSchema(type, new SchemaRepository());

Assert.Equal("array", schema.Type);
Assert.NotNull(schema.Items);
Assert.Equal(expectedItemsType, schema.Items.Type);
Assert.Equal(expectedItemsFormat, schema.Items.Format);
Assert.Equal(expectedItemsNullable, schema.Items.Nullable);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,22 +149,28 @@ public void GenerateSchema_GeneratesReferencedDictionarySchema_IfDictionaryTypeI
}

[Theory]
[InlineData(typeof(int[]), "integer", "int32")]
[InlineData(typeof(IEnumerable<string>), "string", null)]
[InlineData(typeof(DateTime?[]), "string", "date-time")]
[InlineData(typeof(int[][]), "array", null)]
[InlineData(typeof(IList), null, null)]
[InlineData(typeof(int[]), "integer", "int32", false)]
[InlineData(typeof(int?[]), "integer", "int32", true)]
[InlineData(typeof(List<int?>), "integer", "int32", true)]
[InlineData(typeof(Nullable<int>[]), "integer", "int32", true)]
[InlineData(typeof(IEnumerable<string>), "string", null, false)]
[InlineData(typeof(DateTime[]), "string", "date-time", false)]
[InlineData(typeof(DateTime?[]), "string", "date-time", true)]
[InlineData(typeof(int[][]), "array", null, false)]
[InlineData(typeof(IList), null, null, false)]
public void GenerateSchema_GeneratesArraySchema_IfEnumerableType(
Type type,
string expectedItemsType,
string expectedItemsFormat)
string expectedItemsFormat,
bool expectedItemsNullable)
{
var schema = Subject().GenerateSchema(type, new SchemaRepository());

Assert.Equal("array", schema.Type);
Assert.NotNull(schema.Items);
Assert.Equal(expectedItemsType, schema.Items.Type);
Assert.Equal(expectedItemsFormat, schema.Items.Format);
Assert.Equal(expectedItemsNullable, schema.Items.Nullable);
}

[Fact]
Expand Down

0 comments on commit 7078ab7

Please sign in to comment.