diff --git a/Fluxera.Temporal.sln b/Fluxera.Temporal.sln new file mode 100644 index 0000000..6b1f610 --- /dev/null +++ b/Fluxera.Temporal.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".items", ".items", "{75AC41E9-2B8B-4D76-A7DF-A18F4FB9D6D6}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + azure-pipelines.yml = azure-pipelines.yml + GitVersion.yml = GitVersion.yml + global.json = global.json + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DF28D730-99B3-4811-AAC7-725A73B3F668}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F18D2D58-282C-4D93-8D9A-3A76CF98F018}" + ProjectSection(SolutionItems) = preProject + tests\Directory.Build.props = tests\Directory.Build.props + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluxera.Temporal", "src\Fluxera.Temporal\Fluxera.Temporal.csproj", "{00724D4C-75EF-4EA2-B5F9-0922B948021B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.Temporal.MongoDB", "src\Fluxera.Temporal.MongoDB\Fluxera.Temporal.MongoDB.csproj", "{D1674A18-F0B2-43EF-9C6B-C541B40A7F73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.Temporal.MongoDB.UnitTests", "tests\Fluxera.Temporal.MongoDB.UnitTests\Fluxera.Temporal.MongoDB.UnitTests.csproj", "{309830E5-238E-4BF4-A9BA-F942D30EE764}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00724D4C-75EF-4EA2-B5F9-0922B948021B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00724D4C-75EF-4EA2-B5F9-0922B948021B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00724D4C-75EF-4EA2-B5F9-0922B948021B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00724D4C-75EF-4EA2-B5F9-0922B948021B}.Release|Any CPU.Build.0 = Release|Any CPU + {D1674A18-F0B2-43EF-9C6B-C541B40A7F73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1674A18-F0B2-43EF-9C6B-C541B40A7F73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1674A18-F0B2-43EF-9C6B-C541B40A7F73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1674A18-F0B2-43EF-9C6B-C541B40A7F73}.Release|Any CPU.Build.0 = Release|Any CPU + {309830E5-238E-4BF4-A9BA-F942D30EE764}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {309830E5-238E-4BF4-A9BA-F942D30EE764}.Debug|Any CPU.Build.0 = Debug|Any CPU + {309830E5-238E-4BF4-A9BA-F942D30EE764}.Release|Any CPU.ActiveCfg = Release|Any CPU + {309830E5-238E-4BF4-A9BA-F942D30EE764}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {00724D4C-75EF-4EA2-B5F9-0922B948021B} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {D1674A18-F0B2-43EF-9C6B-C541B40A7F73} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {309830E5-238E-4BF4-A9BA-F942D30EE764} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D97BF2AF-6E68-4E88-BF70-773A86E26013} + EndGlobalSection +EndGlobal diff --git a/GitVersion.yml b/GitVersion.yml new file mode 100644 index 0000000..0093d88 --- /dev/null +++ b/GitVersion.yml @@ -0,0 +1 @@ +mode: Mainline diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..6262d04 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,58 @@ +# CI pipeline for a NuGet package solution. + +trigger: + branches: + include: [ '*' ] + exclude: [ 'refs/tags/*' ] + +variables: + BuildConfiguration: Release + DotNetCoreVersion: 6.0.201 + +stages: +- stage: BuildAndTest + jobs: + - job: BuildAndTest + pool: + name: Default + steps: + - checkout: self + persistCredentials: 'true' + clean: true + # Install the desired .NET SDK. + - task: UseDotNet@2 + displayName: 'Acquire .NET SDK' + inputs: + packageType: 'sdk' + version: $(DotNetCoreVersion) + includePreviewVersions: false + # Build all projects. + - task: DotNetCoreCLI@2 + displayName: 'Build Projects' + inputs: + projects: '**/*.csproj' + arguments: '--configuration $(BuildConfiguration)' + # Run all available tests. + - task: DotNetCoreCLI@2 + displayName: 'Execute Tests' + inputs: + command: test + projects: '**/*Tests/*.csproj' + arguments: '--configuration $(BuildConfiguration)' + nobuild: true + # Create the NuGet packages. + - task: DotNetCoreCLI@2 + displayName: 'Pack Packages' + inputs: + command: 'pack' + packagesToPack: 'src/**/*.csproj' + nobuild: true + verbosityPack: Minimal + includesymbols: false + # Copy created NuGet packages to the builds artifacts directory. + - task: PublishBuildArtifacts@1 + displayName: 'Publish Package Artifacts' + inputs: + PathtoPublish: '$(Build.ArtifactStagingDirectory)' + ArtifactName: 'drop' + publishLocation: 'Container' \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 0000000..726ce1b --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "6.0.201" + } +} \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..f35c53b Binary files /dev/null and b/icon.png differ diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/src/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..1721285 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,36 @@ + + + + latest + enable + disable + true + true + + + + Fluxera Software Development GmbH + Fluxera Software Foundation + Copyright © 2014-2022 Fluxera Software Development GmbH. All rights reserved. + + + + Matthias Gernand + https://github.com/fluxera/Fluxera.Guard + https://github.com/fluxera/Fluxera.Guard + icon.png + README.md + false + false + en + git + MIT + true + + + + + + + + \ No newline at end of file diff --git a/src/Fluxera.Temporal.MongoDB/ConventionPackExtensions.cs b/src/Fluxera.Temporal.MongoDB/ConventionPackExtensions.cs new file mode 100644 index 0000000..f762335 --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/ConventionPackExtensions.cs @@ -0,0 +1,20 @@ +namespace Fluxera.Temporal.MongoDB +{ + using global::MongoDB.Bson.Serialization.Conventions; + using JetBrains.Annotations; + + [PublicAPI] + public static class ConventionPackExtensions + { + public static ConventionPack UseTemporal(this ConventionPack pack) + { + pack.Add(new DateTimeConvention()); + pack.Add(new DateTimeOffsetConvention()); + pack.Add(new DateOnlyConvention()); + pack.Add(new TimeOnlyConvention()); + pack.Add(new TimeSpanConvention()); + + return pack; + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateOnlyConvention.cs b/src/Fluxera.Temporal.MongoDB/DateOnlyConvention.cs new file mode 100644 index 0000000..8673ff7 --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateOnlyConvention.cs @@ -0,0 +1,29 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using Fluxera.Utilities.Extensions; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using global::MongoDB.Bson.Serialization.Serializers; + + internal sealed class DateOnlyConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = originalMemberType.UnwrapNullableType(); + + if(memberType == typeof(DateOnly)) + { + DateOnlySerializer dateOnlySerializer = new DateOnlySerializer(); + + IBsonSerializer serializer = originalMemberType.IsNullable() + ? new NullableSerializer(dateOnlySerializer) + : dateOnlySerializer; + + memberMap.SetSerializer(serializer); + } + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeOffsetSerializer.cs b/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeOffsetSerializer.cs new file mode 100644 index 0000000..73afcce --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeOffsetSerializer.cs @@ -0,0 +1,31 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Serializers; + using JetBrains.Annotations; + + [PublicAPI] + public class DateOnlyDateTimeOffsetSerializer : DateTimeOffsetSerializer + { + private readonly DateTimeSerializer dateTimeSerializer; + + public DateOnlyDateTimeOffsetSerializer(DateTimeSerializer dateTimeSerializer) + { + this.dateTimeSerializer = dateTimeSerializer; + } + + /// + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTimeOffset value) + { + this.dateTimeSerializer.Serialize(context, args, value.Date); + } + + /// + public override DateTimeOffset Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + DateTimeOffset dateTimeOffset = this.dateTimeSerializer.Deserialize(context, args); + return dateTimeOffset.Date; + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeSerializer.cs b/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeSerializer.cs new file mode 100644 index 0000000..9b8434f --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateOnlyDateTimeSerializer.cs @@ -0,0 +1,31 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Serializers; + using JetBrains.Annotations; + + [PublicAPI] + public class DateOnlyDateTimeSerializer : DateTimeSerializer + { + private readonly DateTimeSerializer dateTimeSerializer; + + public DateOnlyDateTimeSerializer(DateTimeSerializer dateTimeSerializer) + { + this.dateTimeSerializer = dateTimeSerializer; + } + + /// + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value) + { + this.dateTimeSerializer.Serialize(context, args, value.Date); + } + + /// + public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + DateTime dateTime = this.dateTimeSerializer.Deserialize(context, args); + return dateTime.Date; + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateOnlySerializer.cs b/src/Fluxera.Temporal.MongoDB/DateOnlySerializer.cs new file mode 100644 index 0000000..f1cc6b7 --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateOnlySerializer.cs @@ -0,0 +1,21 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Serializers; + using JetBrains.Annotations; + + [PublicAPI] + public class DateOnlySerializer : StructSerializerBase + { + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateOnly value) + { + context.Writer.WriteString(value.ToString("yyyy-MM-dd")); + } + + public override DateOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + return DateOnly.ParseExact(context.Reader.ReadString(), "yyyy-MM-dd", null); + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateTimeConvention.cs b/src/Fluxera.Temporal.MongoDB/DateTimeConvention.cs new file mode 100644 index 0000000..d35113a --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateTimeConvention.cs @@ -0,0 +1,44 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using System.Reflection; + using Fluxera.ComponentModel.Annotations; + using Fluxera.Utilities.Extensions; + using global::MongoDB.Bson; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using global::MongoDB.Bson.Serialization.Serializers; + + internal sealed class DateTimeConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = Nullable.GetUnderlyingType(originalMemberType) ?? originalMemberType; + MemberInfo member = memberMap.MemberInfo; + + if(memberType == typeof(DateTime)) + { + bool dateOnly = member.IsDefined(typeof(DateOnlyAttribute)); + + BsonType representation = dateOnly ? BsonType.String : BsonType.Document; + + DateTimeSerializer dateTimeSerializer = DateTimeSerializer.UtcInstance + .WithRepresentation(representation) + .WithDateOnly(dateOnly); + + if(dateOnly) + { + dateTimeSerializer = new DateOnlyDateTimeSerializer(dateTimeSerializer); + } + + IBsonSerializer serializer = originalMemberType.IsNullable() + ? new NullableSerializer(dateTimeSerializer) + : dateTimeSerializer; + + memberMap.SetSerializer(serializer); + } + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/DateTimeOffsetConvention.cs b/src/Fluxera.Temporal.MongoDB/DateTimeOffsetConvention.cs new file mode 100644 index 0000000..4123fae --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/DateTimeOffsetConvention.cs @@ -0,0 +1,46 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using System.Reflection; + using Fluxera.ComponentModel.Annotations; + using Fluxera.Utilities.Extensions; + using global::MongoDB.Bson; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using global::MongoDB.Bson.Serialization.Serializers; + + internal sealed class DateTimeOffsetConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = originalMemberType.UnwrapNullableType(); + MemberInfo member = memberMap.MemberInfo; + + if(memberType == typeof(DateTimeOffset)) + { + bool dateOnly = member.IsDefined(typeof(DateOnlyAttribute)); + + BsonType representation = dateOnly ? BsonType.String : BsonType.Document; + + DateTimeOffsetSerializer dateTimeOffsetSerializer = new DateTimeOffsetSerializer(representation); + + if(dateOnly) + { + DateTimeSerializer dateTimeSerializer = DateTimeSerializer.UtcInstance + .WithRepresentation(representation) + .WithDateOnly(true); + + dateTimeOffsetSerializer = new DateOnlyDateTimeOffsetSerializer(dateTimeSerializer); + } + + IBsonSerializer serializer = originalMemberType.IsNullable() + ? new NullableSerializer(dateTimeOffsetSerializer) + : dateTimeOffsetSerializer; + + memberMap.SetSerializer(serializer); + } + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/Fluxera.Temporal.MongoDB.csproj b/src/Fluxera.Temporal.MongoDB/Fluxera.Temporal.MongoDB.csproj new file mode 100644 index 0000000..6b50bf0 --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/Fluxera.Temporal.MongoDB.csproj @@ -0,0 +1,38 @@ + + + + net6.0 + + + + Fluxera.Temporal.MongoDB + A libary that provides serializer support for MongoDB for temporal types. + fluxera;library;temporal,json + + + + + true + \ + + + true + \ + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + \ No newline at end of file diff --git a/src/Fluxera.Temporal.MongoDB/TimeOnlyConvention.cs b/src/Fluxera.Temporal.MongoDB/TimeOnlyConvention.cs new file mode 100644 index 0000000..d863bfc --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/TimeOnlyConvention.cs @@ -0,0 +1,29 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using Fluxera.Utilities.Extensions; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using global::MongoDB.Bson.Serialization.Serializers; + + internal sealed class TimeOnlyConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = originalMemberType.UnwrapNullableType(); + + if(memberType == typeof(TimeOnly)) + { + TimeOnlySerializer timeOnlySerializer = new TimeOnlySerializer(); + + IBsonSerializer serializer = originalMemberType.IsNullable() + ? new NullableSerializer(timeOnlySerializer) + : timeOnlySerializer; + + memberMap.SetSerializer(serializer); + } + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/TimeOnlySerializer.cs b/src/Fluxera.Temporal.MongoDB/TimeOnlySerializer.cs new file mode 100644 index 0000000..0531b46 --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/TimeOnlySerializer.cs @@ -0,0 +1,24 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Serializers; + using JetBrains.Annotations; + + [PublicAPI] + public class TimeOnlySerializer : StructSerializerBase + { + private readonly TimeSpanSerializer timeSpanSerializer = new TimeSpanSerializer(); + + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TimeOnly value) + { + this.timeSpanSerializer.Serialize(context, args, value.ToTimeSpan()); + } + + public override TimeOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + TimeSpan timeSpan = this.timeSpanSerializer.Deserialize(context, args); + return TimeOnly.FromTimeSpan(timeSpan); + } + } +} diff --git a/src/Fluxera.Temporal.MongoDB/TimeSpanConvention.cs b/src/Fluxera.Temporal.MongoDB/TimeSpanConvention.cs new file mode 100644 index 0000000..253110f --- /dev/null +++ b/src/Fluxera.Temporal.MongoDB/TimeSpanConvention.cs @@ -0,0 +1,29 @@ +namespace Fluxera.Temporal.MongoDB +{ + using System; + using Fluxera.Utilities.Extensions; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using global::MongoDB.Bson.Serialization.Serializers; + + internal sealed class TimeSpanConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = originalMemberType.UnwrapNullableType(); + + if(memberType == typeof(TimeSpan)) + { + TimeSpanSerializer timeSpanSerializer = new TimeSpanSerializer(); + + IBsonSerializer serializer = originalMemberType.IsNullable() + ? new NullableSerializer(timeSpanSerializer) + : timeSpanSerializer; + + memberMap.SetSerializer(serializer); + } + } + } +} diff --git a/src/Fluxera.Temporal/Fluxera.Temporal.csproj b/src/Fluxera.Temporal/Fluxera.Temporal.csproj new file mode 100644 index 0000000..0a967a0 --- /dev/null +++ b/src/Fluxera.Temporal/Fluxera.Temporal.csproj @@ -0,0 +1,34 @@ + + + + net6.0 + + + + Fluxera.Temporal + A libary that provides temporal types. + fluxera;library;temporal + + + + + true + \ + + + true + \ + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + \ No newline at end of file diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/tests/.gitkeep @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 0000000..28e17f1 --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,17 @@ + + + + latest + enable + disable + false + false + + + + Fluxera Software Development GmbH + Fluxera Software Foundation + Copyright © 2014-2022 Fluxera Software Development GmbH. All rights reserved. + + + \ No newline at end of file diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeOffsetTests.cs b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeOffsetTests.cs new file mode 100644 index 0000000..2ca05b5 --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeOffsetTests.cs @@ -0,0 +1,56 @@ +namespace Fluxera.Temporal.MongoDB.UnitTests +{ + using System; + using FluentAssertions; + using Fluxera.ComponentModel.Annotations; + using global::MongoDB.Bson; + using global::MongoDB.Bson.IO; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using NUnit.Framework; + + [TestFixture] + public class DateOnlyDateTimeOffsetTests : TestsBase + { + [SetUp] + public void SetUp() + { + ConventionPack pack = new ConventionPack(); + pack.UseTemporal(); + ConventionRegistry.Register("ConventionPack", pack, t => true); + } + + private class TestClass + { + [DateOnly] + public DateTimeOffset Property { get; set; } + } + + [Test] + public void ShouldDeserialize() + { + string json = @"{""Property"" : ""2022-04-01""}"; + TestClass result = BsonSerializer.Deserialize(json); + + result.Should().NotBeNull(); + result.Property.Should().Be(new DateTime(2022, 4, 1)); + } + + [Test] + public void ShouldSerialize() + { + TestClass obj = new TestClass + { + Property = new DateTimeOffset(2022, 4, 1, 10, 0, 0, TimeSpan.FromHours(1)) + }; + + string json = obj.ToJson(new JsonWriterSettings + { + Indent = true + }); + + Console.WriteLine(json); + json.Should().Contain(@"""2022-04-01"""); + } + } +} diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeTests.cs b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeTests.cs new file mode 100644 index 0000000..362e2fb --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyDateTimeTests.cs @@ -0,0 +1,56 @@ +namespace Fluxera.Temporal.MongoDB.UnitTests +{ + using System; + using FluentAssertions; + using Fluxera.ComponentModel.Annotations; + using global::MongoDB.Bson; + using global::MongoDB.Bson.IO; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using NUnit.Framework; + + [TestFixture] + public class DateOnlyDateTimeTests : TestsBase + { + [SetUp] + public void SetUp() + { + ConventionPack pack = new ConventionPack(); + pack.UseTemporal(); + ConventionRegistry.Register("ConventionPack", pack, t => true); + } + + private class TestClass + { + [DateOnly] + public DateTime Property { get; set; } + } + + [Test] + public void ShouldDeserialize() + { + string json = @"{""Property"" : ""2022-04-01""}"; + TestClass result = BsonSerializer.Deserialize(json); + + result.Should().NotBeNull(); + result.Property.Should().Be(new DateTime(2022, 4, 1)); + } + + [Test] + public void ShouldSerialize() + { + TestClass obj = new TestClass + { + Property = new DateTime(2022, 4, 1, 10, 0, 0) + }; + + string json = obj.ToJson(new JsonWriterSettings + { + Indent = true + }); + + Console.WriteLine(json); + json.Should().Contain(@"""2022-04-01"""); + } + } +} diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyTests.cs b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyTests.cs new file mode 100644 index 0000000..d5fc8b4 --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/DateOnlyTests.cs @@ -0,0 +1,54 @@ +namespace Fluxera.Temporal.MongoDB.UnitTests +{ + using System; + using FluentAssertions; + using global::MongoDB.Bson; + using global::MongoDB.Bson.IO; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using NUnit.Framework; + + [TestFixture] + public class DateOnlyTests : TestsBase + { + [SetUp] + public void SetUp() + { + ConventionPack pack = new ConventionPack(); + pack.UseTemporal(); + ConventionRegistry.Register("ConventionPack", pack, t => true); + } + + private class TestClass + { + public DateOnly Property { get; set; } + } + + [Test] + public void ShouldDeserialize() + { + string json = @"{""Property"" : ""2022-04-01""}"; + TestClass result = BsonSerializer.Deserialize(json); + + result.Should().NotBeNull(); + result.Property.Should().Be(new DateOnly(2022, 4, 1)); + } + + [Test] + public void ShouldSerialize() + { + TestClass obj = new TestClass + { + Property = new DateOnly(2022, 4, 1), + }; + + string json = obj.ToJson(new JsonWriterSettings + { + Indent = true + }); + + Console.WriteLine(json); + json.Should().Contain(@"""2022-04-01"""); + } + } +} diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/Fluxera.Temporal.MongoDB.UnitTests.csproj b/tests/Fluxera.Temporal.MongoDB.UnitTests/Fluxera.Temporal.MongoDB.UnitTests.csproj new file mode 100644 index 0000000..21769b2 --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/Fluxera.Temporal.MongoDB.UnitTests.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + enable + + false + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/TestsBase.cs b/tests/Fluxera.Temporal.MongoDB.UnitTests/TestsBase.cs new file mode 100644 index 0000000..6d2b341 --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/TestsBase.cs @@ -0,0 +1,12 @@ +namespace Fluxera.Temporal.MongoDB.UnitTests +{ + using System.Text.RegularExpressions; + + public abstract class TestsBase + { + protected string Minify(string json) + { + return Regex.Replace(json, @"(""(?:[^""\\]|\\.)*"")|\s+", "$1", RegexOptions.Compiled); + } + } +} diff --git a/tests/Fluxera.Temporal.MongoDB.UnitTests/TimeOnlyTests.cs b/tests/Fluxera.Temporal.MongoDB.UnitTests/TimeOnlyTests.cs new file mode 100644 index 0000000..e123332 --- /dev/null +++ b/tests/Fluxera.Temporal.MongoDB.UnitTests/TimeOnlyTests.cs @@ -0,0 +1,54 @@ +namespace Fluxera.Temporal.MongoDB.UnitTests +{ + using System; + using FluentAssertions; + using global::MongoDB.Bson; + using global::MongoDB.Bson.IO; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using NUnit.Framework; + + [TestFixture] + public class TimeOnlyTests : TestsBase + { + [SetUp] + public void SetUp() + { + ConventionPack pack = new ConventionPack(); + pack.UseTemporal(); + ConventionRegistry.Register("ConventionPack", pack, t => true); + } + + private class TestClass + { + public TimeOnly Property { get; set; } + } + + [Test] + public void ShouldDeserialize() + { + string json = @"{""Property"" : ""10:45:35.8471835""}"; + TestClass result = BsonSerializer.Deserialize(json); + + result.Should().NotBeNull(); + result.Property.Should().Be(TimeOnly.Parse("10:45:35.8471835")); + } + + [Test] + public void ShouldSerialize() + { + TestClass obj = new TestClass + { + Property = TimeOnly.Parse("10:45:35.8471835") + }; + + string json = obj.ToJson(new JsonWriterSettings + { + Indent = true + }); + + Console.WriteLine(json); + json.Should().Contain(@"""10:45:35.8471835"""); + } + } +}