Skip to content

Commit

Permalink
fix: deduplicates exclusive min/max properties in the object model
Browse files Browse the repository at this point in the history
Signed-off-by: Vincent Biret <vibiret@microsoft.com>
  • Loading branch information
baywet committed Feb 26, 2025
1 parent 4ee1d8b commit 0d5b471
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 121 deletions.
14 changes: 2 additions & 12 deletions src/Microsoft.OpenApi/Models/Interfaces/IOpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public interface IOpenApiSchema : IOpenApiDescribedElement, IOpenApiSerializable
/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
public decimal? V31ExclusiveMaximum { get; }
public decimal? ExclusiveMaximum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
public decimal? V31ExclusiveMinimum { get; }
public decimal? ExclusiveMinimum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
Expand Down Expand Up @@ -90,21 +90,11 @@ public interface IOpenApiSchema : IOpenApiDescribedElement, IOpenApiSerializable
/// </summary>
public decimal? Maximum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
public bool? ExclusiveMaximum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
public decimal? Minimum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
public bool? ExclusiveMinimum { get; }

/// <summary>
/// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00
/// </summary>
Expand Down
170 changes: 137 additions & 33 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,63 @@ public class OpenApiSchema : IOpenApiReferenceable, IOpenApiExtensible, IOpenApi
/// <inheritdoc />
public IDictionary<string, IOpenApiSchema> Definitions { get; set; }

private decimal? _exclusiveMaximum;
/// <inheritdoc />
public decimal? V31ExclusiveMaximum { get; set; }
public decimal? ExclusiveMaximum
{
get
{
if (_exclusiveMaximum.HasValue)
{
return _exclusiveMaximum;
}
if (IsExclusiveMaximum == true && _maximum.HasValue)
{
return _maximum;
}
return null;
}
set
{
_exclusiveMaximum = value;
IsExclusiveMaximum = value != null;
}
}

/// <summary>
/// Compatibility property for OpenAPI 3.0 or earlier serialization of the exclusive maximum value.
/// </summary>
/// DO NOT CHANGE THE VISIBILITY OF THIS PROPERTY TO PUBLIC
internal bool? IsExclusiveMaximum { get; set; }

private decimal? _exclusiveMinimum;
/// <inheritdoc />
public decimal? V31ExclusiveMinimum { get; set; }
public decimal? ExclusiveMinimum
{
get
{
if (_exclusiveMinimum.HasValue)
{
return _exclusiveMinimum;
}
if (IsExclusiveMinimum == true && _minimum.HasValue)
{
return _minimum;
}
return null;
}
set
{
_exclusiveMinimum = value;
IsExclusiveMinimum = value != null;
}
}

/// <summary>
/// Compatibility property for OpenAPI 3.0 or earlier serialization of the exclusive minimum value.
/// </summary>
/// DO NOT CHANGE THE VISIBILITY OF THIS PROPERTY TO PUBLIC
internal bool? IsExclusiveMinimum { get; set; }

/// <inheritdoc />
public bool UnEvaluatedProperties { get; set; }
Expand All @@ -65,17 +117,42 @@ public class OpenApiSchema : IOpenApiReferenceable, IOpenApiExtensible, IOpenApi
/// <inheritdoc />
public string Description { get; set; }

private decimal? _maximum;
/// <inheritdoc />
public decimal? Maximum { get; set; }

/// <inheritdoc />
public bool? ExclusiveMaximum { get; set; }
public decimal? Maximum
{
get
{
if (IsExclusiveMaximum == true)
{
return null;
}
return _maximum;
}
set
{
_maximum = value;
}
}

/// <inheritdoc />
public decimal? Minimum { get; set; }
private decimal? _minimum;

/// <inheritdoc />
public bool? ExclusiveMinimum { get; set; }
public decimal? Minimum
{
get
{
if (IsExclusiveMinimum == true)
{
return null;
}
return _minimum;
}
set
{
_minimum = value;
}
}

