Skip to content

Commit 5db8757

Browse files
authored
Merge pull request #2157 from microsoft/fix/deprecation-extension-dates
fix: an issue where deprecation extension parsing would fail
2 parents e039532 + d49c38d commit 5db8757

File tree

8 files changed

+85
-36
lines changed

8 files changed

+85
-36
lines changed

src/Microsoft.OpenApi.Hidi/Extensions/OpenApiExtensibleExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.OpenApi.Any;
22
using Microsoft.OpenApi.Interfaces;
33
using System.Collections.Generic;
4+
using System.Text.Json.Nodes;
45

56
namespace Microsoft.OpenApi.Hidi.Extensions
67
{
@@ -14,9 +15,9 @@ internal static class OpenApiExtensibleExtensions
1415
/// <returns>A <see cref="string"/> value matching the provided extensionKey. Return null when extensionKey is not found. </returns>
1516
internal static string GetExtension(this IDictionary<string, IOpenApiExtension> extensions, string extensionKey)
1617
{
17-
if (extensions.TryGetValue(extensionKey, out var value) && value is OpenApiAny castValue)
18+
if (extensions.TryGetValue(extensionKey, out var value) && value is OpenApiAny { Node: JsonValue castValue } && castValue.TryGetValue<string>(out var stringValue))
1819
{
19-
return castValue.Node.GetValue<string>();
20+
return stringValue;
2021
}
2122
return string.Empty;
2223
}

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiDeprecationExtension.cs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
using Microsoft.OpenApi.Interfaces;
1010
using Microsoft.OpenApi.Writers;
1111
using System.Text.Json.Nodes;
12+
using System.Text.Json;
13+
using System.Globalization;
1214

1315
namespace Microsoft.OpenApi.MicrosoftExtensions;
1416

@@ -71,6 +73,35 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
7173
writer.WriteEndObject();
7274
}
7375
}
76+
private static readonly DateTimeStyles datesStyle = DateTimeStyles.AssumeUniversal | DateTimeStyles.RoundtripKind;
77+
private static DateTimeOffset? GetDateTimeOffsetValue(string propertyName, JsonObject rawObject)
78+
{
79+
if (!rawObject.TryGetPropertyValue(propertyName.ToFirstCharacterLowerCase(), out var jsonNode) ||
80+
jsonNode is not JsonValue jsonValue ||
81+
jsonNode.GetValueKind() is not JsonValueKind.String)
82+
return null;
83+
84+
if (jsonValue.TryGetValue<string>(out var strValue) &&
85+
DateTimeOffset.TryParse(strValue, CultureInfo.InvariantCulture, datesStyle, out var parsedValue))
86+
{
87+
return parsedValue;
88+
}
89+
if (jsonValue.TryGetValue<DateTimeOffset>(out var returnedDto))
90+
{
91+
return returnedDto;
92+
}
93+
if (jsonValue.TryGetValue<DateTime>(out var returnedDt))
94+
{
95+
return new DateTimeOffset(returnedDt, TimeSpan.FromHours(0));
96+
}
97+
#if NET6_0_OR_GREATER
98+
if (jsonValue.TryGetValue<DateOnly>(out var returnedDo))
99+
{
100+
return new(returnedDo.Year, returnedDo.Month, returnedDo.Day, 0, 0, 0, TimeSpan.FromHours(0));
101+
}
102+
#endif
103+
return null;
104+
}
74105
/// <summary>
75106
/// Parses the <see cref="OpenApiAny"/> to <see cref="OpenApiDeprecationExtension"/>.
76107
/// </summary>
@@ -80,15 +111,15 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
80111
public static OpenApiDeprecationExtension Parse(JsonNode source)
81112
{
82113
if (source is not JsonObject rawObject) return null;
83-
var extension = new OpenApiDeprecationExtension();
84-
if (rawObject.TryGetPropertyValue(nameof(RemovalDate).ToFirstCharacterLowerCase(), out var removalDate) && removalDate is JsonNode removalDateValue)
85-
extension.RemovalDate = removalDateValue.GetValue<DateTimeOffset>();
86-
if (rawObject.TryGetPropertyValue(nameof(Date).ToFirstCharacterLowerCase(), out var date) && date is JsonNode dateValue)
87-
extension.Date = dateValue.GetValue<DateTimeOffset>();
88-
if (rawObject.TryGetPropertyValue(nameof(Version).ToFirstCharacterLowerCase(), out var version) && version is JsonNode versionValue)
89-
extension.Version = versionValue.GetValue<string>();
90-
if (rawObject.TryGetPropertyValue(nameof(Description).ToFirstCharacterLowerCase(), out var description) && description is JsonNode descriptionValue)
91-
extension.Description = descriptionValue.GetValue<string>();
114+
var extension = new OpenApiDeprecationExtension
115+
{
116+
RemovalDate = GetDateTimeOffsetValue(nameof(RemovalDate), rawObject),
117+
Date = GetDateTimeOffsetValue(nameof(Date), rawObject)
118+
};
119+
if (rawObject.TryGetPropertyValue(nameof(Version).ToFirstCharacterLowerCase(), out var version) && version is JsonValue versionValue && versionValue.TryGetValue<string>(out var versionStr))
120+
extension.Version = versionStr;
121+
if (rawObject.TryGetPropertyValue(nameof(Description).ToFirstCharacterLowerCase(), out var description) && description is JsonValue descriptionValue && descriptionValue.TryGetValue<string>(out var descriptionStr))
122+
extension.Description = descriptionStr;
92123
return extension;
93124
}
94125
}

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiEnumFlagsExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ public static OpenApiEnumFlagsExtension Parse(JsonNode source)
4747
{
4848
if (source is not JsonObject rawObject) throw new ArgumentOutOfRangeException(nameof(source));
4949
var extension = new OpenApiEnumFlagsExtension();
50-
if (rawObject.TryGetPropertyValue(nameof(IsFlags).ToFirstCharacterLowerCase(), out var flagsValue) && flagsValue is JsonNode isFlags)
50+
if (rawObject.TryGetPropertyValue(nameof(IsFlags).ToFirstCharacterLowerCase(), out var flagsValue) && flagsValue is JsonValue isFlags && isFlags.TryGetValue<bool>(out var isFlagsValue))
5151
{
52-
extension.IsFlags = isFlags.GetValue<bool>();
52+
extension.IsFlags = isFlagsValue;
5353
}
5454
return extension;
5555
}

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiEnumValuesDescriptionExtension.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ public EnumDescription()
9696
public EnumDescription(JsonObject source)
9797
{
9898
if (source is null) throw new ArgumentNullException(nameof(source));
99-
if (source.TryGetPropertyValue(nameof(Value).ToFirstCharacterLowerCase(), out var rawValue) && rawValue is JsonNode value)
100-
if (value.GetValueKind() == JsonValueKind.Number)
101-
Value = value.GetValue<decimal>().ToString(CultureInfo.InvariantCulture);
102-
else
103-
Value = value.GetValue<string>();
104-
if (source.TryGetPropertyValue(nameof(Description).ToFirstCharacterLowerCase(), out var rawDescription) && rawDescription is JsonNode description)
105-
Description = description.GetValue<string>();
106-
if (source.TryGetPropertyValue(nameof(Name).ToFirstCharacterLowerCase(), out var rawName) && rawName is JsonNode name)
107-
Name = name.GetValue<string>();
99+
if (source.TryGetPropertyValue(nameof(Value).ToFirstCharacterLowerCase(), out var rawValue) && rawValue is JsonValue value)
100+
if (value.GetValueKind() == JsonValueKind.Number && value.TryGetValue<decimal>(out var decimalValue))
101+
Value = decimalValue.ToString(CultureInfo.InvariantCulture);
102+
else if (value.TryGetValue<string>(out var stringValue))
103+
Value = stringValue;
104+
if (source.TryGetPropertyValue(nameof(Description).ToFirstCharacterLowerCase(), out var rawDescription) && rawDescription is JsonValue description && description.TryGetValue<string>(out var stringValueDescription))
105+
Description = stringValueDescription;
106+
if (source.TryGetPropertyValue(nameof(Name).ToFirstCharacterLowerCase(), out var rawName) && rawName is JsonValue name && name.TryGetValue<string>(out var stringValueName))
107+
Name = stringValueName;
108108
}
109109
/// <summary>
110110
/// The description for the enum symbol

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiPagingExtension.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,19 @@ public static OpenApiPagingExtension Parse(JsonNode source)
7575
{
7676
if (source is not JsonObject rawObject) return null;
7777
var extension = new OpenApiPagingExtension();
78-
if (rawObject.TryGetPropertyValue(nameof(NextLinkName).ToFirstCharacterLowerCase(), out var nextLinkName) && nextLinkName is JsonNode nextLinkNameStr)
78+
if (rawObject.TryGetPropertyValue(nameof(NextLinkName).ToFirstCharacterLowerCase(), out var nextLinkName) && nextLinkName is JsonValue nextLinkNameValue && nextLinkNameValue.TryGetValue<string>(out var nextLinkNameStr))
7979
{
80-
extension.NextLinkName = nextLinkNameStr.GetValue<string>();
80+
extension.NextLinkName = nextLinkNameStr;
8181
}
8282

83-
if (rawObject.TryGetPropertyValue(nameof(OperationName).ToFirstCharacterLowerCase(), out var opName) && opName is JsonNode opNameStr)
83+
if (rawObject.TryGetPropertyValue(nameof(OperationName).ToFirstCharacterLowerCase(), out var opName) && opName is JsonValue opNameValue && opNameValue.TryGetValue<string>(out var opNameStr))
8484
{
85-
extension.OperationName = opNameStr.GetValue<string>();
85+
extension.OperationName = opNameStr;
8686
}
8787

88-
if (rawObject.TryGetPropertyValue(nameof(ItemName).ToFirstCharacterLowerCase(), out var itemName) && itemName is JsonNode itemNameStr)
88+
if (rawObject.TryGetPropertyValue(nameof(ItemName).ToFirstCharacterLowerCase(), out var itemName) && itemName is JsonValue itemNameValue && itemNameValue.TryGetValue<string>(out var itemNameStr))
8989
{
90-
extension.ItemName = itemNameStr.GetValue<string>();
90+
extension.ItemName = itemNameStr;
9191
}
9292

9393
return extension;

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiPrimaryErrorMessageExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
4040
/// <returns>The <see cref="OpenApiPrimaryErrorMessageExtension"/>.</returns>
4141
public static OpenApiPrimaryErrorMessageExtension Parse(JsonNode source)
4242
{
43-
if (source is not JsonNode rawObject) return null;
43+
if (source is not JsonValue rawObject) return null;
4444
return new()
4545
{
46-
IsPrimaryErrorMessage = rawObject.GetValue<bool>()
46+
IsPrimaryErrorMessage = rawObject.TryGetValue<bool>(out var value) && value
4747
};
4848
}
4949
}

