Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend built-in supported types #2804

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ public DataContract GetDataContractForType(Type type)

if (jsonContract is JsonPrimitiveContract && !jsonContract.UnderlyingType.IsEnum)
{
var primitiveTypeAndFormat = PrimitiveTypesAndFormats.TryGetValue(jsonContract.UnderlyingType, out var format)
? format
: Tuple.Create(DataType.String, (string)null);
if (!PrimitiveTypesAndFormats.TryGetValue(jsonContract.UnderlyingType, out var primitiveTypeAndFormat))
{
primitiveTypeAndFormat = Tuple.Create(DataType.String, (string)null);
}

return DataContract.ForPrimitive(
underlyingType: jsonContract.UnderlyingType,
Expand All @@ -48,7 +49,7 @@ public DataContract GetDataContractForType(Type type)
{
var enumValues = jsonContract.UnderlyingType.GetEnumValues();

//Test to determine if the serializer will treat as string
// Test to determine if the serializer will treat as string
var serializeAsString = (enumValues.Length > 0)
&& JsonConverterFunc(enumValues.GetValue(0)).StartsWith("\"");

Expand Down Expand Up @@ -83,7 +84,7 @@ public DataContract GetDataContractForType(Type type)
// This is a special case where we know the possible key values
var enumValuesAsJson = keyType.GetEnumValues()
.Cast<object>()
.Select(value => JsonConverterFunc(value));
.Select(JsonConverterFunc);

keys = enumValuesAsJson.Any(json => json.StartsWith("\""))
? enumValuesAsJson.Select(json => json.Replace("\"", string.Empty))
Expand All @@ -99,6 +100,16 @@ public DataContract GetDataContractForType(Type type)

if (jsonContract is JsonObjectContract jsonObjectContract)
{
// This handles DateOnly and TimeOnly
if (PrimitiveTypesAndFormats.TryGetValue(jsonContract.UnderlyingType, out var primitiveTypeAndFormat))
{
return DataContract.ForPrimitive(
underlyingType: jsonContract.UnderlyingType,
dataType: primitiveTypeAndFormat.Item1,
dataFormat: primitiveTypeAndFormat.Item2,
jsonConverter: JsonConverterFunc);
}

string typeNameProperty = null;
string typeNameValue = null;

Expand Down Expand Up @@ -186,7 +197,7 @@ private IEnumerable<DataProperty> GetDataPropertiesFor(JsonObjectContract jsonOb
return dataProperties;
}

private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new Dictionary<Type, Tuple<DataType, string>>
private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new()
private static readonly Dictionary<Type, Tuple<DataType, string>> PrimitiveTypesAndFormats = new

{
[ typeof(bool) ] = Tuple.Create(DataType.Boolean, (string)null),
[ typeof(byte) ] = Tuple.Create(DataType.Integer, "int32"),
Expand All @@ -210,7 +221,11 @@ private IEnumerable<DataProperty> GetDataPropertiesFor(JsonObjectContract jsonOb
[ typeof(TimeSpan) ] = Tuple.Create(DataType.String, "date-span"),
#if NET6_0_OR_GREATER
[ typeof(DateOnly) ] = Tuple.Create(DataType.String, "date"),
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time")
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time"),
#endif
#if NET7_0_OR_GREATER
[ typeof(Int128) ] = Tuple.Create(DataType.Integer, "int128"),
[ typeof(UInt128) ] = Tuple.Create(DataType.Integer, "int128"),
#endif
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public DataContract GetDataContractForType(Type type)
jsonConverter: JsonConverterFunc);
}

if (PrimitiveTypesAndFormats.TryGetValue(type, out var primitiveTypeAndFormat1))
if (PrimitiveTypesAndFormats.TryGetValue(type, out var primitiveTypeAndFormat))
{
return DataContract.ForPrimitive(
underlyingType: type,
dataType: primitiveTypeAndFormat1.Item1,
dataFormat: primitiveTypeAndFormat1.Item2,
dataType: primitiveTypeAndFormat.Item1,
dataFormat: primitiveTypeAndFormat.Item2,
jsonConverter: JsonConverterFunc);
}

Expand All @@ -44,7 +44,7 @@ public DataContract GetDataContractForType(Type type)
var serializeAsString = (enumValues.Length > 0)
&& JsonConverterFunc(enumValues.GetValue(0)).StartsWith("\"");

var primitiveTypeAndFormat = serializeAsString
primitiveTypeAndFormat = serializeAsString
? PrimitiveTypesAndFormats[typeof(string)]
: PrimitiveTypesAndFormats[type.GetEnumUnderlyingType()];

Expand Down Expand Up @@ -240,11 +240,17 @@ private IEnumerable<DataProperty> GetDataPropertiesFor(Type objectType, out Type
[ 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(TimeSpan) ] = Tuple.Create(DataType.String, "date-span"),
[ typeof(Guid) ] = Tuple.Create(DataType.String, "uuid"),
[ typeof(Uri) ] = Tuple.Create(DataType.String, "uri"),
[ typeof(Version) ] = Tuple.Create(DataType.String, (string)null),
#if NET6_0_OR_GREATER
[ typeof(DateOnly) ] = Tuple.Create(DataType.String, "date"),
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time")
[ typeof(TimeOnly) ] = Tuple.Create(DataType.String, "time"),
#endif
#if NET7_0_OR_GREATER
[ typeof(Int128) ] = Tuple.Create(DataType.Integer, "int128"),
[ typeof(UInt128) ] = Tuple.Create(DataType.Integer, "int128"),
#endif
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ namespace Swashbuckle.AspNetCore.SwaggerGen
{
public static class OpenApiSchemaExtensions
{
private static readonly Dictionary<AnnotationsDataType, string> DataFormatMappings = new()
{
[AnnotationsDataType.DateTime] = "date-time",
[AnnotationsDataType.Date] = "date",
[AnnotationsDataType.Time] = "time",
[AnnotationsDataType.Duration] = "duration",
[AnnotationsDataType.PhoneNumber] = "tel",
[AnnotationsDataType.Currency] = "currency",
[AnnotationsDataType.Text] = "string",
[AnnotationsDataType.Html] = "html",
[AnnotationsDataType.MultilineText] = "multiline",
[AnnotationsDataType.EmailAddress] = "email",
[AnnotationsDataType.Password] = "password",
[AnnotationsDataType.Url] = "uri",
[AnnotationsDataType.ImageUrl] = "uri",
[AnnotationsDataType.CreditCard] = "credit-card",
[AnnotationsDataType.PostalCode] = "postal-code",
[AnnotationsDataType.Upload] = "binary",
};

public static void ApplyValidationAttributes(this OpenApiSchema schema, IEnumerable<object> customAttributes)
{
foreach (var attribute in customAttributes)
Expand Down Expand Up @@ -88,26 +108,7 @@ public static string ResolveType(this OpenApiSchema schema, SchemaRepository sch

private static void ApplyDataTypeAttribute(OpenApiSchema schema, DataTypeAttribute dataTypeAttribute)
{
var formats = new Dictionary<AnnotationsDataType, string>
{
{ AnnotationsDataType.DateTime, "date-time" },
{ AnnotationsDataType.Date, "date" },
{ AnnotationsDataType.Time, "time" },
{ AnnotationsDataType.Duration, "duration" },
{ AnnotationsDataType.PhoneNumber, "tel" },
{ AnnotationsDataType.Currency, "currency" },
{ AnnotationsDataType.Text, "string" },
{ AnnotationsDataType.Html, "html" },
{ AnnotationsDataType.MultilineText, "multiline" },
{ AnnotationsDataType.EmailAddress, "email" },
{ AnnotationsDataType.Password, "password" },
{ AnnotationsDataType.Url, "uri" },
{ AnnotationsDataType.ImageUrl, "uri" },
{ AnnotationsDataType.CreditCard, "credit-card" },
{ AnnotationsDataType.PostalCode, "postal-code" }
};

if (formats.TryGetValue(dataTypeAttribute.DataType, out string format))
if (DataFormatMappings.TryGetValue(dataTypeAttribute.DataType, out string format))
{
schema.Format = format;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type t
}

[Theory]
[InlineData(typeof(bool), "boolean", null)]
[InlineData(typeof(byte), "integer", "int32")]
[InlineData(typeof(sbyte), "integer", "int32")]
[InlineData(typeof(short), "integer", "int32")]
Expand All @@ -48,12 +49,24 @@ public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type t
[InlineData(typeof(byte[]), "string", "byte")]
[InlineData(typeof(DateTime), "string", "date-time")]
[InlineData(typeof(DateTimeOffset), "string", "date-time")]
[InlineData(typeof(Guid), "string", "uuid")]
[InlineData(typeof(TimeSpan), "string", "date-span")]
[InlineData(typeof(Guid), "string", "uuid")]
[InlineData(typeof(Uri), "string", "uri")]
[InlineData(typeof(Version), "string", null)]
[InlineData(typeof(DateOnly), "string", "date")]
[InlineData(typeof(TimeOnly), "string", "time")]
[InlineData(typeof(bool?), "boolean", null)]
[InlineData(typeof(int?), "integer", "int32")]
[InlineData(typeof(DateTime?), "string", "date-time")]
[InlineData(typeof(Guid?), "string", "uuid")]
[InlineData(typeof(DateOnly?), "string", "date")]
[InlineData(typeof(TimeOnly?), "string", "time")]
#if NET7_0_OR_GREATER
[InlineData(typeof(Int128), "integer", "int128")]
[InlineData(typeof(Int128?), "integer", "int128")]
[InlineData(typeof(UInt128), "integer", "int128")]
[InlineData(typeof(UInt128?), "integer", "int128")]
#endif
public void GenerateSchema_GeneratesPrimitiveSchema_IfPrimitiveOrNullablePrimitiveType(
Type type,
string expectedSchemaType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type t
[InlineData(typeof(byte[]), "string", "byte")]
[InlineData(typeof(DateTime), "string", "date-time")]
[InlineData(typeof(DateTimeOffset), "string", "date-time")]
[InlineData(typeof(TimeSpan), "string", "date-span")]
[InlineData(typeof(Guid), "string", "uuid")]
[InlineData(typeof(Uri), "string", "uri")]
[InlineData(typeof(Version), "string", null)]
[InlineData(typeof(DateOnly), "string", "date")]
[InlineData(typeof(TimeOnly), "string", "time")]
[InlineData(typeof(bool?), "boolean", null)]
Expand All @@ -61,6 +63,12 @@ public void GenerateSchema_GeneratesFileSchema_IfFormFileOrFileResultType(Type t
[InlineData(typeof(Guid?), "string", "uuid")]
[InlineData(typeof(DateOnly?), "string", "date")]
[InlineData(typeof(TimeOnly?), "string", "time")]
#if NET7_0_OR_GREATER
[InlineData(typeof(Int128), "integer", "int128")]
[InlineData(typeof(Int128?), "integer", "int128")]
[InlineData(typeof(UInt128), "integer", "int128")]
[InlineData(typeof(UInt128?), "integer", "int128")]
#endif
public void GenerateSchema_GeneratesPrimitiveSchema_IfPrimitiveOrNullablePrimitiveType(
Type type,
string expectedSchemaType,
Expand Down