Skip to content

Commit

Permalink
fix: removes nullable property that shouldn't be part of dom
Browse files Browse the repository at this point in the history
  • Loading branch information
baywet committed Feb 3, 2025
1 parent 974ab44 commit 4d9c17b
Show file tree
Hide file tree
Showing 18 changed files with 120 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,6 @@ private static void CopySchema(OpenApiSchema schema, OpenApiSchema newSchema)
schema.Enum ??= newSchema.Enum;
schema.ReadOnly = !schema.ReadOnly ? newSchema.ReadOnly : schema.ReadOnly;
schema.WriteOnly = !schema.WriteOnly ? newSchema.WriteOnly : schema.WriteOnly;
schema.Nullable = !schema.Nullable ? newSchema.Nullable : schema.Nullable;
schema.Deprecated = !schema.Deprecated ? newSchema.Deprecated : schema.Deprecated;
}
}
Expand Down
84 changes: 42 additions & 42 deletions src/Microsoft.OpenApi/Extensions/OpenApiTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,19 @@ public static JsonSchemaType ToJsonSchemaType(this string identifier)
[typeof(char)] = () => new() { Type = JsonSchemaType.String },

// Nullable types
[typeof(bool?)] = () => new() { Type = JsonSchemaType.Boolean, Nullable = true },
[typeof(byte?)] = () => new() { Type = JsonSchemaType.String, Format = "byte", Nullable = true },
[typeof(int?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32", Nullable = true },
[typeof(uint?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int32", Nullable = true },
[typeof(long?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64", Nullable = true },
[typeof(ulong?)] = () => new() { Type = JsonSchemaType.Integer, Format = "int64", Nullable = true },
[typeof(float?)] = () => new() { Type = JsonSchemaType.Number, Format = "float", Nullable = true },
[typeof(double?)] = () => new() { Type = JsonSchemaType.Number, Format = "double", Nullable = true },
[typeof(decimal?)] = () => new() { Type = JsonSchemaType.Number, Format = "double", Nullable = true },
[typeof(DateTime?)] = () => new() { Type = JsonSchemaType.String, Format = "date-time", Nullable = true },
[typeof(DateTimeOffset?)] = () => new() { Type = JsonSchemaType.String, Format = "date-time", Nullable = true },
[typeof(Guid?)] = () => new() { Type = JsonSchemaType.String, Format = "uuid", Nullable = true },
[typeof(char?)] = () => new() { Type = JsonSchemaType.String, Nullable = true },
[typeof(bool?)] = () => new() { Type = JsonSchemaType.Boolean | JsonSchemaType.Null },
[typeof(byte?)] = () => new() { Type = JsonSchemaType.String | JsonSchemaType.Null, Format = "byte" },
[typeof(int?)] = () => new() { Type = JsonSchemaType.Integer | JsonSchemaType.Null, Format = "int32" },
[typeof(uint?)] = () => new() { Type = JsonSchemaType.Integer | JsonSchemaType.Null, Format = "int32" },
[typeof(long?)] = () => new() { Type = JsonSchemaType.Integer | JsonSchemaType.Null, Format = "int64" },
[typeof(ulong?)] = () => new() { Type = JsonSchemaType.Integer | JsonSchemaType.Null, Format = "int64" },
[typeof(float?)] = () => new() { Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "float" },
[typeof(double?)] = () => new() { Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "double" },
[typeof(decimal?)] = () => new() { Type = JsonSchemaType.Number | JsonSchemaType.Null, Format = "double" },
[typeof(DateTime?)] = () => new() { Type = JsonSchemaType.String | JsonSchemaType.Null, Format = "date-time" },
[typeof(DateTimeOffset?)] = () => new() { Type = JsonSchemaType.String | JsonSchemaType.Null, Format = "date-time" },
[typeof(Guid?)] = () => new() { Type = JsonSchemaType.String | JsonSchemaType.Null, Format = "uuid" },
[typeof(char?)] = () => new() { Type = JsonSchemaType.String | JsonSchemaType.Null },

[typeof(Uri)] = () => new() { Type = JsonSchemaType.String, Format = "uri" }, // Uri is treated as simple string
[typeof(string)] = () => new() { Type = JsonSchemaType.String },
Expand Down Expand Up @@ -153,37 +153,37 @@ public static Type MapOpenApiPrimitiveTypeToSimpleType(this OpenApiSchema schema
throw new ArgumentNullException(nameof(schema));
}

var type = (schema.Type.ToIdentifier(), schema.Format?.ToLowerInvariant(), schema.Nullable) switch
var type = ((schema.Type.Value ^ JsonSchemaType.Null).ToIdentifier(), schema.Format?.ToLowerInvariant(), schema.Type.Value & JsonSchemaType.Null) switch

Check warning

Code scanning / CodeQL

Dereferenced variable may be null Warning

Variable
schema.Type
may be null at this access because it has a nullable type.
{
("boolean", null, false) => typeof(bool),
("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?),
("boolean", null, _) => typeof(bool),
// integer is technically not valid with format, but we must provide some compatibility
("integer" or "number", "int32", false) => typeof(int),
("integer" or "number", "int64", false) => typeof(long),
("integer", null, false) => typeof(long),
("number", "float", false) => typeof(float),
("number", "double", false) => typeof(double),
("number", "decimal", false) => typeof(decimal),
("number", null, false) => typeof(double),
("string", "byte", false) => typeof(byte),
("string", "date-time", false) => typeof(DateTimeOffset),
("string", "uuid", false) => typeof(Guid),
("string", "duration", false) => typeof(TimeSpan),
("string", "char", false) => typeof(char),
("string", null, false) => typeof(string),
("object", null, false) => typeof(object),
("string", "uri", false) => typeof(Uri),
("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?),
("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),
_ => typeof(string),
};

Expand Down
5 changes: 0 additions & 5 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,6 @@ public interface IOpenApiSchema : IOpenApiDescribedElement, IOpenApiSerializable
/// </summary>
public IList<JsonNode> Enum { get; }

/// <summary>
/// Allows sending a null value for the defined schema. Default value is false.
/// </summary>
public bool Nullable { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
Expand Down
17 changes: 5 additions & 12 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,6 @@ public class OpenApiSchema : IOpenApiReferenceable, IOpenApiExtensible, IOpenApi
/// <inheritdoc />
public IList<JsonNode> Enum { get; set; } = new List<JsonNode>();

/// <inheritdoc />
public bool Nullable { get; set; }

/// <inheritdoc />
public bool UnevaluatedProperties { get; set;}

Expand Down Expand Up @@ -236,7 +233,6 @@ internal OpenApiSchema(IOpenApiSchema schema)
Example = schema.Example != null ? JsonNodeCloneHelper.Clone(schema.Example) : null;
Examples = schema.Examples != null ? new List<JsonNode>(schema.Examples) : null;
Enum = schema.Enum != null ? new List<JsonNode>(schema.Enum) : null;
Nullable = schema.Nullable;
ExternalDocs = schema.ExternalDocs != null ? new(schema.ExternalDocs) : null;
Deprecated = schema.Deprecated;
Xml = schema.Xml != null ? new(schema.Xml) : null;
Expand Down Expand Up @@ -633,8 +629,7 @@ private void SerializeAsV2(
private void SerializeTypeProperty(JsonSchemaType? type, IOpenApiWriter writer, OpenApiSpecVersion version)
{
// check whether nullable is true for upcasting purposes
var isNullable = Nullable ||
(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} &&
Expand Down Expand Up @@ -679,10 +674,6 @@ Extensions is not null &&
var list = (from JsonSchemaType flag in jsonSchemaTypeValues
where type.Value.HasFlag(flag)
select flag).ToList();
if (Nullable && !list.Contains(JsonSchemaType.Null))
{
list.Add(JsonSchemaType.Null);
}
writer.WriteOptionalCollection(OpenApiConstants.Type, list, (w, s) => w.WriteValue(s.ToIdentifier()));
}
}
Expand Down Expand Up @@ -735,7 +726,9 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
? OpenApiConstants.NullableExtension
: OpenApiConstants.Nullable;

if (!HasMultipleTypes(schemaType ^ JsonSchemaType.Null) && (schemaType & JsonSchemaType.Null) == JsonSchemaType.Null) // checks for two values and one is null
var nullable = (schemaType & JsonSchemaType.Null) == JsonSchemaType.Null;

if (!HasMultipleTypes(schemaType ^ JsonSchemaType.Null) && nullable) // checks for two values and one is null
{
foreach (JsonSchemaType flag in jsonSchemaTypeValues)
{
Expand All @@ -746,7 +739,7 @@ private void DowncastTypeArrayToV2OrV3(JsonSchemaType schemaType, IOpenApiWriter
writer.WriteProperty(OpenApiConstants.Type, flag.ToIdentifier());
}
}
if (!Nullable)
if (!nullable || version is not OpenApiSpecVersion.OpenApi2_0)
{
writer.WriteProperty(nullableProp, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,6 @@ public string Description
/// <inheritdoc/>
public IList<JsonNode> Enum { get => Target?.Enum; }
/// <inheritdoc/>
public bool Nullable { get => Target?.Nullable ?? false; }
/// <inheritdoc/>
public bool UnevaluatedProperties { get => Target?.UnevaluatedProperties ?? false; }
/// <inheritdoc/>
public OpenApiExternalDocs ExternalDocs { get => Target?.ExternalDocs; }
Expand Down
20 changes: 18 additions & 2 deletions src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,14 @@ internal static partial class OpenApiV3Deserializer
},
{
"type",
(o, n, _) => o.Type = n.GetScalarValue().ToJsonSchemaType()
(o, n, _) => {
var type = n.GetScalarValue().ToJsonSchemaType();
// so we don't loose the value from nullable
if (o.Type.HasValue)
o.Type |= type;
else
o.Type = type;
}
},
{
"allOf",
Expand Down Expand Up @@ -139,7 +146,16 @@ internal static partial class OpenApiV3Deserializer
},
{
"nullable",
(o, n, _) => o.Nullable = bool.Parse(n.GetScalarValue())
(o, n, _) =>
{
if (bool.TryParse(n.GetScalarValue(), out var parsed) && parsed)
{
if (o.Type.HasValue)
o.Type |= JsonSchemaType.Null;
else
o.Type = JsonSchemaType.Null;
}
}
},
{
"discriminator",
Expand Down
3 changes: 1 addition & 2 deletions src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ public static void ValidateDataTypeMismatch(

var type = schema.Type.ToIdentifier();
var format = schema.Format;
var nullable = schema.Nullable;

// Before checking the type, check first if the schema allows null.
// If so and the data given is also null, this is allowed for any type.
if (nullable && valueKind is JsonValueKind.Null)
if ((schema.Type.Value & JsonSchemaType.Null) is JsonSchemaType.Null && valueKind is JsonValueKind.Null)
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,23 @@ public void RemoveAnyOfAndOneOfFromSchema()
var walker = new OpenApiWalker(powerShellFormatter);
walker.Walk(openApiDocument);

var testSchema = openApiDocument.Components?.Schemas?["TestSchema"];
var averageAudioDegradationProperty = testSchema?.Properties["averageAudioDegradation"];
var defaultPriceProperty = testSchema?.Properties["defaultPrice"];
Assert.NotNull(openApiDocument.Components);
Assert.NotNull(openApiDocument.Components.Schemas);
var testSchema = openApiDocument.Components.Schemas["TestSchema"];
var averageAudioDegradationProperty = testSchema.Properties["averageAudioDegradation"];
var defaultPriceProperty = testSchema.Properties["defaultPrice"];

// Assert
Assert.NotNull(openApiDocument.Components);
Assert.NotNull(openApiDocument.Components.Schemas);
Assert.NotNull(testSchema);
Assert.Null(averageAudioDegradationProperty?.AnyOf);
Assert.Equal(JsonSchemaType.Number, averageAudioDegradationProperty?.Type);
Assert.Equal("float", averageAudioDegradationProperty?.Format);
Assert.True(averageAudioDegradationProperty?.Nullable);
Assert.Null(defaultPriceProperty?.OneOf);
Assert.Equal(JsonSchemaType.Number, defaultPriceProperty?.Type);
Assert.Equal("double", defaultPriceProperty?.Format);
Assert.Null(averageAudioDegradationProperty.AnyOf);
Assert.Equal(JsonSchemaType.Number, averageAudioDegradationProperty.Type);
Assert.Equal("float", averageAudioDegradationProperty.Format);
Assert.Equal(JsonSchemaType.Null, averageAudioDegradationProperty.Type & JsonSchemaType.Null);
Assert.Null(defaultPriceProperty.OneOf);
Assert.Equal(JsonSchemaType.Number, defaultPriceProperty.Type);
Assert.Equal("double", defaultPriceProperty.Format);
Assert.NotNull(testSchema.AdditionalProperties);
}

Expand Down Expand Up @@ -165,7 +167,7 @@ private static OpenApiDocument GetSampleOpenApiDocument()
new OpenApiSchema() { Type = JsonSchemaType.String }
},
Format = "float",
Nullable = true
Type = JsonSchemaType.Number | JsonSchemaType.Null | JsonSchemaType.String
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,7 @@ public static OpenApiDocument CreateOpenApiDocument()
{
Schema = new OpenApiSchema()
{
AnyOf = new List<IOpenApiSchema>
{
new OpenApiSchema()
{
Type = JsonSchemaType.String
}
},
Nullable = true
Type = JsonSchemaType.String | JsonSchemaType.Null
}
}
}
Expand Down Expand Up @@ -627,9 +620,8 @@ public static OpenApiDocument CreateOpenApiDocument()
{
"description", new OpenApiSchema
{
Type = JsonSchemaType.String,
Type = JsonSchemaType.String | JsonSchemaType.Null,
Description = "Description of the NIC (e.g. Ethernet adapter, Wireless LAN adapter Local Area Connection <#>, etc.).",
Nullable = true
}
}
}
Expand Down
Loading

0 comments on commit 4d9c17b

Please sign in to comment.