Skip to content

Commit f9f3a94

Browse files
committed
fix: refactor ToIdentifier() to normalize flaggable enums
1 parent 911b191 commit f9f3a94

File tree

3 files changed

+50
-38
lines changed

3 files changed

+50
-38
lines changed

src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Linq;
67
using Microsoft.OpenApi.Exceptions;
78
using Microsoft.OpenApi.Models;
89

@@ -19,7 +20,7 @@ public static class OpenApiTypeMapper
1920
/// </summary>
2021
/// <param name="schemaType"></param>
2122
/// <returns></returns>
22-
public static string? ToIdentifier(this JsonSchemaType? schemaType)
23+
public static string[]? ToIdentifier(this JsonSchemaType? schemaType)
2324
{
2425
if (schemaType is null)
2526
{
@@ -33,20 +34,21 @@ public static class OpenApiTypeMapper
3334
/// </summary>
3435
/// <param name="schemaType"></param>
3536
/// <returns></returns>
36-
public static string? ToIdentifier(this JsonSchemaType schemaType)
37+
public static string[] ToIdentifier(this JsonSchemaType schemaType)
3738
{
38-
return schemaType switch
39-
{
40-
JsonSchemaType.Null => "null",
41-
JsonSchemaType.Boolean => "boolean",
42-
JsonSchemaType.Integer => "integer",
43-
JsonSchemaType.Number => "number",
44-
JsonSchemaType.String => "string",
45-
JsonSchemaType.Array => "array",
46-
JsonSchemaType.Object => "object",
47-
_ => null,
48-
};
39+
var types = new List<string>();
40+
41+
if (schemaType.HasFlag(JsonSchemaType.Boolean)) types.Add("boolean");
42+
if (schemaType.HasFlag(JsonSchemaType.Integer)) types.Add("integer");
43+
if (schemaType.HasFlag(JsonSchemaType.Number)) types.Add("number");
44+
if (schemaType.HasFlag(JsonSchemaType.String)) types.Add("string");
45+
if (schemaType.HasFlag(JsonSchemaType.Object)) types.Add("object");
46+
if (schemaType.HasFlag(JsonSchemaType.Array)) types.Add("array");
47+
if (schemaType.HasFlag(JsonSchemaType.Null)) types.Add("null");
48+
49+
return types.ToArray();
4950
}
51+
5052
#nullable restore
5153

5254
/// <summary>
@@ -141,7 +143,7 @@ public static OpenApiSchema MapTypeToOpenApiPrimitiveType(this Type type)
141143
}
142144

143145
/// <summary>
144-
/// Maps an JsonSchema data type and format to a simple type.
146+
/// Maps a JsonSchema data type and format to a simple type.
145147
/// </summary>
146148
/// <param name="schema">The OpenApi data type</param>
147149
/// <returns>The simple type</returns>
@@ -152,21 +154,24 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema
152154
{
153155
throw new ArgumentNullException(nameof(schema));
154156
}
157+
var typeIdentifier = schema.Type.ToIdentifier();
158+
var isNullable = typeIdentifier.Contains("null");
159+
var nonNullable = typeIdentifier.FirstOrDefault(t => t != "null");
155160

156-
var type = ((schema.Type & ~JsonSchemaType.Null).ToIdentifier(), schema.Format?.ToLowerInvariant(), schema.Type & JsonSchemaType.Null) switch
161+
var type = (nonNullable, schema.Format?.ToLowerInvariant(), isNullable) switch
157162
{
158-
("integer" or "number", "int32", JsonSchemaType.Null) => typeof(int?),
159-
("integer" or "number", "int64", JsonSchemaType.Null) => typeof(long?),
160-
("integer", null, JsonSchemaType.Null) => typeof(long?),
161-
("number", "float", JsonSchemaType.Null) => typeof(float?),
162-
("number", "double", JsonSchemaType.Null) => typeof(double?),
163-
("number", null, JsonSchemaType.Null) => typeof(double?),
164-
("number", "decimal", JsonSchemaType.Null) => typeof(decimal?),
165-
("string", "byte", JsonSchemaType.Null) => typeof(byte?),
166-
("string", "date-time", JsonSchemaType.Null) => typeof(DateTimeOffset?),
167-
("string", "uuid", JsonSchemaType.Null) => typeof(Guid?),
168-
("string", "char", JsonSchemaType.Null) => typeof(char?),
169-
("boolean", null, JsonSchemaType.Null) => typeof(bool?),
163+
("integer" or "number", "int32", true) => typeof(int?),
164+
("integer" or "number", "int64", true) => typeof(long?),
165+
("integer", null, true) => typeof(long?),
166+
("number", "float", true) => typeof(float?),
167+
("number", "double", true) => typeof(double?),
168+
("number", null, true) => typeof(double?),
169+
("number", "decimal", true) => typeof(decimal?),
170+
("string", "byte", true) => typeof(byte?),
171+
("string", "date-time", true) => typeof(DateTimeOffset?),
172+
("string", "uuid", true) => typeof(Guid?),
173+
("string", "char", true) => typeof(char?),
174+
("boolean", null, true) => typeof(bool?),
170175
("boolean", null, _) => typeof(bool),
171176
// integer is technically not valid with format, but we must provide some compatibility
172177
("integer" or "number", "int32", _) => typeof(int),

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer)
413413
internal void WriteAsItemsProperties(IOpenApiWriter writer)
414414
{
415415
// type
416-
writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null).ToIdentifier());
416+
writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null).ToIdentifier()?.FirstOrDefault());
417417

