Visitar URL original
fix: refactor ToIdentifier() to normalize flaggable enums by MaggieKimani1 · Pull Request #2156 · microsoft/OpenAPI.NET · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: address more PR feedback, add and fix tests
  • Loading branch information
MaggieKimani1 committed Feb 20, 2025
commit b79d66f5f0e7d08c3858c6fb063421d33afbef44
79 changes: 45 additions & 34 deletions src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ public static class OpenApiTypeMapper
/// </summary>
/// <param name="schemaType"></param>
/// <returns></returns>
public static string[]? ToIdentifier(this JsonSchemaType? schemaType)
public static string[]? ToIdentifiers(this JsonSchemaType? schemaType)
{
if (schemaType is null)
{
return null;
}
return schemaType.Value.ToIdentifier();
return schemaType.Value.ToIdentifiers();
}

/// <summary>
Expand Down Expand Up @@ -56,10 +56,24 @@ public static string[] ToIdentifiers(this JsonSchemaType schemaType)
/// <returns></returns>
internal static string ToFirstIdentifier(this JsonSchemaType schemaType)
{
var identifier = schemaType.ToIdentifier();
var identifier = schemaType.ToIdentifiers();
return identifier[0];
}

/// <summary>
/// Returns a single identifier from an array with only one item.
/// </summary>
/// <param name="schemaType"></param>
/// <returns></returns>
public static string ToSingleIdentifier(this JsonSchemaType schemaType)
{
if (schemaType.ToIdentifiers().Length != 1)
{
throw new OpenApiException($"Schema type {schemaType} must have exactly one identifier.");
}
return schemaType.ToFirstIdentifier();
Comment thread
MaggieKimani1 marked this conversation as resolved.
Resolved
}

#nullable restore

/// <summary>
Expand Down Expand Up @@ -185,40 +199,37 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema
{
throw new ArgumentNullException(nameof(schema));
}
var isNullable = (schema.Type & JsonSchemaType.Null) == JsonSchemaType.Null;
var nonNullable = (schema.Type & ~JsonSchemaType.Null)?.FirstIdentifier();

var type = (nonNullable, schema.Format?.ToLowerInvariant(), isNullable) switch
var type = (schema.Type, schema.Format?.ToLowerInvariant()) switch
{
("integer" or "number", "int32", true) => typeof(int?),
("integer" or "number", "int64", true) => typeof(long?),
("integer", null, true) => typeof(long?),
("number", "float", true) => typeof(float?),
("number", "double", true) => typeof(double?),
("number", null, true) => typeof(double?),
("number", "decimal", true) => typeof(decimal?),
("string", "byte", true) => typeof(byte?),
("string", "date-time", true) => typeof(DateTimeOffset?),
("string", "uuid", true) => typeof(Guid?),
("string", "char", true) => typeof(char?),
("boolean", null, true) => typeof(bool?),
("boolean", null, _) => typeof(bool),
(JsonSchemaType.Integer | JsonSchemaType.Null or JsonSchemaType.Number | JsonSchemaType.Null, "int32") => typeof(int?),
(JsonSchemaType.Integer | JsonSchemaType.Null or JsonSchemaType.Number | JsonSchemaType.Null, "int64") => typeof(long?),
(JsonSchemaType.Integer | JsonSchemaType.Null, null) => typeof(long?),
(JsonSchemaType.Number | JsonSchemaType.Null, "float") => typeof(float?),
(JsonSchemaType.Number | JsonSchemaType.Null, "double") => typeof(double?),
(JsonSchemaType.Number | JsonSchemaType.Null, null) => typeof(double?),
(JsonSchemaType.Number | JsonSchemaType.Null, "decimal") => typeof(decimal?),
(JsonSchemaType.String | JsonSchemaType.Null, "byte") => typeof(byte?),
(JsonSchemaType.String | JsonSchemaType.Null, "date-time") => typeof(DateTimeOffset?),
(JsonSchemaType.String | JsonSchemaType.Null, "uuid") => typeof(Guid?),
(JsonSchemaType.String | JsonSchemaType.Null, "char") => typeof(char?),
(JsonSchemaType.Boolean | JsonSchemaType.Null, null) => typeof(bool?),
(JsonSchemaType.Boolean, null) => typeof(bool),
// integer is technically not valid with format, but we must provide some compatibility
("integer" or "number", "int32", _) => typeof(int),
("integer" or "number", "int64", _) => typeof(long),
("integer", null, _) => typeof(long),
("number", "float", _) => typeof(float),
("number", "double", _) => typeof(double),
("number", "decimal", _) => typeof(decimal),
("number", null, _) => typeof(double),
("string", "byte", _) => typeof(byte),
("string", "date-time", _) => typeof(DateTimeOffset),
("string", "uuid", _) => typeof(Guid),
("string", "duration", _) => typeof(TimeSpan),
("string", "char", _) => typeof(char),
("string", null, _) => typeof(string),
("object", null, _) => typeof(object),
("string", "uri", _) => typeof(Uri),
(JsonSchemaType.Integer or JsonSchemaType.Number, "int32") => typeof(int),
(JsonSchemaType.Integer or JsonSchemaType.Number, "int64") => typeof(long),
(JsonSchemaType.Integer, null) => typeof(long),
(JsonSchemaType.Number, "float") => typeof(float),
(JsonSchemaType.Number, "double") => typeof(double),
(JsonSchemaType.Number, "decimal") => typeof(decimal),
(JsonSchemaType.Number, null) => typeof(double),
(JsonSchemaType.String, "byte") => typeof(byte),
(JsonSchemaType.String, "date-time") => typeof(DateTimeOffset),
(JsonSchemaType.String, "uuid") => typeof(Guid),
(JsonSchemaType.String, "char") => typeof(char),
(JsonSchemaType.String, null) => typeof(string),
(JsonSchemaType.Object, null) => typeof(object),
(JsonSchemaType.String, "uri") => typeof(Uri),
_ => typeof(string),
};