/// <inheritdoc />
public int? MaxLength { get; set; }
Expand Down Expand Up @@ -201,15 +278,18 @@ internal OpenApiSchema(IOpenApiSchema schema)
DynamicRef = schema.DynamicRef ?? DynamicRef;
Definitions = schema.Definitions != null ? new Dictionary<string, IOpenApiSchema>(schema.Definitions) : null;
UnevaluatedProperties = schema.UnevaluatedProperties;
V31ExclusiveMaximum = schema.V31ExclusiveMaximum ?? V31ExclusiveMaximum;
V31ExclusiveMinimum = schema.V31ExclusiveMinimum ?? V31ExclusiveMinimum;
ExclusiveMaximum = schema.ExclusiveMaximum ?? ExclusiveMaximum;
ExclusiveMinimum = schema.ExclusiveMinimum ?? ExclusiveMinimum;
if (schema is OpenApiSchema eMSchema)
{
IsExclusiveMaximum = eMSchema.IsExclusiveMaximum;
IsExclusiveMinimum = eMSchema.IsExclusiveMinimum;
}
Type = schema.Type ?? Type;
Format = schema.Format ?? Format;
Description = schema.Description ?? Description;
Maximum = schema.Maximum ?? Maximum;
ExclusiveMaximum = schema.ExclusiveMaximum ?? ExclusiveMaximum;
Minimum = schema.Minimum ?? Minimum;
ExclusiveMinimum = schema.ExclusiveMinimum ?? ExclusiveMinimum;
MaxLength = schema.MaxLength ?? MaxLength;
MinLength = schema.MinLength ?? MinLength;
Pattern = schema.Pattern ?? Pattern;
Expand Down Expand Up @@ -257,6 +337,44 @@ public void SerializeAsV3(IOpenApiWriter writer)
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
}

private static void SerializeBounds(IOpenApiWriter writer, OpenApiSpecVersion version, string propertyName, string exclusivePropertyName, string isExclusivePropertyName, decimal? value, decimal? exclusiveValue, bool? isExclusiveValue)

Check warning on line 340 in src/Microsoft.OpenApi/Models/OpenApiSchema.cs

View workflow job for this annotation

GitHub Actions / Build

Method has 8 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)

Check warning on line 340 in src/Microsoft.OpenApi/Models/OpenApiSchema.cs

View workflow job for this annotation

GitHub Actions / Build

Method has 8 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)
{
if (version >= OpenApiSpecVersion.OpenApi3_1)
{
if (exclusiveValue.HasValue)
{
// was explicitly set in the document or object model
writer.WriteProperty(exclusivePropertyName, exclusiveValue.Value);
}
else if (isExclusiveValue == true && value.HasValue)
{
// came from parsing an old document
writer.WriteProperty(exclusivePropertyName, value);
}
else if (value.HasValue)
{
// was explicitly set in the document or object model
writer.WriteProperty(propertyName, value);
}
}
else
{
if (exclusiveValue.HasValue)
{
// was explicitly set in a new document being downcast or object model
writer.WriteProperty(propertyName, exclusiveValue.Value);
writer.WriteProperty(isExclusivePropertyName, true);
}
else if (value.HasValue)
{
// came from parsing an old document, we're just mirroring the information
writer.WriteProperty(propertyName, value);
if (isExclusiveValue.HasValue)
writer.WriteProperty(isExclusivePropertyName, isExclusiveValue.Value);
}
}
}

