diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 15fd094b975..eb3550d0869 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "boost.tool": { - "version": "0.2.2", + "version": "0.2.6", "commands": [ "boo" ] diff --git a/src/HotChocolate/Core/HotChocolate.Core.sln b/src/HotChocolate/Core/HotChocolate.Core.sln index e066b6c4233..a0a8a25218c 100644 --- a/src/HotChocolate/Core/HotChocolate.Core.sln +++ b/src/HotChocolate/Core/HotChocolate.Core.sln @@ -61,35 +61,39 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Execution.Benc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Fetching", "src\Fetching\HotChocolate.Fetching.csproj", "{3ED68361-0D3D-45F5-8A83-444DD3D5C1BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Subscriptions.Redis.Tests", "test\Subscriptions.Redis.Tests\HotChocolate.Subscriptions.Redis.Tests.csproj", "{14648C7D-8958-4C94-B837-7F90F2F10081}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.Redis.Tests", "test\Subscriptions.Redis.Tests\HotChocolate.Subscriptions.Redis.Tests.csproj", "{14648C7D-8958-4C94-B837-7F90F2F10081}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Subscriptions.InMemory.Tests", "test\Subscriptions.InMemory.Tests\HotChocolate.Subscriptions.InMemory.Tests.csproj", "{F72E72DE-F163-4BCC-A442-94DB331D3C35}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.InMemory.Tests", "test\Subscriptions.InMemory.Tests\HotChocolate.Subscriptions.InMemory.Tests.csproj", "{F72E72DE-F163-4BCC-A442-94DB331D3C35}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Fetching.Tests", "test\Fetching.Tests\HotChocolate.Fetching.Tests.csproj", "{2A319702-6C2E-4184-8E71-43F87745B38D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Fetching.Tests", "test\Fetching.Tests\HotChocolate.Fetching.Tests.csproj", "{2A319702-6C2E-4184-8E71-43F87745B38D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.StarWars", "benchmark\StarWars\HotChocolate.StarWars.csproj", "{BB53E6F4-2CA0-494C-85FD-DF22530476C0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.StarWars", "benchmark\StarWars\HotChocolate.StarWars.csproj", "{BB53E6F4-2CA0-494C-85FD-DF22530476C0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.CursorPagination", "src\Types.CursorPagination\HotChocolate.Types.CursorPagination.csproj", "{91F00C73-78E5-4400-8F9F-910BE99EDFD5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.CursorPagination", "src\Types.CursorPagination\HotChocolate.Types.CursorPagination.csproj", "{91F00C73-78E5-4400-8F9F-910BE99EDFD5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.OffsetPagination", "src\Types.OffsetPagination\HotChocolate.Types.OffsetPagination.csproj", "{738F2483-286E-4517-9660-5BFEEF343595}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.OffsetPagination", "src\Types.OffsetPagination\HotChocolate.Types.OffsetPagination.csproj", "{738F2483-286E-4517-9660-5BFEEF343595}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.CursorPagination.Tests", "test\Types.CursorPagination.Tests\HotChocolate.Types.CursorPagination.Tests.csproj", "{31AF3FED-A6C5-4DD5-99DF-03D4B4186D39}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.CursorPagination.Tests", "test\Types.CursorPagination.Tests\HotChocolate.Types.CursorPagination.Tests.csproj", "{31AF3FED-A6C5-4DD5-99DF-03D4B4186D39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.OffsetPagination.Tests", "test\Types.OffsetPagination.Tests\HotChocolate.Types.OffsetPagination.Tests.csproj", "{9EF119AD-4168-49F8-B2B2-8F6F1B5E47C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.OffsetPagination.Tests", "test\Types.OffsetPagination.Tests\HotChocolate.Types.OffsetPagination.Tests.csproj", "{9EF119AD-4168-49F8-B2B2-8F6F1B5E47C8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Records.Tests", "test\Types.Records.Tests\HotChocolate.Types.Records.Tests.csproj", "{CF2FA420-6295-4F83-8F87-340D8365282D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Records.Tests", "test\Types.Records.Tests\HotChocolate.Types.Records.Tests.csproj", "{CF2FA420-6295-4F83-8F87-340D8365282D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark10", "benchmark10", "{4882F732-028B-48E9-99D6-E94E56B6406E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Execution.Benchmarks", "benchmark10\Execution.Benchmarks\HotChocolate.Execution.Benchmarks.csproj", "{CBA38330-E94C-472F-8FFE-DA70DF0368EC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Execution.Benchmarks", "benchmark10\Execution.Benchmarks\HotChocolate.Execution.Benchmarks.csproj", "{CBA38330-E94C-472F-8FFE-DA70DF0368EC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.StarWars", "benchmark10\StarWars\HotChocolate.StarWars.csproj", "{0BE70B30-33CC-4BB7-8EEA-4981A2E9417C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.StarWars", "benchmark10\StarWars\HotChocolate.StarWars.csproj", "{0BE70B30-33CC-4BB7-8EEA-4981A2E9417C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars", "src\Types.Scalars\HotChocolate.Types.Scalars.csproj", "{8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars", "src\Types.Scalars\HotChocolate.Types.Scalars.csproj", "{8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars.Tests", "test\Types.Scalars.Tests\HotChocolate.Types.Scalars.Tests.csproj", "{480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars.Tests", "test\Types.Scalars.Tests\HotChocolate.Types.Scalars.Tests.csproj", "{480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars.Upload", "src\Types.Scalars.Upload\HotChocolate.Types.Scalars.Upload.csproj", "{670D3B20-401F-4004-AD0B-865560D02CC3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars.Upload", "src\Types.Scalars.Upload\HotChocolate.Types.Scalars.Upload.csproj", "{670D3B20-401F-4004-AD0B-865560D02CC3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.NodaTime", "src\Types.NodaTime\HotChocolate.Types.NodaTime.csproj", "{07F3E312-EC4C-4B71-A095-E478DF4CB52B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.NodaTime.Tests", "test\Types.NodaTime.Tests\HotChocolate.Types.NodaTime.Tests.csproj", "{414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -569,6 +573,30 @@ Global {670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x64.Build.0 = Release|Any CPU {670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x86.ActiveCfg = Release|Any CPU {670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x86.Build.0 = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x64.ActiveCfg = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x64.Build.0 = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x86.ActiveCfg = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x86.Build.0 = Debug|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|Any CPU.Build.0 = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x64.ActiveCfg = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x64.Build.0 = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x86.ActiveCfg = Release|Any CPU + {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x86.Build.0 = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x64.ActiveCfg = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x64.Build.0 = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x86.ActiveCfg = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x86.Build.0 = Debug|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|Any CPU.Build.0 = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x64.ActiveCfg = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x64.Build.0 = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x86.ActiveCfg = Release|Any CPU + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -613,6 +641,8 @@ Global {8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2} {480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5} = {7462D089-D350-44D6-8131-896D949A65B7} {670D3B20-401F-4004-AD0B-865560D02CC3} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2} + {07F3E312-EC4C-4B71-A095-E478DF4CB52B} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2} + {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB} = {7462D089-D350-44D6-8131-896D949A65B7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E4D94C77-6657-4630-9D42-0A9AC5153A1B} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs new file mode 100644 index 00000000000..e01992e3504 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs @@ -0,0 +1,154 @@ +using HotChocolate.Language; +using System.Diagnostics.CodeAnalysis; +using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// This base class provides serialization functionality for noda time scalars + /// that have a result value and a struct runtime value. + /// + /// + /// The runtime type. + /// + public abstract class IntToStructBaseType + : ScalarType + where TRuntimeType : struct + { + /// + /// Initializes a new instance of . + /// + /// + /// The name of the scalar. + /// + protected IntToStructBaseType(string name) + : base(name, BindingBehavior.Implicit) + { + } + + /// + protected override TRuntimeType ParseLiteral(IntValueNode literal) + { + if (TryDeserialize(literal.ToInt32(), out TRuntimeType? value)) + { + return value.Value; + } + + throw new SerializationException( + string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name), + this); + } + + /// + protected override IntValueNode ParseValue(TRuntimeType value) + { + if (TrySerialize(value, out var val)) + { + return new IntValueNode(val.Value); + } + + throw new SerializationException( + string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name), + this); + } + + /// + public override IValueNode ParseResult(object? resultValue) + { + if (resultValue is null) + { + return NullValueNode.Default; + } + + if (resultValue is int s) + { + return new IntValueNode(s); + } + + if (resultValue is TRuntimeType v) + { + return ParseValue(v); + } + + throw new SerializationException( + string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name), + this); + } + + /// + public override bool TrySerialize( + object? runtimeValue, + out object? resultValue) + { + if (runtimeValue is null) + { + resultValue = null; + return true; + } + + if (runtimeValue is TRuntimeType dt && TrySerialize(dt, out var val)) + { + resultValue = val.Value; + return true; + } + + resultValue = null; + return false; + } + + /// + /// Tries to serialize the .net runtime representation to the + /// serialized result representation. + /// + /// + /// The .net runtime representation. + /// + /// + /// The serialized result value. + /// + /// + /// Returns the serialized result value. + /// + protected abstract bool TrySerialize( + TRuntimeType runtimeValue, + [NotNullWhen(true)] out int? resultValue); + + /// + public override bool TryDeserialize( + object? resultValue, + out object? runtimeValue) + { + if (resultValue is null) + { + runtimeValue = null; + return true; + } + + if (resultValue is int i && TryDeserialize(i, out TRuntimeType? val)) + { + runtimeValue = val; + return true; + } + + runtimeValue = null; + return false; + } + + /// + /// Tries to deserializes the value from the output format to the .net + /// runtime representation. + /// + /// + /// The serialized result value. + /// + /// + /// The .net runtime representation. + /// + /// + /// true if the serialized value was correctly deserialized; otherwise, false. + /// + protected abstract bool TryDeserialize( + int resultValue, + [NotNullWhen(true)] out TRuntimeType? runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs new file mode 100644 index 00000000000..118a19b17e7 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs @@ -0,0 +1,135 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Language; +using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// This base class provides serialization functionality for noda time scalars + /// that have a result value and a class runtime value. + /// + /// + /// The runtime type. + /// + public abstract class StringToClassBaseType + : ScalarType + where TRuntimeType : class + { + /// + /// Initializes a new instance of . + /// + /// + /// The name of the scalar. + /// + public StringToClassBaseType(string name) + : base(name, BindingBehavior.Implicit) + { + } + + /// + protected override TRuntimeType ParseLiteral(StringValueNode literal) + { + if (TryDeserialize(literal.Value, out TRuntimeType? value)) + { + return value; + } + + throw new SerializationException( + string.Format(StringToClassBaseType_ParseLiteral_UnableToDeserializeString, Name), + this); + } + + /// + protected override StringValueNode ParseValue(TRuntimeType value) => + new(Serialize(value)); + + /// + public override IValueNode ParseResult(object? resultValue) + { + if (resultValue is null) + { + return NullValueNode.Default; + } + + if (resultValue is string s) + { + return new StringValueNode(s); + } + + if (resultValue is TRuntimeType v) + { + return ParseValue(v); + } + + throw new SerializationException( + string.Format(StringToClassBaseType_ParseLiteral_UnableToDeserializeString, Name), + this); + } + + /// + public override bool TrySerialize(object? runtimeValue, out object? resultValue) + { + if (runtimeValue is null) + { + resultValue = null; + return true; + } + + if (runtimeValue is TRuntimeType dt) + { + resultValue = Serialize(dt); + return true; + } + + resultValue = null; + return false; + } + + /// + /// Serializes the .net runtime representation to the serialized result representation. + /// + /// + /// The .net value representation. + /// + /// + /// Returns the serialized result value. + /// + protected abstract string Serialize(TRuntimeType runtimeValue); + + /// + public override bool TryDeserialize(object? resultValue, out object? runtimeValue) + { + if (resultValue is null) + { + runtimeValue = null; + return true; + } + + if (resultValue is string s && TryDeserialize(s, out TRuntimeType? val)) + { + runtimeValue = val; + return true; + } + + runtimeValue = null; + return false; + } + + // + /// Tries to deserializes the value from the output format to the .net + /// runtime representation. + /// + /// + /// The serialized result value. + /// + /// + /// The .net runtime representation. + /// + /// + /// true if the serialized value was correctly deserialized; otherwise, false. + /// + protected abstract bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out TRuntimeType? runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs new file mode 100644 index 00000000000..4644b37e6a1 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs @@ -0,0 +1,137 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Language; +using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// This base class provides serialization functionality for noda time scalars + /// that have a result value and a struct runtime value. + /// + /// + /// The runtime type. + /// + public abstract class StringToStructBaseType + : ScalarType + where TRuntimeType : struct + { + /// + /// Initializes a new instance of . + /// + /// + /// The name of the scalar. + /// + public StringToStructBaseType(string name) + : base(name, BindingBehavior.Implicit) + { + } + + /// + protected override TRuntimeType ParseLiteral(StringValueNode literal) + { + if (TryDeserialize(literal.Value, out TRuntimeType? value)) + { + return value.Value; + } + + throw new SerializationException( + string.Format(StringToStructBaseType_ParseLiteral_UnableToDeserializeString, Name), + this); + } + + /// + protected override StringValueNode ParseValue(TRuntimeType value) + { + return new(Serialize(value)); + } + + /// + public override IValueNode ParseResult(object? resultValue) + { + if (resultValue is null) + { + return NullValueNode.Default; + } + + if (resultValue is string s) + { + return new StringValueNode(s); + } + + if (resultValue is TRuntimeType v) + { + return ParseValue(v); + } + + throw new SerializationException( + string.Format(StringToStructBaseType_ParseLiteral_UnableToDeserializeString, Name), + this); + } + + /// + public override bool TrySerialize(object? runtimeValue, out object? resultValue) + { + if (runtimeValue is null) + { + resultValue = null; + return true; + } + + if (runtimeValue is TRuntimeType dt) + { + resultValue = Serialize(dt); + return true; + } + + resultValue = null; + return false; + } + + /// + /// Serializes the .net runtime representation to the serialized result representation. + /// + /// + /// The .net value representation. + /// + /// + /// Returns the serialized result value. + /// + protected abstract string Serialize(TRuntimeType runtimeValue); + + /// + public override bool TryDeserialize(object? resultValue, out object? runtimeValue) + { + if (resultValue is null) + { + runtimeValue = null; + return true; + } + + if (resultValue is string s && TryDeserialize(s, out TRuntimeType? val)) + { + runtimeValue = val; + return true; + } + + runtimeValue = null; + return false; + } + + /// + /// Tries to deserializes the value from the output format to the .net + /// runtime representation. + /// + /// + /// The serialized result value. + /// + /// + /// The .net runtime representation. + /// + /// + /// true if the serialized value was correctly deserialized; otherwise, false. + /// + protected abstract bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out TRuntimeType? runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs new file mode 100644 index 00000000000..6476fa24713 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs @@ -0,0 +1,43 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// Represents a time zone - a mapping between UTC and local time. + /// A time zone maps UTC instants to local times - or, equivalently, + /// to the offset from UTC at any particular instant. + /// + public class DateTimeZoneType : StringToClassBaseType + { + /// + /// Initializes a new instance of . + /// + public DateTimeZoneType() : base("DateTimeZone") + { + Description = NodaTimeResources.DateTimeZoneType_Description; + } + + /// + protected override string Serialize(DateTimeZone runtimeValue) + => runtimeValue.Id; + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out DateTimeZone? runtimeValue) + { + DateTimeZone? result = DateTimeZoneProviders.Tzdb.GetZoneOrNull(resultValue); + + if (result == null) + { + runtimeValue = null; + return false; + } + + runtimeValue = result; + return true; + } + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs new file mode 100644 index 00000000000..f8ff33b95ad --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// Represents a fixed (and calendar-independent) length of time. + /// + public class DurationType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public DurationType() : base("Duration") + { + Description = NodaTimeResources.DurationType_Description; + } + + /// + protected override string Serialize(Duration runtimeValue) + => DurationPattern.Roundtrip + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out Duration? runtimeValue) + => DurationPattern.Roundtrip + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs new file mode 100644 index 00000000000..3ef5df23f2f --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs @@ -0,0 +1,43 @@ +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + internal static class PatternExtensions + { + public static bool TryParse( + this IPattern pattern, + string text, + out NodaTimeType? output) + where NodaTimeType : struct + { + ParseResult result = pattern.Parse(text); + + if (result.Success) + { + output = result.Value; + return true; + } + + output = null; + return false; + } + + public static bool TryParse( + this IPattern pattern, + string text, + out NodaTimeType? output) + where NodaTimeType : class + { + ParseResult result = pattern.Parse(text); + + if (result.Success) + { + output = result.Value; + return true; + } + + output = null; + return false; + } + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj b/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj new file mode 100644 index 00000000000..b0ea3c73010 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj @@ -0,0 +1,56 @@ + + + + enable + $(WarningsAsErrors);nullable + true + + + + HotChocolate.Types.NodaTime + HotChocolate.Types.NodaTime + HotChocolate.Types.NodaTime + Adds additional scalars that map to NodaTime runtime types + + + + + + + + + + + + + ResXFileCodeGenerator + NodaTimeResources.Designer.cs + + + + + + True + True + NodaTimeResources.resx + + + + + + diff --git a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs new file mode 100644 index 00000000000..88759e3ae74 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs @@ -0,0 +1,36 @@ +using System.Globalization; +using NodaTime; +using NodaTime.Text; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Types.NodaTime.Properties; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// Represents an instant on the global timeline, with nanosecond resolution. + /// + public class InstantType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public InstantType() : base("Instant") + { + Description = NodaTimeResources.InstantType_Description; + } + + /// + protected override string Serialize(Instant runtimeValue) + => InstantPattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out Instant? runtimeValue) + => InstantPattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs b/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs new file mode 100644 index 00000000000..056047f260b --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs @@ -0,0 +1,51 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// Equates the days of the week with their numerical value according to ISO-8601. + /// Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7. + /// + public class IsoDayOfWeekType : IntToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public IsoDayOfWeekType() : base("IsoDayOfWeek") + { + Description = NodaTimeResources.IsoDayOfWeekType_Description; + } + + /// + protected override bool TrySerialize( + IsoDayOfWeek runtimeValue, + [NotNullWhen(true)] out int? resultValue) + { + if (runtimeValue == IsoDayOfWeek.None) + { + resultValue = null; + return false; + } + + resultValue = (int)runtimeValue; + return true; + } + + /// + protected override bool TryDeserialize( + int resultValue, + [NotNullWhen(true)] out IsoDayOfWeek? runtimeValue) + { + if (resultValue < 1 || resultValue > 7) + { + runtimeValue = null; + return false; + } + + runtimeValue = (IsoDayOfWeek)resultValue; + return true; + } + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs new file mode 100644 index 00000000000..f850b042cf8 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// A date and time in a particular calendar system. + /// + public class LocalDateTimeType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public LocalDateTimeType() : base("LocalDateTime") + { + Description = NodaTimeResources.LocalDateTimeType_Description; + } + + /// + protected override string Serialize(LocalDateTime runtimeValue) + => LocalDateTimePattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out LocalDateTime? runtimeValue) + => LocalDateTimePattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs new file mode 100644 index 00000000000..852e892db2e --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// LocalDate is an immutable struct representing a date within the calendar, + /// with no reference to a particular time zone or time of day. + /// + public class LocalDateType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public LocalDateType() : base("LocalDate") + { + Description = NodaTimeResources.LocalDateType_Description; + } + + /// + protected override string Serialize(LocalDate runtimeValue) + => LocalDatePattern.Iso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out LocalDate? runtimeValue) + => LocalDatePattern.Iso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs new file mode 100644 index 00000000000..e59c145e231 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// LocalTime is an immutable struct representing a time of day, + /// with no reference to a particular calendar, time zone or date. + /// + public class LocalTimeType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public LocalTimeType() : base("LocalTime") + { + Description = NodaTimeResources.LocalTimeType_Description; + } + + /// + protected override string Serialize(LocalTime runtimeValue) + => LocalTimePattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out LocalTime? runtimeValue) + => LocalTimePattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs new file mode 100644 index 00000000000..a3cb247be8f --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs @@ -0,0 +1,36 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// A local date and time in a particular calendar system, combined with an offset from UTC. + /// + public class OffsetDateTimeType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public OffsetDateTimeType() : base("OffsetDateTime") + { + Description = NodaTimeResources.OffsetDateTimeType_Description; + } + + /// + protected override string Serialize(OffsetDateTime runtimeValue) + => OffsetDateTimePattern.GeneralIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out OffsetDateTime? runtimeValue) + => OffsetDateTimePattern.ExtendedIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs new file mode 100644 index 00000000000..d123d0df004 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// A combination of a LocalDate and an Offset, + /// to represent a date at a specific offset from UTC but + /// without any time-of-day information. + /// + public class OffsetDateType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public OffsetDateType() : base("OffsetDate") + { + Description = NodaTimeResources.OffsetDateType_Description; + } + + /// + protected override string Serialize(OffsetDate runtimeValue) + => OffsetDatePattern.GeneralIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out OffsetDate? runtimeValue) + => OffsetDatePattern.GeneralIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs new file mode 100644 index 00000000000..286b4140792 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset + /// from UTC but without any date information. + /// + public class OffsetTimeType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public OffsetTimeType() : base("OffsetTime") + { + Description = NodaTimeResources.OffsetTimeType_Description; + } + + /// + protected override string Serialize(OffsetTime runtimeValue) + => OffsetTimePattern.GeneralIso + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out OffsetTime? runtimeValue) + => OffsetTimePattern.GeneralIso + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs new file mode 100644 index 00000000000..eaa74d4d95d --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// An offset from UTC in seconds. + /// A positive value means that the local time is ahead of UTC (e.g. for Europe); + /// a negative value means that the local time is behind UTC (e.g. for America). + /// + public class OffsetType : StringToStructBaseType + { + /// + /// Initializes a new instance of . + /// + public OffsetType() : base("Offset") + { + Description = NodaTimeResources.OffsetType_Description; + } + + /// + protected override string Serialize(Offset runtimeValue) + => OffsetPattern.GeneralInvariantWithZ + .WithCulture(CultureInfo.InvariantCulture) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out Offset? runtimeValue) + => OffsetPattern.GeneralInvariantWithZ + .WithCulture(CultureInfo.InvariantCulture) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs new file mode 100644 index 00000000000..64299c02f6c --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs @@ -0,0 +1,32 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// Represents a period of time expressed in human chronological terms: + /// hours, days, weeks, months and so on. + /// + public class PeriodType : StringToClassBaseType + { + /// + /// Initializes a new instance of . + /// + public PeriodType() : base("Period") + { + Description = NodaTimeResources.PeriodType_Description; + } + + /// + protected override string Serialize(Period runtimeValue) + => PeriodPattern.Roundtrip.Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out Period? runtimeValue) + => PeriodPattern.Roundtrip.TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs new file mode 100644 index 00000000000..484ebfda87b --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HotChocolate.Types.NodaTime.Properties { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class NodaTimeResources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal NodaTimeResources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Types.NodaTime.Properties.NodaTimeResources", typeof(NodaTimeResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string IntToStructBaseType_ParseLiteral_UnableToDeserializeInt { + get { + return ResourceManager.GetString("IntToStructBaseType_ParseLiteral_UnableToDeserializeInt", resourceCulture); + } + } + + internal static string StringToClassBaseType_ParseLiteral_UnableToDeserializeString { + get { + return ResourceManager.GetString("StringToClassBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + } + } + + internal static string StringToStructBaseType_ParseLiteral_UnableToDeserializeString { + get { + return ResourceManager.GetString("StringToStructBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + } + } + + internal static string DateTimeZoneType_Description { + get { + return ResourceManager.GetString("DateTimeZoneType_Description", resourceCulture); + } + } + + internal static string DurationType_Description { + get { + return ResourceManager.GetString("DurationType_Description", resourceCulture); + } + } + + internal static string InstantType_Description { + get { + return ResourceManager.GetString("InstantType_Description", resourceCulture); + } + } + + internal static string IsoDayOfWeekType_Description { + get { + return ResourceManager.GetString("IsoDayOfWeekType_Description", resourceCulture); + } + } + + internal static string LocalDateTimeType_Description { + get { + return ResourceManager.GetString("LocalDateTimeType_Description", resourceCulture); + } + } + + internal static string LocalDateType_Description { + get { + return ResourceManager.GetString("LocalDateType_Description", resourceCulture); + } + } + + internal static string LocalTimeType_Description { + get { + return ResourceManager.GetString("LocalTimeType_Description", resourceCulture); + } + } + + internal static string OffsetDateTimeType_Description { + get { + return ResourceManager.GetString("OffsetDateTimeType_Description", resourceCulture); + } + } + + internal static string OffsetDateType_Description { + get { + return ResourceManager.GetString("OffsetDateType_Description", resourceCulture); + } + } + + internal static string OffsetTimeType_Description { + get { + return ResourceManager.GetString("OffsetTimeType_Description", resourceCulture); + } + } + + internal static string OffsetType_Description { + get { + return ResourceManager.GetString("OffsetType_Description", resourceCulture); + } + } + + internal static string PeriodType_Description { + get { + return ResourceManager.GetString("PeriodType_Description", resourceCulture); + } + } + + internal static string ZonedDateTimeType_Description { + get { + return ResourceManager.GetString("ZonedDateTimeType_Description", resourceCulture); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx new file mode 100644 index 00000000000..5b3c434deef --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx @@ -0,0 +1,73 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Unable to deserialize integer to {0} + + + Unable to deserialize string to {0} + + + Unable to deserialize string to {0} + + + Represents a time zone - a mapping between UTC and local time. +A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. + + + Represents a fixed (and calendar-independent) length of time. + + + Represents an instant on the global timeline, with nanosecond resolution. + + + Equates the days of the week with their numerical value according to ISO-8601. + Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7. + + + A date and time in a particular calendar system. + + + LocalDate is an immutable struct representing a date within the calendar, with no reference to a particular time zone or time of day. + + + LocalTime is an immutable struct representing a time of day, with no reference to a particular calendar, time zone or date. + + + A local date and time in a particular calendar system, combined with an offset from UTC. + + + A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + + + A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + + + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + + + Represents a period of time expressed in human chronological terms: hours, days, weeks, months and so on. + + + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. +A ZonedDateTime is global, in that it maps to a single Instant. + + diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt new file mode 100644 index 00000000000..ab058de62d4 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt @@ -0,0 +1 @@ +#nullable enable diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt new file mode 100644 index 00000000000..d54a80a9848 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt @@ -0,0 +1,79 @@ +abstract HotChocolate.Types.NodaTime.IntToStructBaseType.TryDeserialize(int resultValue, out TRuntimeType? runtimeValue) -> bool +abstract HotChocolate.Types.NodaTime.IntToStructBaseType.TrySerialize(TRuntimeType runtimeValue, out int? resultValue) -> bool +abstract HotChocolate.Types.NodaTime.StringToClassBaseType.Serialize(TRuntimeType runtimeValue) -> string +abstract HotChocolate.Types.NodaTime.StringToClassBaseType.TryDeserialize(string resultValue, out TRuntimeType runtimeValue) -> bool +abstract HotChocolate.Types.NodaTime.StringToStructBaseType.Serialize(TRuntimeType runtimeValue) -> string +abstract HotChocolate.Types.NodaTime.StringToStructBaseType.TryDeserialize(string resultValue, out TRuntimeType? runtimeValue) -> bool +HotChocolate.Types.NodaTime.DateTimeZoneType +HotChocolate.Types.NodaTime.DateTimeZoneType.DateTimeZoneType() -> void +HotChocolate.Types.NodaTime.DurationType +HotChocolate.Types.NodaTime.DurationType.DurationType() -> void +HotChocolate.Types.NodaTime.InstantType +HotChocolate.Types.NodaTime.InstantType.InstantType() -> void +HotChocolate.Types.NodaTime.IntToStructBaseType +HotChocolate.Types.NodaTime.IntToStructBaseType.IntToStructBaseType(string name) -> void +HotChocolate.Types.NodaTime.IsoDayOfWeekType +HotChocolate.Types.NodaTime.IsoDayOfWeekType.IsoDayOfWeekType() -> void +HotChocolate.Types.NodaTime.LocalDateTimeType +HotChocolate.Types.NodaTime.LocalDateTimeType.LocalDateTimeType() -> void +HotChocolate.Types.NodaTime.LocalDateType +HotChocolate.Types.NodaTime.LocalDateType.LocalDateType() -> void +HotChocolate.Types.NodaTime.LocalTimeType +HotChocolate.Types.NodaTime.LocalTimeType.LocalTimeType() -> void +HotChocolate.Types.NodaTime.OffsetDateTimeType +HotChocolate.Types.NodaTime.OffsetDateTimeType.OffsetDateTimeType() -> void +HotChocolate.Types.NodaTime.OffsetDateType +HotChocolate.Types.NodaTime.OffsetDateType.OffsetDateType() -> void +HotChocolate.Types.NodaTime.OffsetTimeType +HotChocolate.Types.NodaTime.OffsetTimeType.OffsetTimeType() -> void +HotChocolate.Types.NodaTime.OffsetType +HotChocolate.Types.NodaTime.OffsetType.OffsetType() -> void +HotChocolate.Types.NodaTime.PeriodType +HotChocolate.Types.NodaTime.PeriodType.PeriodType() -> void +HotChocolate.Types.NodaTime.StringToClassBaseType +HotChocolate.Types.NodaTime.StringToClassBaseType.StringToClassBaseType(string name) -> void +HotChocolate.Types.NodaTime.StringToStructBaseType +HotChocolate.Types.NodaTime.StringToStructBaseType.StringToStructBaseType(string name) -> void +HotChocolate.Types.NodaTime.ZonedDateTimeType +HotChocolate.Types.NodaTime.ZonedDateTimeType.ZonedDateTimeType() -> void +override HotChocolate.Types.NodaTime.DateTimeZoneType.Serialize(NodaTime.DateTimeZone runtimeValue) -> string +override HotChocolate.Types.NodaTime.DateTimeZoneType.TryDeserialize(string resultValue, out NodaTime.DateTimeZone runtimeValue) -> bool +override HotChocolate.Types.NodaTime.DurationType.Serialize(NodaTime.Duration runtimeValue) -> string +override HotChocolate.Types.NodaTime.DurationType.TryDeserialize(string resultValue, out NodaTime.Duration? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.InstantType.Serialize(NodaTime.Instant runtimeValue) -> string +override HotChocolate.Types.NodaTime.InstantType.TryDeserialize(string resultValue, out NodaTime.Instant? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseLiteral(HotChocolate.Language.IntValueNode literal) -> TRuntimeType +override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode +override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.IntValueNode +override HotChocolate.Types.NodaTime.IntToStructBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool +override HotChocolate.Types.NodaTime.IntToStructBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool +override HotChocolate.Types.NodaTime.IsoDayOfWeekType.TryDeserialize(int resultValue, out NodaTime.IsoDayOfWeek? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.IsoDayOfWeekType.TrySerialize(NodaTime.IsoDayOfWeek runtimeValue, out int? resultValue) -> bool +override HotChocolate.Types.NodaTime.LocalDateTimeType.Serialize(NodaTime.LocalDateTime runtimeValue) -> string +override HotChocolate.Types.NodaTime.LocalDateTimeType.TryDeserialize(string resultValue, out NodaTime.LocalDateTime? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.LocalDateType.Serialize(NodaTime.LocalDate runtimeValue) -> string +override HotChocolate.Types.NodaTime.LocalDateType.TryDeserialize(string resultValue, out NodaTime.LocalDate? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.LocalTimeType.Serialize(NodaTime.LocalTime runtimeValue) -> string +override HotChocolate.Types.NodaTime.LocalTimeType.TryDeserialize(string resultValue, out NodaTime.LocalTime? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.OffsetDateTimeType.Serialize(NodaTime.OffsetDateTime runtimeValue) -> string +override HotChocolate.Types.NodaTime.OffsetDateTimeType.TryDeserialize(string resultValue, out NodaTime.OffsetDateTime? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.OffsetDateType.Serialize(NodaTime.OffsetDate runtimeValue) -> string +override HotChocolate.Types.NodaTime.OffsetDateType.TryDeserialize(string resultValue, out NodaTime.OffsetDate? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.OffsetTimeType.Serialize(NodaTime.OffsetTime runtimeValue) -> string +override HotChocolate.Types.NodaTime.OffsetTimeType.TryDeserialize(string resultValue, out NodaTime.OffsetTime? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.OffsetType.Serialize(NodaTime.Offset runtimeValue) -> string +override HotChocolate.Types.NodaTime.OffsetType.TryDeserialize(string resultValue, out NodaTime.Offset? runtimeValue) -> bool +override HotChocolate.Types.NodaTime.PeriodType.Serialize(NodaTime.Period runtimeValue) -> string +override HotChocolate.Types.NodaTime.PeriodType.TryDeserialize(string resultValue, out NodaTime.Period runtimeValue) -> bool +override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseLiteral(HotChocolate.Language.StringValueNode literal) -> TRuntimeType +override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode +override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.StringValueNode +override HotChocolate.Types.NodaTime.StringToClassBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool +override HotChocolate.Types.NodaTime.StringToClassBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool +override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseLiteral(HotChocolate.Language.StringValueNode literal) -> TRuntimeType +override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode +override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.StringValueNode +override HotChocolate.Types.NodaTime.StringToStructBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool +override HotChocolate.Types.NodaTime.StringToStructBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool +override HotChocolate.Types.NodaTime.ZonedDateTimeType.Serialize(NodaTime.ZonedDateTime runtimeValue) -> string +override HotChocolate.Types.NodaTime.ZonedDateTimeType.TryDeserialize(string resultValue, out NodaTime.ZonedDateTime? runtimeValue) -> bool \ No newline at end of file diff --git a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs new file mode 100644 index 00000000000..5c36838e779 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs @@ -0,0 +1,40 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Types.NodaTime.Properties; +using NodaTime; +using NodaTime.Text; + +namespace HotChocolate.Types.NodaTime +{ + /// + /// A LocalDateTime in a specific time zone and with a particular offset to distinguish between + /// otherwise-ambiguous instants.\nA ZonedDateTime is global, in that it maps to + /// a single Instant. + /// + public class ZonedDateTimeType : StringToStructBaseType + { + private static readonly string formatString = "uuuu'-'MM'-'dd'T'HH':'mm':'ss' 'z' 'o"; + + /// + /// Initializes a new instance of . + /// + public ZonedDateTimeType() + : base("ZonedDateTime") + { + Description = NodaTimeResources.ZonedDateTimeType_Description; + } + + /// + protected override string Serialize(ZonedDateTime runtimeValue) + => ZonedDateTimePattern + .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb) + .Format(runtimeValue); + + /// + protected override bool TryDeserialize( + string resultValue, + [NotNullWhen(true)] out ZonedDateTime? runtimeValue) + => ZonedDateTimePattern + .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb) + .TryParse(resultValue, out runtimeValue); + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs index 54f4c98e18d..9ffbdc7de0c 100644 --- a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs @@ -9,8 +9,7 @@ namespace HotChocolate.Types /// GraphQL responses take the form of a hierarchical tree; /// the leaves on these trees are GraphQL scalars. /// - public abstract class ScalarType - : ScalarType + public abstract class ScalarType : ScalarType { /// protected ScalarType(NameString name, BindingBehavior bind = BindingBehavior.Explicit) diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs new file mode 100644 index 00000000000..5a863c27cbe --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs @@ -0,0 +1,114 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class DateTimeZoneTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public DateTimeZone Utc => DateTimeZone.Utc; + public DateTimeZone Rome => DateTimeZoneProviders.Tzdb["Europe/Rome"]; + public DateTimeZone Chihuahua => DateTimeZoneProviders.Tzdb["America/Chihuahua"]; + } + + public class Mutation + { + public string Test(DateTimeZone arg) + { + return arg.Id; + } + } + } + + private readonly IRequestExecutor testExecutor; + public DateTimeZoneTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: utc }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("UTC", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsRome() + { + IExecutionResult? result = testExecutor.Execute("query { test: rome }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("Europe/Rome", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsChihuahua() + { + IExecutionResult? result = testExecutor.Execute("query { test: chihuahua }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("America/Chihuahua", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: DateTimeZone!) { test(arg: $arg) }") + .SetVariableValue("arg", "Europe/Amsterdam") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("Europe/Amsterdam", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: DateTimeZone!) { test(arg: $arg) }") + .SetVariableValue("arg", "Europe/Hamster") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"Europe/Amsterdam\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("Europe/Amsterdam", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"Europe/Hamster\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to DateTimeZone", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs new file mode 100644 index 00000000000..2846785ce1c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs @@ -0,0 +1,232 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class DurationTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public Duration PositiveWithDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + public Duration NegativeWithDecimals => -Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + public Duration PositiveWithoutDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10)); + public Duration PositiveWithoutSeconds => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 0)); + public Duration PositiveWithoutMinutes => Duration.FromTimeSpan(new TimeSpan(123, 7, 0, 0)); + public Duration PositiveWithRoundtrip => Duration.FromTimeSpan(new TimeSpan(123, 26, 0, 70)); + } + + public class Mutation + { + public Duration Test(Duration arg) + => arg + Duration.FromMinutes(10); + } + } + + private readonly IRequestExecutor testExecutor; + public DurationTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsSerializedDataWithDecimals() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("123:07:53:10.019", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithNegativeValue() + { + IExecutionResult? result = testExecutor.Execute("query { test: negativeWithDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-123:07:53:10.019", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutDecimals() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutDecimals }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("123:07:53:10", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutSeconds() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutSeconds }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("123:07:53:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithoutMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("123:07:00:00", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSerializedDataWithRoundtrip() + { + IExecutionResult? result = testExecutor.Execute("query { test: positiveWithRoundtrip }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("124:02:01:10", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "09:22:01:00.019") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00.019", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithoutDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "09:22:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithoutLeadingZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "9:22:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesInputWithNegativeValue() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "-9:22:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-9:21:51:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationDoesntParseInputWithPlusSign() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "+09:22:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationDoesntParseInputWithOverflownHours() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValue("arg", "9:26:01:00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationParsesLiteralWithDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"09:22:01:00.019\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00.019", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithoutDecimals() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"09:22:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithoutLeadingZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"09:22:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("9:22:11:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesLiteralWithNegativeValue() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"-9:22:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("-9:21:51:00", queryResult!.Data!["test"]); + } + + [Fact] + public void MutationDoesntParseLiteralWithPlusSign() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+09:22:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationDoesntParseLiteralWithOverflownHours() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"9:26:01:00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj new file mode 100644 index 00000000000..ba278a8d870 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj @@ -0,0 +1,28 @@ + + + + HotChocolate.Types.NodaTime.Tests + HotChocolate.Types.NodaTime + enable + $(WarningsAsErrors);nullable + + + + + + + + + + + Always + + + + + + + + + + diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs new file mode 100644 index 00000000000..f64c56a9b55 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs @@ -0,0 +1,96 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class InstantTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public Instant One => Instant.FromUtc(2020, 02, 20, 17, 42, 59); + } + + public class Mutation + { + public Instant Test(Instant arg) + { + return arg + Duration.FromMinutes(10); + } + } + } + + private readonly IRequestExecutor testExecutor; + public InstantTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:42:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to Instant", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs new file mode 100644 index 00000000000..f81f80db203 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs @@ -0,0 +1,139 @@ +using System; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class IsoDayOfWeekTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public IsoDayOfWeek Monday => IsoDayOfWeek.Monday; + public IsoDayOfWeek Sunday => IsoDayOfWeek.Sunday; + public IsoDayOfWeek Friday => IsoDayOfWeek.Friday; + public IsoDayOfWeek None => IsoDayOfWeek.None; + } + + public class Mutation + { + public IsoDayOfWeek Test(IsoDayOfWeek arg) + { + var intRepr = (int)arg; + var nextIntRepr = Math.Max(1, (intRepr + 1) % 8); + return (IsoDayOfWeek)nextIntRepr; + } + } + } + + private readonly IRequestExecutor testExecutor; + public IsoDayOfWeekTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturnsMonday() + { + IExecutionResult? result = testExecutor.Execute("query { test: monday }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal(1, queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsSunday() + { + IExecutionResult? result = testExecutor.Execute("query { test: sunday }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal(7, queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsFriday() + { + IExecutionResult? result = testExecutor.Execute("query { test: friday }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal(5, queryResult!.Data!["test"]); + } + + [Fact] + public void QueryDoesntReturnNone() + { + IExecutionResult? result = testExecutor.Execute("query { test: none }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.NotEmpty(queryResult!.Errors); + } + + [Fact] + public void MutationParsesMonday() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }") + .SetVariableValue("arg", 1) + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal(2, queryResult!.Data!["test"]); + } + + [Fact] + public void MutationParsesSunday() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }") + .SetVariableValue("arg", 7) + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal(1, queryResult!.Data!["test"]); + } + + [Fact] + public void MutationDoesntParseZero() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }") + .SetVariableValue("arg", 0) + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationDoesntParseEight() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }") + .SetVariableValue("arg", 8) + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void MutationDoesntParseNegativeNumbers() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }") + .SetVariableValue("arg", -2) + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs new file mode 100644 index 00000000000..b128ad4e8ce --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalDateTimeTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public LocalDateTime One => LocalDateTime.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59)); + } + + public class Mutation + { + public LocalDateTime Test(LocalDateTime arg) + { + return arg + Period.FromMinutes(10); + } + } + } + + private readonly IRequestExecutor testExecutor; + public LocalDateTimeTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:42:59", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-21T17:52:59", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59Z") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20T17:52:59", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to LocalDateTime", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs new file mode 100644 index 00000000000..5671a5706d5 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalDateTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public LocalDate One => LocalDate.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59)); + } + + public class Mutation + { + public LocalDate Test(LocalDate arg) + { + return arg + Period.FromDays(3); + } + } + } + + private readonly IRequestExecutor testExecutor; + public LocalDateTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-20", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-21") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-24", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-02-20T17:42:59") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-02-23", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to LocalDate", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs new file mode 100644 index 00000000000..74fca6eb1f9 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs @@ -0,0 +1,119 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class LocalTimeTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public LocalTime One => LocalTime.FromHourMinuteSecondMillisecondTick(12, 42, 13, 31, 100); + } + + public class Mutation + { + public LocalTime Test(LocalTime arg) + { + return arg + Period.FromMinutes(10); + } + } + } + + private readonly IRequestExecutor testExecutor; + public LocalTimeTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:42:13.03101", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "12:42:13.03101") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithoutTicks() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "12:42:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "12:42") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"12:42:13.03101\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithoutTick() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"12:42:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("12:52:13", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"12:42\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to LocalTime", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs new file mode 100644 index 00000000000..46eb18a82fb --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs @@ -0,0 +1,23 @@ +namespace HotChocolate.Types.NodaTime.Tests +{ + public static class NodaTimeRequestExecutorBuilderExtensions + { + public static ISchemaBuilder AddNodaTime(this ISchemaBuilder schemaBuilder) + { + return schemaBuilder + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType(); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs new file mode 100644 index 00000000000..2ed669eeab5 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs @@ -0,0 +1,134 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTimeTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public OffsetDateTime Hours => + OffsetDateTime.FromDateTimeOffset( + new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2))); + + public OffsetDateTime HoursAndMinutes => + OffsetDateTime.FromDateTimeOffset( + new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30))); + } + + public class Mutation + { + public OffsetDateTime Test(OffsetDateTime arg) + { + return arg + Duration.FromMinutes(10); + } + } + } + + private readonly IRequestExecutor testExecutor; + public OffsetDateTimeTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13+02:30", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T18:30:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to OffsetDateTime", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs new file mode 100644 index 00000000000..1789f6c91e6 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs @@ -0,0 +1,133 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetDateTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public OffsetDate Hours => + new OffsetDate( + LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), + Offset.FromHours(2)); + + public OffsetDate HoursAndMinutes => + new OffsetDate( + LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), + Offset.FromHoursAndMinutes(2, 35)); + } + + public class Mutation + { + public OffsetDate Test(OffsetDate arg) => arg; + } + } + + private readonly IRequestExecutor testExecutor; + public OffsetDateTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to OffsetDate", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs new file mode 100644 index 00000000000..0a617f38a18 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs @@ -0,0 +1,132 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetTimeTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public OffsetTime Hours => + new OffsetTime( + LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100), + Offset.FromHours(2)); + + public OffsetTime HoursAndMinutes => + new OffsetTime( + LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100), + Offset.FromHoursAndMinutes(2, 35)); + } + + public class Mutation + { + public OffsetTime Test(OffsetTime arg) => arg; + } + } + + private readonly IRequestExecutor testExecutor; + public OffsetTimeTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to OffsetTime", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs new file mode 100644 index 00000000000..7337936a45c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs @@ -0,0 +1,126 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class OffsetTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public Offset Hours => Offset.FromHours(2); + public Offset HoursAndMinutes => Offset.FromHoursAndMinutes(2, 35); + } + + public class Mutation + { + public Offset Test(Offset arg) + => arg + Offset.FromHoursAndMinutes(1, 5); + } + } + + private readonly IRequestExecutor testExecutor; + public OffsetTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: hours }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+02", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsWithMinutes() + { + IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+02:35", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:05", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "+02:35") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:40", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValue("arg", "18:30:13+02") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:05", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithMinutes() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"+02:35\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("+03:40", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"18:30:13+02\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs new file mode 100644 index 00000000000..a9e25bd8226 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs @@ -0,0 +1,94 @@ +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class PeriodTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public Period One => Period.FromWeeks(-3) + Period.FromDays(3) + Period.FromTicks(139); + } + + public class Mutation + { + public Period Test(Period arg) + => arg + Period.FromMinutes(-10); + } + } + + private readonly IRequestExecutor testExecutor; + public PeriodTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: one }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-3W3DT139t", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Period!) { test(arg: $arg) }") + .SetVariableValue("arg", "P-3W15DT139t") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-3W15DT-10M139t", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: Period!) { test(arg: $arg) }") + .SetVariableValue("arg", "-3W3DT-10M139t") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"P-3W15DT139t\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("P-3W15DT-10M139t", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"-3W3DT-10M139t\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to Period", queryResult.Errors.First().Message); + } + } +} diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs new file mode 100644 index 00000000000..f028026b074 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs @@ -0,0 +1,138 @@ +using System; +using System.Linq; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Xunit; + +namespace HotChocolate.Types.NodaTime.Tests +{ + public class ZonedDateTimeTypeIntegrationTests + { + public static class Schema + { + public class Query + { + public ZonedDateTime Rome => + new ZonedDateTime( + LocalDateTime.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), + DateTimeZoneProviders.Tzdb["Asia/Kathmandu"], + Offset.FromHoursAndMinutes(5, 45)); + + public ZonedDateTime Utc => + new ZonedDateTime( + LocalDateTime.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)), + DateTimeZoneProviders.Tzdb["UTC"], + Offset.FromHours(0)); + } + + public class Mutation + { + public ZonedDateTime Test(ZonedDateTime arg) + { + return arg + Duration.FromMinutes(10); + } + } + } + + private readonly IRequestExecutor testExecutor; + public ZonedDateTimeTypeIntegrationTests() + { + testExecutor = SchemaBuilder.New() + .AddQueryType() + .AddMutationType() + .AddNodaTime() + .Create() + .MakeExecutable(); + } + + [Fact] + public void QueryReturns() + { + IExecutionResult? result = testExecutor.Execute("query { test: rome }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]); + } + + [Fact] + public void QueryReturnsUtc() + { + IExecutionResult? result = testExecutor.Execute("query { test: utc }"); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T18:30:13 UTC +00", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 Asia/Kathmandu +05:45") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesVariableWithUTC() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 UTC +00") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 UTC +00", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseAnIncorrectVariable() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") + .SetVariableValue("arg", "2020-12-31T19:30:13 UTC") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + } + + [Fact] + public void ParsesLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 Asia/Kathmandu +05:45\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]); + } + + [Fact] + public void ParsesLiteralWithUTC() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 UTC +00\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Equal("2020-12-31T19:40:13 UTC +00", queryResult!.Data!["test"]); + } + + [Fact] + public void DoesntParseIncorrectLiteral() + { + IExecutionResult? result = testExecutor + .Execute(QueryRequestBuilder.New() + .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 UTC\") }") + .Create()); + var queryResult = result as IReadOnlyQueryResult; + Assert.Null(queryResult!.Data); + Assert.Equal(1, queryResult!.Errors!.Count); + Assert.Null(queryResult.Errors.First().Code); + Assert.Equal("Unable to deserialize string to ZonedDateTime", queryResult.Errors.First().Message); + } + } +} diff --git a/website/src/docs/hotchocolate/defining-a-schema/interfaces.md b/website/src/docs/hotchocolate/defining-a-schema/interfaces.md index 02457765e42..c5faa135765 100644 --- a/website/src/docs/hotchocolate/defining-a-schema/interfaces.md +++ b/website/src/docs/hotchocolate/defining-a-schema/interfaces.md @@ -237,7 +237,7 @@ public class Startup > Note: We have to explicitly register the interface implementations: > > ```csharp -> services.AddType() +> services.AddGraphQLServer().AddType() > ``` # Interfaces implementing interfaces @@ -458,7 +458,7 @@ public class Startup > Note: We also have to register the `DatedMessage` interface manually, if we do not expose it through a field directly: > > ```csharp -> services.AddType() +> services.AddGraphQLServer().AddType() > ``` # Dynamic fields diff --git a/website/src/docs/hotchocolate/defining-a-schema/scalars.md b/website/src/docs/hotchocolate/defining-a-schema/scalars.md index 6052ac3a9ce..5a763132602 100644 --- a/website/src/docs/hotchocolate/defining-a-schema/scalars.md +++ b/website/src/docs/hotchocolate/defining-a-schema/scalars.md @@ -176,7 +176,44 @@ dotnet add package HotChocolate.Types.Scalars [3]: https://tools.ietf.org/html/rfc7042#page-19 [4]: https://tools.ietf.org/html/rfc7043 -These additional scalars are not mapped by Hot Chocolate automatically, we need to [specify them manually](/docs/hotchocolate/defining-a-schema/object-types/#explicit-types). +> Note: Most of these scalars are based on a `string`. When returning a `string` from our resolver, we need to specify the scalar type manually. +> +> ```csharp +> [GraphQLType(typeof(EmailAddressType))] +> public string GetEmail() => "test@example.com"; +> ``` +> +> [Learn more about this](/docs/hotchocolate/defining-a-schema/object-types/#explicit-types). + +## NodaTime + +We also offer a package specifically for [NodaTime](https://github.com/nodatime/nodatime). + +It can be installed like the following. + +```bash +dotnet add package HotChocolate.Types.NodaTime +``` + +**Available Scalars:** + +| Type | Description | Example | +| -------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- | +| DateTimeZone | A [NodaTime DateTimeZone](https://nodatime.org/TimeZones) | `"Europe/Rome"` | +| Duration | A [NodaTime Duration](https://nodatime.org/3.0.x/userguide/duration-patterns) | `"-123:07:53:10.019"` | +| Instant | A [NodaTime Instant](https://nodatime.org/3.0.x/userguide/instant-patterns) | `"2020-02-20T17:42:59Z"` | +| IsoDayOfWeek | A [NodaTime IsoDayOfWeek](https://nodatime.org/3.0.x/api/NodaTime.IsoDayOfWeek.html) | `7` | +| LocalDate | A [NodaTime LocalDate](https://nodatime.org/3.0.x/userguide/localdate-patterns) | `"2020-12-25"` | +| LocalDateTime | A [NodaTime LocalDateTime](https://nodatime.org/3.0.x/userguide/localdatetime-patterns) | `"2020-12-25T13:46:78"` | +| LocalTime | A [NodaTime LocalTime](https://nodatime.org/3.0.x/userguide/localtime-patterns) | `"12:42:13.03101"` | +| OffsetDateTime | A [NodaTime OffsetDateTime](https://nodatime.org/3.0.x/userguide/offsetdatetime-patterns) | `"2020-12-25T13:46:78+02:35"` | +| OffsetDate | A [NodaTime OffsetDate](https://nodatime.org/3.0.x/userguide/offsetdate-patterns) | `"2020-12-25+02:35"` | +| OffsetTime | A [NodaTime OffsetTime](https://nodatime.org/3.0.x/userguide/offsettime-patterns) | `"13:46:78+02:35"` | +| Offset | A [NodeTime Offset](https://nodatime.org/3.0.x/userguide/offset-patterns) | `"+02:35"` | +| Period | A [NodeTime Period](https://nodatime.org/3.0.x/userguide/period-patterns) | `"P-3W3DT139t"` | +| ZonedDateTime | A [NodaTime ZonedDateTime](https://nodatime.org/3.0.x/userguide/zoneddatetime-patterns) | `"2020-12-31T19:40:13 Asia/Kathmandu +05:45"` | + +This package was originally developed by [@shoooe](https://github.com/shoooe). # Binding behavior