From ea9d45b27eb7c94af6e6635167e6115a47fa3537 Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Mon, 6 May 2024 11:13:28 +0200 Subject: [PATCH] added Akka.NET messages --- Directory.Packages.props | 1 + DrawTogether.sln | 14 ++++++ src/DrawTogether.Actors/Class1.cs | 43 +++++++++++++++++++ .../DrawTogether.Actors.csproj | 17 ++++++++ src/DrawTogether.Entities/CommandResult.cs | 21 +++++++++ .../DrawTogether.Entities.csproj | 4 -- .../Drawings/DrawingSessionId.cs | 3 ++ .../Drawings/DrawingSessionState.cs | 12 ++++++ .../Messages/DrawingSessionCommands.cs | 18 ++++++++ .../Drawings/Messages/DrawingSessionEvents.cs | 25 +++++++++++ .../Messages/DrawingSessionQueries.cs | 14 ++++++ .../Messages/IWithDrawingSessionId.cs | 11 +++++ src/DrawTogether.Entities/Users/UserId.cs | 3 ++ 13 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 src/DrawTogether.Actors/Class1.cs create mode 100644 src/DrawTogether.Actors/DrawTogether.Actors.csproj create mode 100644 src/DrawTogether.Entities/CommandResult.cs create mode 100644 src/DrawTogether.Entities/Drawings/DrawingSessionId.cs create mode 100644 src/DrawTogether.Entities/Drawings/DrawingSessionState.cs create mode 100644 src/DrawTogether.Entities/Drawings/Messages/DrawingSessionCommands.cs create mode 100644 src/DrawTogether.Entities/Drawings/Messages/DrawingSessionEvents.cs create mode 100644 src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs create mode 100644 src/DrawTogether.Entities/Drawings/Messages/IWithDrawingSessionId.cs create mode 100644 src/DrawTogether.Entities/Users/UserId.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index d9dea52..051ddfe 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,7 @@ + diff --git a/DrawTogether.sln b/DrawTogether.sln index 3f427ea..93503e0 100644 --- a/DrawTogether.sln +++ b/DrawTogether.sln @@ -25,6 +25,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastru EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawTogether.Entities", "src\DrawTogether.Entities\DrawTogether.Entities.csproj", "{7F01390E-AF15-442C-831C-C574D6AAFC1F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DrawTogether.Actors", "src\DrawTogether.Actors\DrawTogether.Actors.csproj", "{5863DD74-00DE-4DE9-9F0A-FCBC32195E78}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -95,6 +97,18 @@ Global {7F01390E-AF15-442C-831C-C574D6AAFC1F}.Release|x64.Build.0 = Release|Any CPU {7F01390E-AF15-442C-831C-C574D6AAFC1F}.Release|x86.ActiveCfg = Release|Any CPU {7F01390E-AF15-442C-831C-C574D6AAFC1F}.Release|x86.Build.0 = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|x64.ActiveCfg = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|x64.Build.0 = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|x86.ActiveCfg = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Debug|x86.Build.0 = Debug|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|Any CPU.Build.0 = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|x64.ActiveCfg = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|x64.Build.0 = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|x86.ActiveCfg = Release|Any CPU + {5863DD74-00DE-4DE9-9F0A-FCBC32195E78}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DrawTogether.Actors/Class1.cs b/src/DrawTogether.Actors/Class1.cs new file mode 100644 index 0000000..ba46e93 --- /dev/null +++ b/src/DrawTogether.Actors/Class1.cs @@ -0,0 +1,43 @@ +using Akka.Actor; +using Akka.Cluster.Sharding; + +namespace DrawTogether.Actors; + +/// +/// A generic "child per entity" parent actor. +/// +/// +/// Intended for simplifying unit tests where we don't want to use Akka.Cluster.Sharding. +/// +public sealed class GenericChildPerEntityParent : UntypedActor +{ + public static Props Props(IMessageExtractor extractor, Func propsFactory) + { + return Akka.Actor.Props.Create(() => new GenericChildPerEntityParent(extractor, propsFactory)); + } + + /* + * Re-use Akka.Cluster.Sharding's infrastructure here to keep things simple. + */ + private readonly IMessageExtractor _extractor; + private readonly Func _propsFactory; + + public GenericChildPerEntityParent(IMessageExtractor extractor, Func propsFactory) + { + _extractor = extractor; + _propsFactory = propsFactory; + } + + protected override void OnReceive(object message) + { + var result = _extractor.EntityId(message); + if (result is null) + { + Unhandled(message); + return; + } + + Context.Child(result).GetOrElse(() => Context.ActorOf(_propsFactory(result), result)) + .Forward(_extractor.EntityMessage(message)); + } +} \ No newline at end of file diff --git a/src/DrawTogether.Actors/DrawTogether.Actors.csproj b/src/DrawTogether.Actors/DrawTogether.Actors.csproj new file mode 100644 index 0000000..a235903 --- /dev/null +++ b/src/DrawTogether.Actors/DrawTogether.Actors.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/src/DrawTogether.Entities/CommandResult.cs b/src/DrawTogether.Entities/CommandResult.cs new file mode 100644 index 0000000..d09e988 --- /dev/null +++ b/src/DrawTogether.Entities/CommandResult.cs @@ -0,0 +1,21 @@ +namespace DrawTogether.Entities; + +public enum ResultCode +{ + Ok, + NoOp, + BadRequest, + Unauthorized, + TimeOut +} + +public sealed record CommandResult +{ + public ResultCode Code { get; init; } + + public string? Message { get; init; } + + public static CommandResult Ok() => new() { Code = ResultCode.Ok }; + + public bool IsError => Code != ResultCode.Ok && Code != ResultCode.NoOp; +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/DrawTogether.Entities.csproj b/src/DrawTogether.Entities/DrawTogether.Entities.csproj index 5d30217..3a63532 100644 --- a/src/DrawTogether.Entities/DrawTogether.Entities.csproj +++ b/src/DrawTogether.Entities/DrawTogether.Entities.csproj @@ -6,8 +6,4 @@ enable - - - - diff --git a/src/DrawTogether.Entities/Drawings/DrawingSessionId.cs b/src/DrawTogether.Entities/Drawings/DrawingSessionId.cs new file mode 100644 index 0000000..32ca76d --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/DrawingSessionId.cs @@ -0,0 +1,3 @@ +namespace DrawTogether.Entities.Drawings; + +public sealed class DrawingSessionId(string SessionId); \ No newline at end of file diff --git a/src/DrawTogether.Entities/Drawings/DrawingSessionState.cs b/src/DrawTogether.Entities/Drawings/DrawingSessionState.cs new file mode 100644 index 0000000..28d0292 --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/DrawingSessionState.cs @@ -0,0 +1,12 @@ +using System.Collections.Immutable; +using DrawTogether.Entities.Drawings.Messages; +using DrawTogether.Entities.Users; + +namespace DrawTogether.Entities.Drawings; + +public sealed record DrawingSessionState(DrawingSessionId DrawingSessionId) : IWithDrawingSessionId +{ + public ImmutableDictionary Strokes { get; init; } = ImmutableDictionary.Empty; + + public ImmutableHashSet ConnectedUsers { get; init; } = ImmutableHashSet.Empty; +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionCommands.cs b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionCommands.cs new file mode 100644 index 0000000..382ca74 --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionCommands.cs @@ -0,0 +1,18 @@ +using DrawTogether.Entities.Users; + +namespace DrawTogether.Entities.Drawings.Messages; + +public interface IDrawingSessionCommand : IWithDrawingSessionId{ } + +public static class DrawingSessionCommands +{ + public sealed record AddStroke(DrawingSessionId DrawingSessionId, ConnectedStroke Stroke) : IDrawingSessionCommand; + + public sealed record RemoveStroke(DrawingSessionId DrawingSessionId, StrokeId StrokeId) : IDrawingSessionCommand; + + public sealed record ClearStrokes(DrawingSessionId DrawingSessionId) : IDrawingSessionCommand; + + public sealed record AddUser(DrawingSessionId DrawingSessionId, UserId UserId) : IDrawingSessionCommand; + + public sealed record RemoveUser(DrawingSessionId DrawingSessionId, UserId UserId) : IDrawingSessionCommand; +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionEvents.cs b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionEvents.cs new file mode 100644 index 0000000..0debaa7 --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionEvents.cs @@ -0,0 +1,25 @@ +using DrawTogether.Entities.Users; + +namespace DrawTogether.Entities.Drawings.Messages; + +public interface IDrawingSessionEvent : IWithDrawingSessionId { } + +public static class DrawingSessionEvents +{ + public sealed record DrawingSessionCreated(DrawingSessionId DrawingSessionId) : IDrawingSessionEvent; + + public sealed record StrokeAdded(DrawingSessionId DrawingSessionId, ConnectedStroke Stroke) : IDrawingSessionEvent; + + public sealed record StrokeRemoved(DrawingSessionId DrawingSessionId, StrokeId StrokeId) : IDrawingSessionEvent; + + public sealed record StrokesCleared(DrawingSessionId DrawingSessionId) : IDrawingSessionEvent; + + public sealed record UserAdded(DrawingSessionId DrawingSessionId, UserId UserId) : IDrawingSessionEvent; + + public sealed record UserRemoved(DrawingSessionId DrawingSessionId, UserId UserId) : IDrawingSessionEvent; + + /// + /// Occurs when the last user leaves the session. + /// + public sealed record DrawingSessionClosed(DrawingSessionId DrawingSessionId) : IDrawingSessionEvent; +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs new file mode 100644 index 0000000..2cd82b2 --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/Messages/DrawingSessionQueries.cs @@ -0,0 +1,14 @@ +namespace DrawTogether.Entities.Drawings.Messages; + +public interface IDrawingSessionQuery : IWithDrawingSessionId { } + +public static class DrawingSessionQueries +{ + public sealed record GetDrawingSessionState(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; + + public sealed record GetDrawingSessionUsers(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; + + public sealed record SubscribeToDrawingSession(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; + + public sealed record UnsubscribeFromDrawingSession(DrawingSessionId DrawingSessionId) : IDrawingSessionQuery; +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/Drawings/Messages/IWithDrawingSessionId.cs b/src/DrawTogether.Entities/Drawings/Messages/IWithDrawingSessionId.cs new file mode 100644 index 0000000..67d4163 --- /dev/null +++ b/src/DrawTogether.Entities/Drawings/Messages/IWithDrawingSessionId.cs @@ -0,0 +1,11 @@ +using DrawTogether.Entities.Users; + +namespace DrawTogether.Entities.Drawings.Messages; + +/// +/// Marker interface for messages that have a . +/// +public interface IWithDrawingSessionId +{ + DrawingSessionId DrawingSessionId { get; } +} \ No newline at end of file diff --git a/src/DrawTogether.Entities/Users/UserId.cs b/src/DrawTogether.Entities/Users/UserId.cs new file mode 100644 index 0000000..493abd3 --- /dev/null +++ b/src/DrawTogether.Entities/Users/UserId.cs @@ -0,0 +1,3 @@ +namespace DrawTogether.Entities.Users; + +public sealed class UserId(string IdentityName); \ No newline at end of file