src/Microsoft.OpenApi/MicrosoftExtensions/OpenApiReservedParameterExtension.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ public bool? IsReserved
4242
/// <returns></returns>
4343
public static OpenApiReservedParameterExtension Parse(JsonNode source)
4444
{
45-
if (source is not JsonNode rawBoolean) return null;
45+
if (source is not JsonValue rawBoolean) return null;
4646
return new()
4747
{
48-
IsReserved = rawBoolean.GetValue<bool>()
48+
IsReserved = rawBoolean.TryGetValue<bool>(out var value) && value
4949
};
5050
}
5151
}

test/Microsoft.OpenApi.Tests/MicrosoftExtensions/OpenApiDeprecationExtensionTests.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ public void Parses()
7575
{
7676
var oaiValue = new JsonObject
7777
{
78-
{ "date", new OpenApiAny(new DateTimeOffset(2023,05,04, 16, 0, 0, 0, 0, new(4, 0, 0))).Node},
79-
{ "removalDate", new OpenApiAny(new DateTimeOffset(2023,05,04, 16, 0, 0, 0, 0, new(4, 0, 0))).Node},
80-
{ "version", new OpenApiAny("v1.0").Node},
81-
{ "description", new OpenApiAny("removing").Node}
78+
{ "date", new DateTimeOffset(2023,05,04, 16, 0, 0, 0, 0, new(4, 0, 0))},
79+
{ "removalDate", new DateTimeOffset(2023,05,04, 16, 0, 0, 0, 0, new(4, 0, 0))},
80+
{ "version", "v1.0"},
81+
{ "description", "removing"}
8282
};
8383
var value = OpenApiDeprecationExtension.Parse(oaiValue);
8484
Assert.NotNull(value);
@@ -88,6 +88,23 @@ public void Parses()
8888
Assert.Equal(new DateTimeOffset(2023, 05, 04, 16, 0, 0, 0, 0, new(4, 0, 0)), value.RemovalDate);
8989
}
9090
[Fact]
91+
public void ParsesStringValues()
92+
{
93+
var oaiValue = new JsonObject
94+
{
95+
{ "date", "2023-05-04T16:00:00Z"},
96+
{ "removalDate", "2023-05-04"},
97+
{ "version", "v1.0"},
98+
{ "description", "removing"}
99+
};
100+
var value = OpenApiDeprecationExtension.Parse(oaiValue);
101+
Assert.NotNull(value);
102+
Assert.Equal("v1.0", value.Version);
103+
Assert.Equal("removing", value.Description);
104+
Assert.Equal(new DateTimeOffset(2023, 05, 04, 16, 0, 0, 0, 0, new(0, 0, 0)), value.Date);
105+
Assert.Equal(new DateTimeOffset(2023, 05, 04, 0, 0, 0, 0, 0, new(0, 0, 0)), value.RemovalDate);
106+
}
107+
[Fact]
91108
public void Serializes()
92109
{
93110
var value = new OpenApiDeprecationExtension

0 commit comments

Comments
 (0)