418418
// format
419419
WriteFormatProperty(writer);
@@ -629,10 +629,10 @@ private void SerializeAsV2(
629629
private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer, OpenApiSpecVersion version)
630630
{
631631
// check whether nullable is true for upcasting purposes
632-
var isNullable = (Type.HasValue && Type.Value.HasFlag(JsonSchemaType.Null)) ||
632+
var isNullable = (Type.HasValue && Type.Value.HasFlag(JsonSchemaType.Null)) ||
633633
Extensions is not null &&
634634
Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) &&
635-
nullExtRawValue is OpenApiAny { Node: JsonNode jsonNode} &&
635+
nullExtRawValue is OpenApiAny { Node: JsonNode jsonNode } &&
636636
jsonNode.GetValueKind() is JsonValueKind.True;
637637
if (type is null)
638638
{
@@ -651,14 +651,14 @@ Extensions is not null &&
651651
break;
652652
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value == JsonSchemaType.Null:
653653
writer.WriteProperty(OpenApiConstants.Nullable, true);
654-
writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.ToIdentifier());
654+
writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.ToIdentifier().FirstOrDefault());
655655
break;
656656
case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value != JsonSchemaType.Null:
657657
writer.WriteProperty(OpenApiConstants.Nullable, true);
658-
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
658+
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault());
659659
break;
660660
default:
661-
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier());
661+
writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault());
662662
break;
663663
}
664664
}
@@ -674,7 +674,13 @@ Extensions is not null &&
674674
var list = (from JsonSchemaType flag in jsonSchemaTypeValues
675675
where type.Value.HasFlag(flag)
676676
select flag).ToList();
677-
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier()));
677+
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) =>
678+
{
679+
foreach(var item in s.ToIdentifier())
680+
{
681+
w.WriteValue(item);
682+
}
683+
});
678684
}
679685
}
680686
}
@@ -697,7 +703,7 @@ private static void UpCastSchemaTypeToV31(JsonSchemaType type, IOpenApiWriter wr
697703
var temporaryType = type | JsonSchemaType.Null;
698704
var list = (from JsonSchemaType flag in jsonSchemaTypeValues// Check if the flag is set in 'type' using a bitwise AND operation
699705
where temporaryType.HasFlag(flag)
700-
select flag.ToIdentifier()).ToList();
706+
select flag.ToIdentifier().FirstOrDefault()).ToList();
701707
if (list.Count > 1)
702708
{
703709
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s));
@@ -734,7 +740,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
734740
if (schemaType.HasFlag(flag) && flag != JsonSchemaType.Null)
735741
{
736742
// Write the non-null flag value to the writer
737-
writer.WriteProperty(OpenApiConstants.Type, flag.ToIdentifier());
743+
writer.WriteProperty(OpenApiConstants.Type, flag.ToIdentifier().FirstOrDefault());
738744
}
739745
}
740746
writer.WriteProperty(nullableProp, true);
@@ -747,7 +753,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
747753
}
748754
else
749755
{
750-
writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier());
756+
writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier().FirstOrDefault());
751757
}
752758
}
753759
}

src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
using System.Linq;
45
using System.Text.Json;
56
using System.Text.Json.Nodes;
67
using Microsoft.OpenApi.Extensions;
@@ -55,7 +56,7 @@ public static void ValidateDataTypeMismatch(
5556
// convert value to JsonElement and access the ValueKind property to determine the type.
5657
var valueKind = value.GetValueKind();
5758

58-
var type = schema.Type.ToIdentifier();
59+
var type = schema.Type.ToIdentifier().FirstOrDefault(x => x is not null);
5960
var format = schema.Format;
6061

6162
// Before checking the type, check first if the schema allows null.

0 commit comments

Comments
 (0)