Expand Down
16 changes: 8 additions & 8 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer)
internal void WriteAsItemsProperties(IOpenApiWriter writer)
{
// type
writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null)?.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null)?.ToFirstIdentifier());

// format
WriteFormatProperty(writer);
Expand Down Expand Up @@ -651,14 +651,14 @@ Extensions is not null &&
break;
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value == JsonSchemaType.Null:
writer.WriteProperty(OpenApiConstants.Nullable, true);
writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.ToFirstIdentifier());
break;
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value != JsonSchemaType.Null:
writer.WriteProperty(OpenApiConstants.Nullable, true);
writer.WriteProperty(OpenApiConstants.Type, type.Value.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToFirstIdentifier());
break;
default:
writer.WriteProperty(OpenApiConstants.Type, type.Value.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToFirstIdentifier());
break;
}
}
Expand All @@ -676,7 +676,7 @@ where type.Value.HasFlag(flag)
select flag).ToList();
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) =>
{
foreach(var item in s.ToIdentifier())
foreach(var item in s.ToIdentifiers())
{
w.WriteValue(item);
}
Expand All @@ -703,7 +703,7 @@ private static void UpCastSchemaTypeToV31(JsonSchemaType type, IOpenApiWriter wr
var temporaryType = type | JsonSchemaType.Null;
var list = (from JsonSchemaType flag in jsonSchemaTypeValues// Check if the flag is set in 'type' using a bitwise AND operation
where temporaryType.HasFlag(flag)
select flag.FirstIdentifier()).ToList();
select flag.ToFirstIdentifier()).ToList();
if (list.Count > 1)
{
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s));
Expand Down Expand Up @@ -740,7 +740,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
if (schemaType.HasFlag(flag) && flag != JsonSchemaType.Null)
{
// Write the non-null flag value to the writer
writer.WriteProperty(OpenApiConstants.Type, flag.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, flag.ToFirstIdentifier());
}
}
writer.WriteProperty(nullableProp, true);
Expand All @@ -753,7 +753,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
}
else
{
writer.WriteProperty(OpenApiConstants.Type, schemaType.FirstIdentifier());
writer.WriteProperty(OpenApiConstants.Type, schemaType.ToFirstIdentifier());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static void ValidateDataTypeMismatch(
// convert value to JsonElement and access the ValueKind property to determine the type.
var valueKind = value.GetValueKind();

var type = (schema.Type & ~JsonSchemaType.Null)?.FirstIdentifier();
var type = (schema.Type & ~JsonSchemaType.Null)?.ToFirstIdentifier();
var format = schema.Format;

// Before checking the type, check first if the schema allows null.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using Microsoft.OpenApi.Tests;
using Microsoft.OpenApi.Writers;
using Xunit;
using Microsoft.OpenApi.Exceptions;

namespace Microsoft.OpenApi.Readers.Tests.V31Tests
{
Expand Down Expand Up @@ -532,7 +533,7 @@ public void NormalizeFlaggableJsonSchemaTypeEnumWorks(JsonSchemaType type, strin
Type = type
};

var actual = schema.Type.ToIdentifier();
var actual = schema.Type.ToIdentifiers();
Assert.Equal(expected, actual);
}

Expand All @@ -552,5 +553,15 @@ public void StringIdentifierToEnumConversionWorks()
var actual = "integer".ToJsonSchemaType();
Assert.Equal(JsonSchemaType.Integer, actual);
}

[Fact]
public void ReturnSingleIdentifierWorks()
{
var type = JsonSchemaType.Integer;
var types = JsonSchemaType.Integer | JsonSchemaType.Null;

Assert.Equal("integer", type.ToSingleIdentifier());
Assert.Throws<OpenApiException>(() => types.ToSingleIdentifier());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,11 @@ namespace Microsoft.OpenApi.Extensions
{
public static System.Type MapOpenApiPrimitiveTypeToSimpleType(this Microsoft.OpenApi.Models.OpenApiSchema schema) { }
public static Microsoft.OpenApi.Models.OpenApiSchema MapTypeToOpenApiPrimitiveType(this System.Type type) { }
public static string[] ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { }
public static string[]? ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType? schemaType) { }
public static string[] ToIdentifiers(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { }
public static string[]? ToIdentifiers(this Microsoft.OpenApi.Models.JsonSchemaType? schemaType) { }
public static Microsoft.OpenApi.Models.JsonSchemaType ToJsonSchemaType(this string identifier) { }
public static Microsoft.OpenApi.Models.JsonSchemaType? ToJsonSchemaType(this string[] identifier) { }
public static string ToSingleIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { }
}
}
namespace Microsoft.OpenApi.Interfaces
Expand Down