Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cultureinfo to reader/writer settings. #152

Merged
merged 15 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class AsyncApiUnsupportedSpecVersionException : Exception
/// </summary>
/// <param name="specificationVersion">Version that caused this exception to be thrown.</param>
public AsyncApiUnsupportedSpecVersionException(string specificationVersion)
: base(string.Format(CultureInfo.InvariantCulture, MessagePattern, specificationVersion))
: base(string.Format(Configuration.CultureInfo, MessagePattern, specificationVersion))
{
this.SpecificationVersion = specificationVersion;
}
Expand All @@ -30,7 +30,7 @@ public AsyncApiUnsupportedSpecVersionException(string specificationVersion)
/// <param name="specificationVersion">Version that caused this exception to be thrown.</param>
/// <param name="innerException">Inner exception that caused this exception to be thrown.</param>
public AsyncApiUnsupportedSpecVersionException(string specificationVersion, Exception innerException)
: base(string.Format(CultureInfo.InvariantCulture, MessagePattern, specificationVersion), innerException)
: base(string.Format(Configuration.CultureInfo, MessagePattern, specificationVersion), innerException)
{
this.SpecificationVersion = specificationVersion;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LEGO.AsyncAPI.Readers/JsonHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class JsonHelper
public static string GetScalarValue(this JsonNode node)
{
var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value");
return Convert.ToString(scalarNode.GetValue<object>(), CultureInfo.InvariantCulture);
return Convert.ToString(scalarNode.GetValue<object>(), Configuration.CultureInfo);
}

public static JsonNode ParseJsonString(string jsonString)
Expand Down
18 changes: 9 additions & 9 deletions src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,14 @@ public class JsonSchemaDeserializer
"multipleOf",
(a, n) =>
{
a.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
a.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, Configuration.CultureInfo);
}
},
{
"maximum",
(a, n) =>
{
a.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
a.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, Configuration.CultureInfo);
}
},
{
Expand All @@ -66,37 +66,37 @@ public class JsonSchemaDeserializer
"minimum",
(a, n) =>
{
a.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture);
a.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, Configuration.CultureInfo);
}
},
{
"exclusiveMinimum", (a, n) => { a.ExclusiveMinimum = bool.Parse(n.GetScalarValue()); }
},
{
"maxLength", (a, n) => { a.MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
"maxLength", (a, n) => { a.MaxLength = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"minLength", (a, n) => { a.MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
"minLength", (a, n) => { a.MinLength = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"pattern", (a, n) => { a.Pattern = n.GetScalarValue(); }
},
{
"maxItems", (a, n) => { a.MaxItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
"maxItems", (a, n) => { a.MaxItems = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"minItems", (a, n) => { a.MinItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
"minItems", (a, n) => { a.MinItems = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"uniqueItems", (a, n) => { a.UniqueItems = bool.Parse(n.GetScalarValue()); }
},
{
"maxProperties",
(a, n) => { a.MaxProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
(a, n) => { a.MaxProperties = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"minProperties",
(a, n) => { a.MinProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); }
(a, n) => { a.MinProperties = int.Parse(n.GetScalarValue(), Configuration.CultureInfo); }
},
{
"enum", (a, n) => { a.Enum = n.CreateListOfAny(); }
Expand Down
6 changes: 3 additions & 3 deletions src/LEGO.AsyncAPI.Readers/YamlConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,16 @@ public static JsonNode ToJsonNode(this YamlNode yaml)
};
}

private static JsonValue ToJsonValue(this YamlScalarNode yaml)
public static JsonValue ToJsonValue(this YamlScalarNode yaml)
{
switch (yaml.Style)
{
case ScalarStyle.Plain:
return decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d)
return decimal.TryParse(yaml.Value, NumberStyles.Float, Configuration.CultureInfo, out var d)
? JsonValue.Create(d)
: bool.TryParse(yaml.Value, out var b)
? JsonValue.Create(b)
: JsonValue.Create(yaml.Value)!;
: JsonValue.Create(yaml.Value) !;
case ScalarStyle.SingleQuoted:
case ScalarStyle.DoubleQuoted:
case ScalarStyle.Literal:
Expand Down
28 changes: 28 additions & 0 deletions src/LEGO.AsyncAPI/Configuration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI
{
using System.Globalization;

/// <summary>
/// Contains configuration settings for AsyncAPI used across the board.
/// </summary>
public static class Configuration
{
static Configuration()
{
DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffzzz";
CultureInfo = CultureInfo.InvariantCulture;
}

/// <summary>
/// Gets the format used for reading and writing date time structures.
/// </summary>
public static string DateTimeFormat { get; }

/// <summary>
/// Gets the culture info used for strings.
/// </summary>
public static CultureInfo CultureInfo { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static void Serialize<T>(
throw new ArgumentNullException(nameof(stream));
}

var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture);
var streamWriter = new FormattingStreamWriter(stream, Configuration.CultureInfo);

IAsyncApiWriter writer = format switch
{
Expand Down
4 changes: 2 additions & 2 deletions src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ public virtual void WriteValue(long value)
/// <param name="value">The DateTime value.</param>
public virtual void WriteValue(DateTime value)
{
this.WriteValue(value.ToString("o"));
this.WriteValue(value.ToString(Configuration.DateTimeFormat, Configuration.CultureInfo));
}

/// <summary>
Expand All @@ -186,7 +186,7 @@ public virtual void WriteValue(DateTime value)
/// <param name="value">The DateTimeOffset value.</param>
public virtual void WriteValue(DateTimeOffset value)
{
this.WriteValue(value.ToString("o"));
this.WriteValue(value.ToString(Configuration.DateTimeFormat, Configuration.CultureInfo));
}

/// <summary>
Expand Down
47 changes: 32 additions & 15 deletions src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ namespace LEGO.AsyncAPI.Writers
using System;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

public static class SpecialCharacterStringExtensions
{
private static readonly Regex numberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled);

// Plain style strings cannot start with indicators.
// http://www.yaml.org/spec/1.2/spec.html#indicator//
private static readonly char[] yamlIndicators =
Expand Down Expand Up @@ -100,24 +103,32 @@ public static class SpecialCharacterStringExtensions
/// Escapes all special characters and put the string in quotes if necessary to
/// get a YAML-compatible string.
/// </summary>
/// <param name="input">The string to turn into yaml</param>
/// <returns>The string as yaml.</returns>
internal static string GetYamlCompatibleString(this string input)
{
// If string is an empty string, wrap it in quote to ensure it is not recognized as null.
if (input == "")
if (input == null)
{
return "''";
}

// If string is the word null, wrap it in quote to ensure it is not recognized as empty scalar null.
if (input == "null")
{
return "'null'";
return "null";
}

// If string is the letter ~, wrap it in quote to ensure it is not recognized as empty scalar null.
if (input == "~")
switch (input.ToLower())
{
return "'~'";
case "":
return "''";

case "~":
// Example 2.20. Floating Point
case "-.inf":
case ".inf":
case ".nan":
// Example 2.21. Miscellaneous
case "null":

// Booleans
case "true":
case "false":
return $"'{input}'";
}

// If string includes a control character, wrapping in double quote is required.
Expand Down Expand Up @@ -184,10 +195,16 @@ internal static string GetYamlCompatibleString(this string input)
}

// If string can be mistaken as a number, a boolean, or a timestamp,
// wrap it in quote to indicate that this is indeed a string, not a number, a boolean, or a timestamp
if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) ||
// wrap it in quot number, a boolean, or a timestamp
if (decimal.TryParse(input, NumberStyles.Float, Configuration.CultureInfo, out var _) ||
bool.TryParse(input, out var _) ||
DateTime.TryParse(input, out var _))
DateTime.TryParseExact(input, Configuration.DateTimeFormat, Configuration.CultureInfo, DateTimeStyles.RoundtripKind, out var _))
{
return $"'{input}'";
}

// Handle numbers
if (numberRegex.IsMatch(input))
{
return $"'{input}'";
}
Expand Down
29 changes: 12 additions & 17 deletions test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace LEGO.AsyncAPI.Tests
using System.Globalization;
using System.IO;
using System.Linq;
using FluentAssertions;
using LEGO.AsyncAPI.Bindings;
using LEGO.AsyncAPI.Bindings.Http;
using LEGO.AsyncAPI.Bindings.Kafka;
Expand All @@ -22,14 +23,14 @@ public class ExtensionClass
public string Key { get; set; }
public long OtherKey { get; set; }
}
public class AsyncApiDocumentV2Tests
public class AsyncApiDocumentV2Tests : TestBase
{
[Test]
public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes()
{
// Arrange
var expected =
@"asyncapi: '2.6.0'
@"asyncapi: 2.6.0
info:
title: Streetlights Kafka API
version: 1.0.0
Expand Down Expand Up @@ -693,18 +694,16 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes()

// Act
var actual = asyncApiDocument.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0);
actual = actual.MakeLineBreaksEnvironmentNeutral();
expected = expected.MakeLineBreaksEnvironmentNeutral();

// Assert
Assert.AreEqual(expected, actual);
actual.Should()
.BePlatformAgnosticEquivalentTo(expected);
}

[Test]
public void SerializeV2_WithFullSpec_Serializes()
{
var expected =
@"asyncapi: '2.6.0'
@"asyncapi: 2.6.0
info:
title: apiTitle
version: apiVersion
Expand Down Expand Up @@ -1117,18 +1116,16 @@ public void SerializeV2_WithFullSpec_Serializes()
},
};

var outputString = new StringWriter(CultureInfo.InvariantCulture);
var outputString = new StringWriter(Configuration.CultureInfo);
var writer = new AsyncApiYamlWriter(outputString);

// Act
document.SerializeV2(writer);
var actual = outputString.GetStringBuilder().ToString();

actual = actual.MakeLineBreaksEnvironmentNeutral();
expected = expected.MakeLineBreaksEnvironmentNeutral();

// Assert
Assert.AreEqual(expected, actual);
actual.Should()
.BePlatformAgnosticEquivalentTo(expected);
}

[Test]
Expand Down Expand Up @@ -1220,7 +1217,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes()
[Test]
public void Serializev2_WithBindings_Serializes()
{
var expected = @"asyncapi: '2.6.0'
var expected = @"asyncapi: 2.6.0
info:
description: test description
servers:
Expand Down Expand Up @@ -1309,11 +1306,9 @@ public void Serializev2_WithBindings_Serializes()
var reader = new AsyncApiStringReader(settings);
var deserialized = reader.Read(actual, out var diagnostic);

actual = actual.MakeLineBreaksEnvironmentNeutral();
expected = expected.MakeLineBreaksEnvironmentNeutral();

// Assert
Assert.AreEqual(expected, actual);
actual.Should()
.BePlatformAgnosticEquivalentTo(expected);
Assert.AreEqual(2, deserialized.Channels.First().Value.Publish.Message.First().Bindings.Count);

var binding = deserialized.Channels.First().Value.Publish.Message.First().Bindings.First();
Expand Down
8 changes: 3 additions & 5 deletions test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Tests
using LEGO.AsyncAPI.Readers.ParseNodes;
using NUnit.Framework;

public class AsyncApiLicenseTests
public class AsyncApiLicenseTests : TestBase
{
[Test]
public void Serialize_WithAllProperties_Serializes()
Expand All @@ -36,10 +36,8 @@ public void Serialize_WithAllProperties_Serializes()
var actual = license.SerializeAsJson(AsyncApiVersion.AsyncApi2_0);

// Assert
actual = actual.MakeLineBreaksEnvironmentNeutral();
expected = expected.MakeLineBreaksEnvironmentNeutral();

Assert.AreEqual(expected, actual);
actual.Should()
.BePlatformAgnosticEquivalentTo(expected);
}

public static Stream GenerateStreamFromString(string s)
Expand Down
10 changes: 4 additions & 6 deletions test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings
using LEGO.AsyncAPI.Writers;
using NUnit.Framework;

public class NestedConfiguration : IAsyncApiExtensible
public class NestedConfiguration : TestBase, IAsyncApiExtensible
{
public string Name { get; set; }

Expand Down Expand Up @@ -62,7 +62,7 @@ public override void SerializeProperties(IAsyncApiWriter writer)
}
}

public class CustomBinding_Should
public class CustomBinding_Should : TestBase
{
[Test]
public void CustomBinding_SerializesDeserializes()
Expand Down Expand Up @@ -107,15 +107,13 @@ public void CustomBinding_SerializesDeserializes()
var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0);

// Assert
actual = actual.MakeLineBreaksEnvironmentNeutral();
expected = expected.MakeLineBreaksEnvironmentNeutral();

var settings = new AsyncApiReaderSettings();
settings.Bindings = new[] { new MyBinding() };
var binding = new AsyncApiStringReader(settings).ReadFragment<AsyncApiChannel>(actual, AsyncApiVersion.AsyncApi2_0, out _);

// Assert
Assert.AreEqual(expected, actual);
actual.Should()
.BePlatformAgnosticEquivalentTo(expected);
binding.Should().BeEquivalentTo(channel);
}
}
Expand Down
Loading
Loading