From f9f3a947213601da5b63895ad1f53db181c3d3a1 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 18 Feb 2025 12:12:15 +0300 Subject: [PATCH 01/15] fix: refactor ToIdentifier() to normalize flaggable enums --- .../Extensions/OpenApiTypeMapper.cs | 59 ++++++++++--------- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 26 ++++---- .../Validations/Rules/RuleHelpers.cs | 3 +- 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index eea41be49..1ab74995b 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Models; @@ -19,7 +20,7 @@ public static class OpenApiTypeMapper /// /// /// - public static string? ToIdentifier(this JsonSchemaType? schemaType) + public static string[]? ToIdentifier(this JsonSchemaType? schemaType) { if (schemaType is null) { @@ -33,20 +34,21 @@ public static class OpenApiTypeMapper /// /// /// - public static string? ToIdentifier(this JsonSchemaType schemaType) + public static string[] ToIdentifier(this JsonSchemaType schemaType) { - return schemaType switch - { - JsonSchemaType.Null => "null", - JsonSchemaType.Boolean => "boolean", - JsonSchemaType.Integer => "integer", - JsonSchemaType.Number => "number", - JsonSchemaType.String => "string", - JsonSchemaType.Array => "array", - JsonSchemaType.Object => "object", - _ => null, - }; + var types = new List(); + + if (schemaType.HasFlag(JsonSchemaType.Boolean)) types.Add("boolean"); + if (schemaType.HasFlag(JsonSchemaType.Integer)) types.Add("integer"); + if (schemaType.HasFlag(JsonSchemaType.Number)) types.Add("number"); + if (schemaType.HasFlag(JsonSchemaType.String)) types.Add("string"); + if (schemaType.HasFlag(JsonSchemaType.Object)) types.Add("object"); + if (schemaType.HasFlag(JsonSchemaType.Array)) types.Add("array"); + if (schemaType.HasFlag(JsonSchemaType.Null)) types.Add("null"); + + return types.ToArray(); } + #nullable restore /// @@ -141,7 +143,7 @@ public static OpenApiSchema MapTypeToOpenApiPrimitiveType(this Type type) } /// - /// Maps an JsonSchema data type and format to a simple type. + /// Maps a JsonSchema data type and format to a simple type. /// /// The OpenApi data type /// The simple type @@ -152,21 +154,24 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema { throw new ArgumentNullException(nameof(schema)); } + var typeIdentifier = schema.Type.ToIdentifier(); + var isNullable = typeIdentifier.Contains("null"); + var nonNullable = typeIdentifier.FirstOrDefault(t => t != "null"); - var type = ((schema.Type & ~JsonSchemaType.Null).ToIdentifier(), schema.Format?.ToLowerInvariant(), schema.Type & JsonSchemaType.Null) switch + var type = (nonNullable, schema.Format?.ToLowerInvariant(), isNullable) switch { - ("integer" or "number", "int32", JsonSchemaType.Null) => typeof(int?), - ("integer" or "number", "int64", JsonSchemaType.Null) => typeof(long?), - ("integer", null, JsonSchemaType.Null) => typeof(long?), - ("number", "float", JsonSchemaType.Null) => typeof(float?), - ("number", "double", JsonSchemaType.Null) => typeof(double?), - ("number", null, JsonSchemaType.Null) => typeof(double?), - ("number", "decimal", JsonSchemaType.Null) => typeof(decimal?), - ("string", "byte", JsonSchemaType.Null) => typeof(byte?), - ("string", "date-time", JsonSchemaType.Null) => typeof(DateTimeOffset?), - ("string", "uuid", JsonSchemaType.Null) => typeof(Guid?), - ("string", "char", JsonSchemaType.Null) => typeof(char?), - ("boolean", null, JsonSchemaType.Null) => typeof(bool?), + ("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), // integer is technically not valid with format, but we must provide some compatibility ("integer" or "number", "int32", _) => typeof(int), diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index cfed33744..d5abebf0c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -413,7 +413,7 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer) internal void WriteAsItemsProperties(IOpenApiWriter writer) { // type - writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null).ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null).ToIdentifier()?.FirstOrDefault()); // format WriteFormatProperty(writer); @@ -629,10 +629,10 @@ private void SerializeAsV2( private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer, OpenApiSpecVersion version) { // check whether nullable is true for upcasting purposes - var isNullable = (Type.HasValue && Type.Value.HasFlag(JsonSchemaType.Null)) || + var isNullable = (Type.HasValue && Type.Value.HasFlag(JsonSchemaType.Null)) || Extensions is not null && Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) && - nullExtRawValue is OpenApiAny { Node: JsonNode jsonNode} && + nullExtRawValue is OpenApiAny { Node: JsonNode jsonNode } && jsonNode.GetValueKind() is JsonValueKind.True; if (type is null) { @@ -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.ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.ToIdentifier().FirstOrDefault()); break; case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value != JsonSchemaType.Null: writer.WriteProperty(OpenApiConstants.Nullable, true); - writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault()); break; default: - writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault()); break; } } @@ -674,7 +674,13 @@ Extensions is not null && var list = (from JsonSchemaType flag in jsonSchemaTypeValues where type.Value.HasFlag(flag) select flag).ToList(); - writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier())); + writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => + { + foreach(var item in s.ToIdentifier()) + { + w.WriteValue(item); + } + }); } } } @@ -697,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.ToIdentifier()).ToList(); + select flag.ToIdentifier().FirstOrDefault()).ToList(); if (list.Count > 1) { writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s)); @@ -734,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.ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, flag.ToIdentifier().FirstOrDefault()); } } writer.WriteProperty(nullableProp, true); @@ -747,7 +753,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter } else { - writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier().FirstOrDefault()); } } } diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs index 63ca4d05e..edebffb86 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.OpenApi.Extensions; @@ -55,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.ToIdentifier(); + var type = schema.Type.ToIdentifier().FirstOrDefault(x => x is not null); var format = schema.Format; // Before checking the type, check first if the schema allows null. From 9ae9565eccca2f15cdfe442fcf6a6cc6a2698242 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 18 Feb 2025 12:31:37 +0300 Subject: [PATCH 02/15] fix: add support for casting type array to a flaggable enum --- .../Extensions/OpenApiTypeMapper.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 1ab74995b..f5b96ed81 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -72,6 +72,21 @@ public static JsonSchemaType ToJsonSchemaType(this string identifier) }; } + /// + /// Converts a schema type's identifier into the enum equivalent + /// + /// + /// + public static JsonSchemaType ToJsonSchemaType(this string[] identifier) + { + JsonSchemaType type = 0; + foreach (var id in identifier) + { + type |= id.ToJsonSchemaType(); + } + return type; + } + private static readonly Dictionary> _simpleTypeToOpenApiSchema = new() { [typeof(bool)] = () => new() { Type = JsonSchemaType.Boolean }, From 3c927e9e7a2a9e5510633f79399482876ceb9f17 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 18 Feb 2025 12:32:01 +0300 Subject: [PATCH 03/15] chore: add tests and update public API --- .../V31Tests/OpenApiSchemaTests.cs | 42 ++++++++++++++++--- .../PublicApi/PublicApi.approved.txt | 5 ++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs index 555b71c54..bc4bfe48a 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs @@ -9,6 +9,7 @@ using FluentAssertions; using FluentAssertions.Equivalency; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Tests; @@ -31,7 +32,7 @@ public static MemoryStream GetMemoryStream(string fileName) public OpenApiSchemaTests() { - OpenApiReaderRegistry.RegisterReader("yaml", new OpenApiYamlReader()); + OpenApiReaderRegistry.RegisterReader("yaml", new OpenApiYamlReader()); } [Fact] @@ -298,8 +299,8 @@ public void CloningSchemaWithExamplesAndEnumsShouldSucceed() clone.Default = 6; // Assert - Assert.Equivalent(new int[] {1, 2, 3, 4}, clone.Enum.Select(static x => x.GetValue()).ToArray()); - Assert.Equivalent(new int[] {2, 3, 4}, clone.Examples.Select(static x => x.GetValue()).ToArray()); + Assert.Equivalent(new int[] { 1, 2, 3, 4 }, clone.Enum.Select(static x => x.GetValue()).ToArray()); + Assert.Equivalent(new int[] { 2, 3, 4 }, clone.Examples.Select(static x => x.GetValue()).ToArray()); Assert.Equivalent(6, clone.Default.GetValue()); } @@ -398,7 +399,7 @@ public void SerializeSchemaWithTypeArrayAndNullableDoesntEmitType() schema.SerializeAsV2(new OpenApiYamlWriter(writer)); var schemaString = writer.ToString(); - Assert.Equal(expected.MakeLineBreaksEnvironmentNeutral(), schemaString.MakeLineBreaksEnvironmentNeutral()); + Assert.Equal(expected.MakeLineBreaksEnvironmentNeutral(), schemaString.MakeLineBreaksEnvironmentNeutral()); } [Theory] @@ -506,7 +507,7 @@ public async Task ParseSchemaWithConstWorks() } [Fact] - public void ParseSchemaWithUnrecognizedKeywordsWorks() + public void ParseSchemaWithUnrecognizedKeywordsWorks() { var input = @"{ ""type"": ""string"", @@ -520,5 +521,36 @@ public void ParseSchemaWithUnrecognizedKeywordsWorks() Assert.Equal(2, schema.UnrecognizedKeywords.Count); } + [Theory] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.String, new[] { "integer", "string" })] + [InlineData(JsonSchemaType.Integer | JsonSchemaType.Null, new[] { "integer", "null" })] + [InlineData(JsonSchemaType.Integer, new[] { "integer" })] + public void NormalizeFlaggableJsonSchemaTypeEnumWorks(JsonSchemaType type, string[] expected) + { + var schema = new OpenApiSchema + { + Type = type + }; + + var actual = schema.Type.ToIdentifier(); + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData(new[] { "integer", "string" }, JsonSchemaType.Integer | JsonSchemaType.String)] + [InlineData(new[] { "integer", "null" }, JsonSchemaType.Integer | JsonSchemaType.Null)] + [InlineData(new[] { "integer" }, JsonSchemaType.Integer)] + public void ArrayIdentifierToEnumConversionWorks(string[] type, JsonSchemaType expected) + { + var actual = type.ToJsonSchemaType(); + Assert.Equal(expected, actual); + } + + [Fact] + public void StringIdentifierToEnumConversionWorks() + { + var actual = "integer".ToJsonSchemaType(); + Assert.Equal(JsonSchemaType.Integer, actual); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index c1fb18800..8348fc084 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -190,9 +190,10 @@ 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[] ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { } + public static string[]? ToIdentifier(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) { } } } namespace Microsoft.OpenApi.Interfaces From a731be6202e9f1144c0bda33c4df2b7adc3d632a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 19 Feb 2025 12:34:28 +0300 Subject: [PATCH 04/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Vincent Biret --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index f5b96ed81..73355bb2b 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -169,9 +169,8 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema { throw new ArgumentNullException(nameof(schema)); } - var typeIdentifier = schema.Type.ToIdentifier(); - var isNullable = typeIdentifier.Contains("null"); - var nonNullable = typeIdentifier.FirstOrDefault(t => t != "null"); + var isNullable = (schema.Type & JsonSchemaType.Null) == JsonSchemaType.Null; + var nonNullable = (schema.Type & ~JsonSchemaType.Null).ToIdentifier().FirstOrDefault(); var type = (nonNullable, schema.Format?.ToLowerInvariant(), isNullable) switch { From 0f4a837e0388e378c13539a4fa9ccaaf7b4a7763 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 19 Feb 2025 13:04:02 +0300 Subject: [PATCH 05/15] chore: address PR feedback --- .../Extensions/OpenApiTypeMapper.cs | 20 +++++++++++++++++-- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 14 ++++++------- .../Validations/Rules/RuleHelpers.cs | 2 +- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 73355bb2b..15a41c6aa 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -49,6 +49,17 @@ public static string[] ToIdentifier(this JsonSchemaType schemaType) return types.ToArray(); } + /// + /// Returns the first identifier from a string array. + /// + /// + /// + public static string FirstIdentifier(this JsonSchemaType schemaType) + { + var identifier = schemaType.ToIdentifier(); + return identifier[0]; + } + #nullable restore /// @@ -77,8 +88,13 @@ public static JsonSchemaType ToJsonSchemaType(this string identifier) /// /// /// - public static JsonSchemaType ToJsonSchemaType(this string[] identifier) + public static JsonSchemaType? ToJsonSchemaType(this string[] identifier) { + if (identifier == null) + { + return null; + } + JsonSchemaType type = 0; foreach (var id in identifier) { @@ -170,7 +186,7 @@ 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).ToIdentifier().FirstOrDefault(); + var nonNullable = (schema.Type & ~JsonSchemaType.Null)?.FirstIdentifier(); var type = (nonNullable, schema.Format?.ToLowerInvariant(), isNullable) switch { diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index d5abebf0c..8d211ea40 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -413,7 +413,7 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer) internal void WriteAsItemsProperties(IOpenApiWriter writer) { // type - writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null).ToIdentifier()?.FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, (Type & ~JsonSchemaType.Null)?.FirstIdentifier()); // format WriteFormatProperty(writer); @@ -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.ToIdentifier().FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, JsonSchemaType.Object.FirstIdentifier()); break; case OpenApiSpecVersion.OpenApi3_0 when isNullable && type.Value != JsonSchemaType.Null: writer.WriteProperty(OpenApiConstants.Nullable, true); - writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, type.Value.FirstIdentifier()); break; default: - writer.WriteProperty(OpenApiConstants.Type, type.Value.ToIdentifier().FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, type.Value.FirstIdentifier()); break; } } @@ -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.ToIdentifier().FirstOrDefault()).ToList(); + select flag.FirstIdentifier()).ToList(); if (list.Count > 1) { writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s)); @@ -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.ToIdentifier().FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, flag.FirstIdentifier()); } } writer.WriteProperty(nullableProp, true); @@ -753,7 +753,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter } else { - writer.WriteProperty(OpenApiConstants.Type, schemaType.ToIdentifier().FirstOrDefault()); + writer.WriteProperty(OpenApiConstants.Type, schemaType.FirstIdentifier()); } } } diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs index edebffb86..1ebded2eb 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -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.ToIdentifier().FirstOrDefault(x => x is not null); + var type = (schema.Type & ~JsonSchemaType.Null)?.FirstIdentifier(); var format = schema.Format; // Before checking the type, check first if the schema allows null. From b6b9f74d245eba6509b0ec73186e40960eab1718 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 19 Feb 2025 13:11:04 +0300 Subject: [PATCH 06/15] chore: make method internal; update public API --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 2 +- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 15a41c6aa..07f9179d2 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -54,7 +54,7 @@ public static string[] ToIdentifier(this JsonSchemaType schemaType) /// /// /// - public static string FirstIdentifier(this JsonSchemaType schemaType) + internal static string FirstIdentifier(this JsonSchemaType schemaType) { var identifier = schemaType.ToIdentifier(); return identifier[0]; diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 8348fc084..a75c12492 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -193,7 +193,7 @@ namespace Microsoft.OpenApi.Extensions public static string[] ToIdentifier(this Microsoft.OpenApi.Models.JsonSchemaType schemaType) { } public static string[]? ToIdentifier(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 Microsoft.OpenApi.Models.JsonSchemaType? ToJsonSchemaType(this string[] identifier) { } } } namespace Microsoft.OpenApi.Interfaces From 63dca8a0a292c91ec08dcb9979ae2332f7b13fa5 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 20 Feb 2025 11:05:20 +0300 Subject: [PATCH 07/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Darrel --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 07f9179d2..ff58ee18a 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -54,7 +54,7 @@ public static string[] ToIdentifier(this JsonSchemaType schemaType) /// /// /// - internal static string FirstIdentifier(this JsonSchemaType schemaType) + internal static string ToFirstIdentifier(this JsonSchemaType schemaType) { var identifier = schemaType.ToIdentifier(); return identifier[0]; From ff9f164c7a7b41275122651a7cfc82dd256e897f Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 20 Feb 2025 11:05:52 +0300 Subject: [PATCH 08/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Darrel --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index ff58ee18a..89c766684 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -34,7 +34,7 @@ public static class OpenApiTypeMapper /// /// /// - public static string[] ToIdentifier(this JsonSchemaType schemaType) + public static string[] ToIdentifiers(this JsonSchemaType schemaType) { var types = new List(); From b79d66f5f0e7d08c3858c6fb063421d33afbef44 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 20 Feb 2025 11:36:12 +0300 Subject: [PATCH 09/15] fix: address more PR feedback, add and fix tests --- .../Extensions/OpenApiTypeMapper.cs | 79 +++++++++++-------- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 16 ++-- .../Validations/Rules/RuleHelpers.cs | 2 +- .../V31Tests/OpenApiSchemaTests.cs | 13 ++- .../PublicApi/PublicApi.approved.txt | 5 +- 5 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 89c766684..9ee48bc87 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -20,13 +20,13 @@ public static class OpenApiTypeMapper /// /// /// - 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(); } /// @@ -56,10 +56,24 @@ public static string[] ToIdentifiers(this JsonSchemaType schemaType) /// internal static string ToFirstIdentifier(this JsonSchemaType schemaType) { - var identifier = schemaType.ToIdentifier(); + var identifier = schemaType.ToIdentifiers(); return identifier[0]; } + /// + /// Returns a single identifier from an array with only one item. + /// + /// + /// + 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(); + } + #nullable restore /// @@ -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), }; diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 8d211ea40..657471e3b 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -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); @@ -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; } } @@ -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); } @@ -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)); @@ -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); @@ -753,7 +753,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter } else { - writer.WriteProperty(OpenApiConstants.Type, schemaType.FirstIdentifier()); + writer.WriteProperty(OpenApiConstants.Type, schemaType.ToFirstIdentifier()); } } } diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs index 1ebded2eb..71f46255f 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -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. diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs index bc4bfe48a..094a1ce2a 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs @@ -15,6 +15,7 @@ using Microsoft.OpenApi.Tests; using Microsoft.OpenApi.Writers; using Xunit; +using Microsoft.OpenApi.Exceptions; namespace Microsoft.OpenApi.Readers.Tests.V31Tests { @@ -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); } @@ -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(() => types.ToSingleIdentifier()); + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index a75c12492..68f29cf11 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -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 From fe50da3a35c39423d6772401aabaf8d41f060e31 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 20 Feb 2025 17:21:20 +0300 Subject: [PATCH 10/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Vincent Biret --- .../Extensions/OpenApiTypeMapper.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 9ee48bc87..e53ceb040 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -36,17 +36,20 @@ public static class OpenApiTypeMapper /// public static string[] ToIdentifiers(this JsonSchemaType schemaType) { - var types = new List(); - - if (schemaType.HasFlag(JsonSchemaType.Boolean)) types.Add("boolean"); - if (schemaType.HasFlag(JsonSchemaType.Integer)) types.Add("integer"); - if (schemaType.HasFlag(JsonSchemaType.Number)) types.Add("number"); - if (schemaType.HasFlag(JsonSchemaType.String)) types.Add("string"); - if (schemaType.HasFlag(JsonSchemaType.Object)) types.Add("object"); - if (schemaType.HasFlag(JsonSchemaType.Array)) types.Add("array"); - if (schemaType.HasFlag(JsonSchemaType.Null)) types.Add("null"); - - return types.ToArray(); + return schemaType.ToIdentifiersInternal().ToArray(); + } + private static readonly Dictionary allSchemaTypes = [ + { JsonSchemaType.Boolean, "boolean"}, + { JsonSchemaType.Integer, "integer" }, + { JsonSchemaType.Number, "number" }, + { JsonSchemaType.String, "string" }, + { JsonSchemaType.Object, "object" }, + { JsonSchemaType.Array, "array" }, + { JsonSchemaType.Null, "null" }, + ]; + private static IEnumerable ToIdentifiersInternal(this JsonSchemaType schemaType) + { + return allSchemaTypes.Where(kvp => schemaType.HasFlag(kvp.Key)).Select(static kvp => kvp.Value); } /// From cbb1407ca8b3c0be3b3d2889ee5da450ee807e0a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 20 Feb 2025 17:21:40 +0300 Subject: [PATCH 11/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Vincent Biret --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index e53ceb040..081fe4bbf 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -59,8 +59,7 @@ private static IEnumerable ToIdentifiersInternal(this JsonSchemaType sch /// internal static string ToFirstIdentifier(this JsonSchemaType schemaType) { - var identifier = schemaType.ToIdentifiers(); - return identifier[0]; + return schemaType.ToIdentifiersInternal().First(); } /// From fe19bfb729906740e7b18189227dcc9482f4df69 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 20 Feb 2025 17:21:49 +0300 Subject: [PATCH 12/15] Update src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs Co-authored-by: Vincent Biret --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 081fe4bbf..8e8c0034e 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -73,7 +73,7 @@ public static string ToSingleIdentifier(this JsonSchemaType schemaType) { throw new OpenApiException($"Schema type {schemaType} must have exactly one identifier."); } - return schemaType.ToFirstIdentifier(); + return schemaType.ToIdentifiersInternal().Single(); } #nullable restore From ee430271f0ad45971449a38f468099a599876aeb Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 20 Feb 2025 17:35:41 +0300 Subject: [PATCH 13/15] chore: clean up --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 8e8c0034e..07773856b 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -38,15 +38,18 @@ public static string[] ToIdentifiers(this JsonSchemaType schemaType) { return schemaType.ToIdentifiersInternal().ToArray(); } - private static readonly Dictionary allSchemaTypes = [ - { JsonSchemaType.Boolean, "boolean"}, + + private static readonly Dictionary allSchemaTypes = new() + { + { JsonSchemaType.Boolean, "boolean" }, { JsonSchemaType.Integer, "integer" }, { JsonSchemaType.Number, "number" }, { JsonSchemaType.String, "string" }, { JsonSchemaType.Object, "object" }, { JsonSchemaType.Array, "array" }, - { JsonSchemaType.Null, "null" }, - ]; + { JsonSchemaType.Null, "null" } + }; + private static IEnumerable ToIdentifiersInternal(this JsonSchemaType schemaType) { return allSchemaTypes.Where(kvp => schemaType.HasFlag(kvp.Key)).Select(static kvp => kvp.Value); From 844b9b1871a3c7a13e9618efa3360ce414d30096 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 20 Feb 2025 11:48:29 -0500 Subject: [PATCH 14/15] chore: removes redundant check Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index 07773856b..d50987ddb 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -72,10 +72,6 @@ internal static string ToFirstIdentifier(this JsonSchemaType schemaType) /// 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.ToIdentifiersInternal().Single(); } From 3d7aa58934d282a8d9deabfff7a278be6e6e524b Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 20 Feb 2025 11:51:11 -0500 Subject: [PATCH 15/15] chore: cleans up api surface Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs | 2 +- .../V31Tests/OpenApiSchemaTests.cs | 3 ++- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs index d50987ddb..3ae417022 100644 --- a/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs +++ b/src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs @@ -70,7 +70,7 @@ internal static string ToFirstIdentifier(this JsonSchemaType schemaType) /// /// /// - public static string ToSingleIdentifier(this JsonSchemaType schemaType) + internal static string ToSingleIdentifier(this JsonSchemaType schemaType) { return schemaType.ToIdentifiersInternal().Single(); } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs index 094a1ce2a..bb195b5cc 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs @@ -16,6 +16,7 @@ using Microsoft.OpenApi.Writers; using Xunit; using Microsoft.OpenApi.Exceptions; +using System; namespace Microsoft.OpenApi.Readers.Tests.V31Tests { @@ -561,7 +562,7 @@ public void ReturnSingleIdentifierWorks() var types = JsonSchemaType.Integer | JsonSchemaType.Null; Assert.Equal("integer", type.ToSingleIdentifier()); - Assert.Throws(() => types.ToSingleIdentifier()); + Assert.Throws(() => types.ToSingleIdentifier()); } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 68f29cf11..9491f2ace 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -194,7 +194,6 @@ namespace Microsoft.OpenApi.Extensions 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