private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
Action<IOpenApiWriter, IOpenApiSerializable> callback)
{
Expand All @@ -274,16 +392,12 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
writer.WriteProperty(OpenApiConstants.MultipleOf, MultipleOf);

// maximum
writer.WriteProperty(OpenApiConstants.Maximum, Maximum);

// exclusiveMaximum
writer.WriteProperty(OpenApiConstants.ExclusiveMaximum, ExclusiveMaximum);
SerializeBounds(writer, version, OpenApiConstants.Maximum, OpenApiConstants.ExclusiveMaximum, OpenApiConstants.V31ExclusiveMaximum, Maximum, ExclusiveMaximum, IsExclusiveMaximum);

// minimum
writer.WriteProperty(OpenApiConstants.Minimum, Minimum);

// exclusiveMinimum
writer.WriteProperty(OpenApiConstants.ExclusiveMinimum, ExclusiveMinimum);
SerializeBounds(writer, version, OpenApiConstants.Minimum, OpenApiConstants.ExclusiveMinimum, OpenApiConstants.V31ExclusiveMinimum, Minimum, ExclusiveMinimum, IsExclusiveMinimum);

// maxLength
writer.WriteProperty(OpenApiConstants.MaxLength, MaxLength);
Expand Down Expand Up @@ -407,8 +521,6 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer)
writer.WriteOptionalMap(OpenApiConstants.Defs, Definitions, (w, s) => s.SerializeAsV31(w));
writer.WriteProperty(OpenApiConstants.DynamicRef, DynamicRef);
writer.WriteProperty(OpenApiConstants.DynamicAnchor, DynamicAnchor);
writer.WriteProperty(OpenApiConstants.V31ExclusiveMaximum, V31ExclusiveMaximum);
writer.WriteProperty(OpenApiConstants.V31ExclusiveMinimum, V31ExclusiveMinimum);
writer.WriteProperty(OpenApiConstants.UnevaluatedProperties, UnevaluatedProperties, false);
writer.WriteOptionalCollection(OpenApiConstants.Examples, Examples, (nodeWriter, s) => nodeWriter.WriteAny(s));
writer.WriteOptionalMap(OpenApiConstants.PatternProperties, PatternProperties, (w, s) => s.SerializeAsV31(w));
Expand Down Expand Up @@ -438,16 +550,12 @@ internal void WriteAsItemsProperties(IOpenApiWriter writer)
writer.WriteOptionalObject(OpenApiConstants.Default, Default, (w, d) => w.WriteAny(d));

// maximum
writer.WriteProperty(OpenApiConstants.Maximum, Maximum);

// exclusiveMaximum
writer.WriteProperty(OpenApiConstants.ExclusiveMaximum, ExclusiveMaximum);
SerializeBounds(writer, OpenApiSpecVersion.OpenApi2_0, OpenApiConstants.Maximum, OpenApiConstants.ExclusiveMaximum, OpenApiConstants.V31ExclusiveMaximum, Maximum, ExclusiveMaximum, IsExclusiveMaximum);

// minimum
writer.WriteProperty(OpenApiConstants.Minimum, Minimum);

// exclusiveMinimum
writer.WriteProperty(OpenApiConstants.ExclusiveMinimum, ExclusiveMinimum);
SerializeBounds(writer, OpenApiSpecVersion.OpenApi2_0, OpenApiConstants.Minimum, OpenApiConstants.ExclusiveMinimum, OpenApiConstants.V31ExclusiveMinimum, Minimum, ExclusiveMinimum, IsExclusiveMinimum);

