Skip to content

Commit

Permalink
fix float64 encoding in duration (#4694)
Browse files Browse the repository at this point in the history
* fix float64 encoding in duration

* it turns out we need the serialization format for double

* change the format specifier to FFF to solve the digits problem

* regenerate cadl-ranch test cases
  • Loading branch information
ArcturusZhang committed May 11, 2024
1 parent 213b563 commit a69b89f
Show file tree
Hide file tree
Showing 37 changed files with 4,517 additions and 370 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 4 additions & 12 deletions src/AutoRest.CSharp/Common/Generation/Types/TypeFactory.cs
Expand Up @@ -57,19 +57,12 @@ public TypeFactory(OutputLibrary library, Type unknownType)
InputTypeKind.BytesBase64Url => Configuration.ShouldTreatBase64AsBinaryData ? new CSharpType(typeof(BinaryData), inputType.IsNullable) : new CSharpType(typeof(byte[]), inputType.IsNullable),
InputTypeKind.Bytes => Configuration.ShouldTreatBase64AsBinaryData ? new CSharpType(typeof(BinaryData), inputType.IsNullable) : new CSharpType(typeof(byte[]), inputType.IsNullable),
InputTypeKind.ContentType => new CSharpType(typeof(ContentType), inputType.IsNullable),
InputTypeKind.Date => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTime => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTimeISO8601 => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTimeRFC1123 => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTimeRFC3339 => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTimeRFC7231 => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.DateTimeUnix => new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.Date or InputTypeKind.DateTime or InputTypeKind.DateTimeISO8601 or InputTypeKind.DateTimeRFC1123 or InputTypeKind.DateTimeRFC3339 or InputTypeKind.DateTimeRFC7231 or InputTypeKind.DateTimeUnix
=> new CSharpType(typeof(DateTimeOffset), inputType.IsNullable),
InputTypeKind.Decimal => new CSharpType(typeof(decimal), inputType.IsNullable),
InputTypeKind.Decimal128 => new CSharpType(typeof(decimal), inputType.IsNullable),
InputTypeKind.DurationISO8601 => new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.DurationSeconds => new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.DurationSecondsFloat => new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.DurationConstant => new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.DurationISO8601 or InputTypeKind.DurationSeconds or InputTypeKind.DurationSecondsFloat or InputTypeKind.DurationSecondsDouble or InputTypeKind.DurationConstant or InputTypeKind.Time
=> new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.ETag => new CSharpType(typeof(ETag), inputType.IsNullable),
InputTypeKind.Float32 => new CSharpType(typeof(float), inputType.IsNullable),
InputTypeKind.Float64 => new CSharpType(typeof(double), inputType.IsNullable),
Expand All @@ -86,7 +79,6 @@ public TypeFactory(OutputLibrary library, Type unknownType)
InputTypeKind.ResourceType => new CSharpType(typeof(ResourceType), inputType.IsNullable),
InputTypeKind.Stream => new CSharpType(typeof(Stream), inputType.IsNullable),
InputTypeKind.String => new CSharpType(typeof(string), inputType.IsNullable),
InputTypeKind.Time => new CSharpType(typeof(TimeSpan), inputType.IsNullable),
InputTypeKind.Uri => new CSharpType(typeof(Uri), inputType.IsNullable),
InputTypeKind.Char => new CSharpType(typeof(char), inputType.IsNullable),
_ => new CSharpType(typeof(object), inputType.IsNullable),
Expand Down
Expand Up @@ -77,11 +77,11 @@ public static FormattableString GetFrameworkTypeValueFormattable(Type frameworkT

if (frameworkType == typeof(TimeSpan))
{
if (format == SerializationFormat.Duration_Seconds)
if (format is SerializationFormat.Duration_Seconds)
{
return $"{typeof(TimeSpan)}.FromSeconds({element}.GetInt32())";
}
else if (format == SerializationFormat.Duration_Seconds_Float)
else if (format is SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Seconds_Double)
{
return $"{typeof(TimeSpan)}.FromSeconds({element}.GetDouble())";
}
Expand Down
Expand Up @@ -159,6 +159,7 @@ private static InputExampleValue BuildEnumExampleValue(InputEnumType enumType)
InputTypeKind.Uri => InputExampleValue.Value(primitiveType, "http://localhost:3000"),
InputTypeKind.DurationSeconds => InputExampleValue.Value(primitiveType, 10),
InputTypeKind.DurationSecondsFloat => InputExampleValue.Value(primitiveType, 10f),
InputTypeKind.DurationSecondsDouble => InputExampleValue.Value(primitiveType, 3.141592),
_ => InputExampleValue.Object(primitiveType, new Dictionary<string, InputExampleValue>())
};

Expand Down Expand Up @@ -204,7 +205,7 @@ private static InputExampleValue BuildModelExampleValue(InputModelType model, bo
{
exampleValue = InputExampleValue.Value(property.Type, model.DiscriminatorValue!);
}
else if (property.ConstantValue is {Value: {} constantValue} )
else if (property.ConstantValue is { Value: { } constantValue })
{
exampleValue = InputExampleValue.Value(property.Type, constantValue);
}
Expand Down
Expand Up @@ -24,6 +24,7 @@ internal enum InputTypeKind
DurationConstant,
DurationSeconds,
DurationSecondsFloat,
DurationSecondsDouble,
ETag,
Float32,
Float64,
Expand Down
Expand Up @@ -72,8 +72,8 @@ public static object ReadConstantValue(ref Utf8JsonReader reader, string propert
value = reader.GetString() ?? throw new JsonException();
break;
case InputTypeKind.Uri:
var stringvalue = reader.GetString() ?? throw new JsonException();
value = new Uri(stringvalue);
var stringValue = reader.GetString() ?? throw new JsonException();
value = new Uri(stringValue);
break;
case InputTypeKind.Int32:
value = reader.GetInt32();
Expand Down
Expand Up @@ -430,7 +430,7 @@ private static MethodBodyStatement SerializeFrameworkTypeValue(Utf8JsonWriterExp
return utf8JsonWriter.WriteNumberValue(InvokeConvert.ToInt32(new TimeSpanExpression(value).InvokeToString(format)));
}

if (valueSerialization.Format is SerializationFormat.Duration_Seconds_Float)
if (valueSerialization.Format is SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Seconds_Double)
{
return utf8JsonWriter.WriteNumberValue(InvokeConvert.ToDouble(new TimeSpanExpression(value).InvokeToString(format)));
}
Expand Down Expand Up @@ -1153,12 +1153,12 @@ public static ValueExpression GetFrameworkTypeValueExpression(Type frameworkType
return element.GetDateTime();
if (frameworkType == typeof(TimeSpan))
{
if (format == SerializationFormat.Duration_Seconds)
if (format is SerializationFormat.Duration_Seconds)
{
return TimeSpanExpression.FromSeconds(element.GetInt32());
}

if (format == SerializationFormat.Duration_Seconds_Float)
if (format is SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Seconds_Double)
{
return TimeSpanExpression.FromSeconds(element.GetDouble());
}
Expand Down
Expand Up @@ -68,6 +68,7 @@ public static SerializationFormat GetDefaultSerializationFormat(CSharpType type)
InputTypeKind.DurationConstant => SerializationFormat.Duration_Constant,
InputTypeKind.DurationSeconds => SerializationFormat.Duration_Seconds,
InputTypeKind.DurationSecondsFloat => SerializationFormat.Duration_Seconds_Float,
InputTypeKind.DurationSecondsDouble => SerializationFormat.Duration_Seconds_Double,
InputTypeKind.Time => SerializationFormat.Time_ISO8601,
_ => SerializationFormat.Default
},
Expand Down
Expand Up @@ -16,6 +16,7 @@ internal enum SerializationFormat
Duration_Constant,
Duration_Seconds,
Duration_Seconds_Float,
Duration_Seconds_Double,
Time_ISO8601,
Bytes_Base64Url,
Bytes_Base64
Expand Down
Expand Up @@ -18,7 +18,8 @@ internal static class SerializationFormatExtensions
SerializationFormat.Duration_ISO8601 => "P",
SerializationFormat.Duration_Constant => "c",
SerializationFormat.Duration_Seconds => "%s",
SerializationFormat.Duration_Seconds_Float => "s\\.fff",
SerializationFormat.Duration_Seconds_Float => "s\\.FFF",
SerializationFormat.Duration_Seconds_Double => "s\\.FFFFFF",
SerializationFormat.Time_ISO8601 => "T",
_ => null
};
Expand Down
Expand Up @@ -87,7 +87,7 @@ private static ValueExpression GetExpressionForFrameworkType(Type frameworkType,
{
switch (serializationFormat)
{
case SerializationFormat.Duration_Seconds or SerializationFormat.Duration_Seconds_Float:
case SerializationFormat.Duration_Seconds or SerializationFormat.Duration_Seconds_Float or SerializationFormat.Duration_Seconds_Double:
if (rawValue.RawValue is int or float or double)
return new InvokeStaticMethodExpression(typeof(TimeSpan), nameof(TimeSpan.FromSeconds), new[] { Literal(rawValue.RawValue) });
break;
Expand Down
2 changes: 1 addition & 1 deletion test/CadlRanchMockApis/package.json
Expand Up @@ -32,6 +32,6 @@
"dist/**"
],
"peerDependencies": {
"@azure-tools/cadl-ranch-specs": "0.33.0"
"@azure-tools/cadl-ranch-specs": "0.33.3"
}
}
42 changes: 41 additions & 1 deletion test/CadlRanchProjects.Tests/encode-duration.cs
@@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using AutoRest.TestServer.Tests.Infrastructure;
Expand All @@ -20,27 +19,39 @@ public class EncodeDurationTests : CadlRanchTestBase
Response response = await new DurationClient(host, null).GetHeaderClient().DefaultAsync(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Header_Float64Seconds() => Test(async (host) =>
{
var input = TimeSpan.FromSeconds(35.621);
var response = await new DurationClient(host, null).GetHeaderClient().Float64SecondsAsync(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Header_FloatSeconds() => Test(async (host) =>
{
var input = TimeSpan.FromSeconds(35.621);
var response = await new DurationClient(host, null).GetHeaderClient().FloatSecondsAsync(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Header_Int32Seconds() => Test(async (host) =>
{
var input = TimeSpan.FromSeconds(36);
var response = await new DurationClient(host, null).GetHeaderClient().Int32SecondsAsync(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Header_ISO8601() => Test(async (host) =>
{
var input = new TimeSpan(40, 0, 0, 0);
var response = await new DurationClient(host, null).GetHeaderClient().Iso8601Async(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Header_ISO8601Array() => Test(async (host) =>
{
Expand All @@ -49,6 +60,7 @@ public class EncodeDurationTests : CadlRanchTestBase
var response = await new DurationClient(host, null).GetHeaderClient().Iso8601ArrayAsync(new[] { data1, data2 });
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Property_Default() => Test(async (host) =>
{
Expand Down Expand Up @@ -129,6 +141,26 @@ public class EncodeDurationTests : CadlRanchTestBase
Assert.AreEqual(body.Value, response.Value.Value);
});

[Test]
public Task Encode_Duration_Property_Float64Seconds() => Test(async (host) =>
{
var data = new
{
value = 35.621,
};
Response response = await new DurationClient(host, null).GetPropertyClient().FloatSecondsAsync(RequestContent.Create(data));
JsonElement result = JsonDocument.Parse(response.ContentStream).RootElement;
Assert.AreEqual("35.621", result.GetProperty("value").ToString());
});

[Test]
public Task Encode_Duration_Property_Float64Seconds_Convenience() => Test(async (host) =>
{
var body = new Float64SecondsDurationProperty(TimeSpan.FromSeconds(35.621));
Response<Float64SecondsDurationProperty> response = await new DurationClient(host, null).GetPropertyClient().Float64SecondsAsync(body);
Assert.AreEqual(body.Value, response.Value.Value);
});

[Test]
public Task Encode_Duration_Property_FloatSecondsArray() => Test(async (host) =>
{
Expand Down Expand Up @@ -184,6 +216,14 @@ public class EncodeDurationTests : CadlRanchTestBase
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Query_Float64Seconds() => Test(async (host) =>
{
var input = TimeSpan.FromSeconds(35.621);
var response = await new DurationClient(host, null).GetQueryClient().Float64SecondsAsync(input);
Assert.AreEqual(204, response.Status);
});

[Test]
public Task Encode_Duration_Query_Int32SecondsArray() => Test(async (host) =>
{
Expand Down
Expand Up @@ -188,6 +188,44 @@ Header client = new DurationClient().GetHeaderClient();
Response response = client.FloatSeconds(TimeSpan.FromSeconds(10F));
Console.WriteLine(response.Status);
]]></code></example>
</member>
<member name="Float64SecondsAsync(TimeSpan,RequestContext)">
<example>
This sample shows how to call Float64SecondsAsync.
<code><![CDATA[
Header client = new DurationClient().GetHeaderClient();
Response response = await client.Float64SecondsAsync(TimeSpan.FromSeconds(3.141592));
Console.WriteLine(response.Status);
]]></code>
This sample shows how to call Float64SecondsAsync with all parameters.
<code><![CDATA[
Header client = new DurationClient().GetHeaderClient();
Response response = await client.Float64SecondsAsync(TimeSpan.FromSeconds(3.141592));
Console.WriteLine(response.Status);
]]></code></example>
</member>
<member name="Float64Seconds(TimeSpan,RequestContext)">
<example>
This sample shows how to call Float64Seconds.
<code><![CDATA[
Header client = new DurationClient().GetHeaderClient();
Response response = client.Float64Seconds(TimeSpan.FromSeconds(3.141592));
Console.WriteLine(response.Status);
]]></code>
This sample shows how to call Float64Seconds with all parameters.
<code><![CDATA[
Header client = new DurationClient().GetHeaderClient();
Response response = client.Float64Seconds(TimeSpan.FromSeconds(3.141592));
Console.WriteLine(response.Status);
]]></code></example>
</member>
Expand Down

0 comments on commit a69b89f

Please sign in to comment.