// maxLength
writer.WriteProperty(OpenApiConstants.MaxLength, MaxLength);
Expand Down Expand Up @@ -522,16 +630,12 @@ private void SerializeAsV2(
writer.WriteProperty(OpenApiConstants.MultipleOf, MultipleOf);

// maximum
writer.WriteProperty(OpenApiConstants.Maximum, Maximum);

// exclusiveMaximum
writer.WriteProperty(OpenApiConstants.ExclusiveMaximum, ExclusiveMaximum);
SerializeBounds(writer, OpenApiSpecVersion.OpenApi2_0, OpenApiConstants.Maximum, OpenApiConstants.ExclusiveMaximum, OpenApiConstants.V31ExclusiveMaximum, Maximum, ExclusiveMaximum, IsExclusiveMaximum);

// minimum
writer.WriteProperty(OpenApiConstants.Minimum, Minimum);

// exclusiveMinimum
writer.WriteProperty(OpenApiConstants.ExclusiveMinimum, ExclusiveMinimum);
SerializeBounds(writer, OpenApiSpecVersion.OpenApi2_0, OpenApiConstants.Minimum, OpenApiConstants.ExclusiveMinimum, OpenApiConstants.V31ExclusiveMinimum, Minimum, ExclusiveMinimum, IsExclusiveMinimum);

// maxLength
writer.WriteProperty(OpenApiConstants.MaxLength, MaxLength);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ public string Description
/// <inheritdoc/>
public IDictionary<string, IOpenApiSchema> Definitions { get => Target?.Definitions; }
/// <inheritdoc/>
public decimal? V31ExclusiveMaximum { get => Target?.V31ExclusiveMaximum; }
public decimal? ExclusiveMaximum { get => Target?.ExclusiveMaximum; }
/// <inheritdoc/>
public decimal? V31ExclusiveMinimum { get => Target?.V31ExclusiveMinimum; }
public decimal? ExclusiveMinimum { get => Target?.ExclusiveMinimum; }
/// <inheritdoc/>
public bool UnEvaluatedProperties { get => Target?.UnEvaluatedProperties ?? false; }
/// <inheritdoc/>
Expand All @@ -80,12 +80,8 @@ public string Description
/// <inheritdoc/>
public decimal? Maximum { get => Target?.Maximum; }
/// <inheritdoc/>
public bool? ExclusiveMaximum { get => Target?.ExclusiveMaximum; }
/// <inheritdoc/>
public decimal? Minimum { get => Target?.Minimum; }
/// <inheritdoc/>
public bool? ExclusiveMinimum { get => Target?.ExclusiveMinimum; }
/// <inheritdoc/>
public int? MaxLength { get => Target?.MaxLength; }
/// <inheritdoc/>
public int? MinLength { get => Target?.MinLength; }
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiHeaderDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ internal static partial class OpenApiV2Deserializer
},
{
"exclusiveMaximum",
(o, n, _) => GetOrCreateSchema(o).ExclusiveMaximum = bool.Parse(n.GetScalarValue())
(o, n, _) => GetOrCreateSchema(o).IsExclusiveMaximum = bool.Parse(n.GetScalarValue())
},
{
"minimum",
(o, n, _) => GetOrCreateSchema(o).Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue)
},
{
"exclusiveMinimum",
(o, n, _) => GetOrCreateSchema(o).ExclusiveMinimum = bool.Parse(n.GetScalarValue())
(o, n, _) => GetOrCreateSchema(o).IsExclusiveMinimum = bool.Parse(n.GetScalarValue())
},
{
"maxLength",
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Reader/V2/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ internal static partial class OpenApiV2Deserializer
},
{
"exclusiveMaximum",
(o, n, _) => o.ExclusiveMaximum = bool.Parse(n.GetScalarValue())
(o, n, _) => o.IsExclusiveMaximum = bool.Parse(n.GetScalarValue())
},
{
"minimum",
(o, n, _) => o.Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue)
},
{
"exclusiveMinimum",
(o, n, _) => o.ExclusiveMinimum = bool.Parse(n.GetScalarValue())
(o, n, _) => o.IsExclusiveMinimum = bool.Parse(n.GetScalarValue())
},
{
"maxLength",
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Reader/V3/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ internal static partial class OpenApiV3Deserializer
},
{
"exclusiveMaximum",
(o, n, _) => o.ExclusiveMaximum = bool.Parse(n.GetScalarValue())
(o, n, _) => o.IsExclusiveMaximum = bool.Parse(n.GetScalarValue())
},
{
"minimum",
(o, n, _) => o.Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue)
},
{
"exclusiveMinimum",
(o, n, _) => o.ExclusiveMinimum = bool.Parse(n.GetScalarValue())
(o, n, _) => o.IsExclusiveMinimum = bool.Parse(n.GetScalarValue())
},
{
"maxLength",
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ internal static partial class OpenApiV31Deserializer
},
{
"exclusiveMaximum",
(o, n, _) => o.V31ExclusiveMaximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue)
(o, n, _) => o.ExclusiveMaximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue)
},
{
"minimum",
(o, n, _) => o.Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue)
},
{
"exclusiveMinimum",
(o, n, _) => o.V31ExclusiveMinimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue)
(o, n, _) => o.ExclusiveMinimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue)
},
{
"maxLength",
Expand Down
Loading

0 comments on commit 0d5b471

Please sign in to comment.