Skip to content

Commit

Permalink
Feat/subscriptions (#25)
Browse files Browse the repository at this point in the history
* Subscriptions

* Subscriptions, watches & more

- special type of event for subscriptions/watches: notification
- watch early implementation
- frontend json serialization enhancements
- compound request

* CamelCase for frontend

* CompoundRequest and CompoundResponse on frontend

* Embedded behaviors

* Watches & more

* 0.11.3-alpha
  • Loading branch information
mikolaj-milewski authored Apr 17, 2024
1 parent 8f24a84 commit 23b5401
Show file tree
Hide file tree
Showing 300 changed files with 4,720 additions and 1,525 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,5 @@ Transport/@stateflows/http-client/dist/
Examples/SignalR/SignalR.Client/ClientApp/.angular/
*.txt
Publish.ps1
/Transport/@stateflows/common/dist
/Transport/@stateflows/http-client/dist
5 changes: 5 additions & 0 deletions Core/Stateflows.Common/Activities/Classes/ActivityId.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Newtonsoft.Json;
using Stateflows.Common.Exceptions;
using Stateflows.Common.Utilities;

Expand Down Expand Up @@ -27,6 +28,10 @@ public ActivityId(BehaviorId id)

public string Instance { get; set; }

[JsonIgnore]
public readonly ActivityClass ActivityClass => new ActivityClass(Name);

[JsonIgnore]
public readonly BehaviorId BehaviorId => new BehaviorId(BehaviorType.Activity, Name, Instance);

public static bool operator ==(ActivityId id1, ActivityId id2)
Expand Down
25 changes: 21 additions & 4 deletions Core/Stateflows.Common/Activities/Classes/ActivityWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Linq;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Stateflows.Activities;
Expand Down Expand Up @@ -28,15 +28,32 @@ public Task<RequestResult<ExecutionResponse>> ExecuteAsync(InitializationRequest
return Behavior.RequestAsync(executionRequest);
}

public Task<RequestResult<CancelResponse>> CancelAsync()
=> RequestAsync(new CancelRequest());

public Task<SendResult> SendAsync<TEvent>(TEvent @event)
where TEvent : Event, new()
=> Behavior.SendAsync(@event);

public Task<RequestResult<TResponse>> RequestAsync<TResponse>(Request<TResponse> request)
where TResponse : Response, new()
=> Behavior.RequestAsync(request);

public Task WatchAsync<TNotification>(Action<TNotification> handler)
where TNotification : Notification, new()
=> Behavior.WatchAsync<TNotification>(handler);

public Task UnwatchAsync<TNotification>()
where TNotification : Notification, new()
=> Behavior.UnwatchAsync<TNotification>();

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
=> Behavior.Dispose();

~ActivityWrapper()
=> Dispose(false);
}
}
7 changes: 0 additions & 7 deletions Core/Stateflows.Common/Activities/Events/CancelRequest.cs

This file was deleted.

9 changes: 0 additions & 9 deletions Core/Stateflows.Common/Activities/Events/CancelResponse.cs

This file was deleted.

2 changes: 0 additions & 2 deletions Core/Stateflows.Common/Activities/Interfaces/IActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,5 @@ namespace Stateflows.Activities
public interface IActivity : IBehavior
{
Task<RequestResult<ExecutionResponse>> ExecuteAsync(InitializationRequest initializationRequest = null, IEnumerable<Token> inputTokens = null);

Task<RequestResult<CancelResponse>> CancelAsync();
}
}
8 changes: 4 additions & 4 deletions Core/Stateflows.Common/Classes/RequestResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ public class RequestResult<TResponse> : SendResult
[JsonConstructor]
protected RequestResult() : base() { }

public RequestResult(Request<TResponse> request, EventStatus status, EventValidation requestValidation)
: base(request, status, requestValidation)
public RequestResult(Request<TResponse> request, EventStatus status, EventValidation validation = null)
: base(request, status, validation)
{
Response = request.Response;
}
Expand All @@ -19,8 +19,8 @@ public RequestResult(Request<TResponse> request, EventStatus status, EventValida

public class RequestResult : SendResult
{
public RequestResult(Event request, Response response, EventStatus status, EventValidation requestValidation)
: base(request, status, requestValidation)
public RequestResult(Event request, Response response, EventStatus status, EventValidation validation = null)
: base(request, status, validation)
{
Response = response;
}
Expand Down
74 changes: 73 additions & 1 deletion Core/Stateflows.Common/Context/StateflowsContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.Generic;

namespace Stateflows.Common.Context
{
Expand All @@ -21,6 +21,78 @@ public bool ShouldSerializePendingTimeEvents()

public Dictionary<Guid, TimeEvent> PendingTimeEvents { get; set; } = new Dictionary<Guid, TimeEvent>();

public Dictionary<BehaviorId, List<string>> Subscriptions { get; set; } = new Dictionary<BehaviorId, List<string>>();

public bool AddSubscription(BehaviorId subscribeeBehaviorId, string eventName)
{
if (!Subscriptions.TryGetValue(subscribeeBehaviorId, out var eventNames))
{
eventNames = new List<string>();
Subscriptions[subscribeeBehaviorId] = eventNames;
}

if (!eventNames.Contains(eventName))
{
eventNames.Add(eventName);

return true;
}

return false;
}

public bool RemoveSubscription(BehaviorId subscribeeBehaviorId, string eventName)
{
if (
Subscriptions.TryGetValue(subscribeeBehaviorId, out var eventNames) &&
eventNames.Contains(eventName)
)
{
eventNames.Remove(eventName);

return true;
}

return false;
}

public Dictionary<string, List<BehaviorId>> Subscribers { get; set; } = new Dictionary<string, List<BehaviorId>>();

public bool AddSubscribers(BehaviorId subscriberBehaviorId, IEnumerable<string> notificationNames)
{
foreach (var notificationName in notificationNames)
{
if (!Subscribers.TryGetValue(notificationName, out var behaviorIds))
{
behaviorIds = new List<BehaviorId>();
Subscribers[notificationName] = behaviorIds;
}

if (!behaviorIds.Contains(subscriberBehaviorId))
{
behaviorIds.Add(subscriberBehaviorId);
}
}

return true;
}

public bool RemoveSubscribers(BehaviorId subscriberBehaviorId, IEnumerable<string> notificationNames)
{
foreach (var notificationName in notificationNames)
{
if (
Subscribers.TryGetValue(notificationName, out var behaviorIds) &&
behaviorIds.Contains(subscriberBehaviorId)
)
{
behaviorIds.Remove(subscriberBehaviorId);
}
}

return true;
}

public bool ShouldSerializeValues()
=> Values.Any();

Expand Down
6 changes: 5 additions & 1 deletion Core/Stateflows.Common/Enums/EventStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public enum EventStatus
/// <summary>
/// Event was omitted because other events in CompoundRequest were invalid
/// </summary>
Omitted
Omitted,
/// <summary>
/// Event was forwarded to embedded behavior
/// </summary>
Forwarded
}
}
13 changes: 13 additions & 0 deletions Core/Stateflows.Common/Events/BehaviorStatusNotification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Stateflows.Common
{
public class BehaviorStatusNotification : Notification
{
public BehaviorStatus BehaviorStatus { get; set; }

[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<string> ExpectedEvents { get; set; }
}
}
8 changes: 7 additions & 1 deletion Core/Stateflows.Common/Events/BehaviorStatusResponse.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
namespace Stateflows.Common
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Stateflows.Common
{
public class BehaviorStatusResponse : Response
{
public override string Name => nameof(BehaviorStatusResponse);

public BehaviorStatus BehaviorStatus { get; set; }

[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public IEnumerable<string> ExpectedEvents { get; set; }
}
}
5 changes: 5 additions & 0 deletions Core/Stateflows.Common/Events/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Stateflows.Common
{
public abstract class Command : Event
{ }
}
2 changes: 2 additions & 0 deletions Core/Stateflows.Common/Events/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public class Event : Token
{
[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public List<EventHeader> Headers { get; set; } = new List<EventHeader>();

public DateTime SentAt { get; set; }
}

public class Event<TPayload> : Event
Expand Down
4 changes: 1 addition & 3 deletions Core/Stateflows.Common/Events/EventValidation.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Stateflows.Common.Extensions;

namespace Stateflows.Common
{
Expand Down
5 changes: 5 additions & 0 deletions Core/Stateflows.Common/Events/FinalizationRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Stateflows.Common
{
public sealed class FinalizationRequest : Request<FinalizationResponse>
{ }
}
7 changes: 7 additions & 0 deletions Core/Stateflows.Common/Events/FinalizationResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Stateflows.Common
{
public sealed class FinalizationResponse : Response
{
public bool FinalizationSuccessful { get; set; }
}
}
17 changes: 17 additions & 0 deletions Core/Stateflows.Common/Events/Notification.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Stateflows.Common
{
public class Notification : Event
{
public BehaviorId SenderId { get; set; }
}

public class Notification<TPayload> : Notification
{
public Notification()
{
Payload = default;
}

public TPayload Payload { get; set; }
}
}
5 changes: 5 additions & 0 deletions Core/Stateflows.Common/Events/NotificationsRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Stateflows.Common
{
public sealed class NotificationsRequest : Request<NotificationsResponse>
{ }
}
5 changes: 5 additions & 0 deletions Core/Stateflows.Common/Events/NotificationsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Stateflows.Common
{
public sealed class NotificationsResponse : Response
{ }
}
4 changes: 3 additions & 1 deletion Core/Stateflows.Common/Events/ResetRequest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Stateflows.Common
{
public class ResetRequest : Request<ResetResponse>
{ }
{
public bool KeepVersion { get; set; } = false;
}
}
13 changes: 13 additions & 0 deletions Core/Stateflows.Common/Events/SubscriptionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Stateflows.Common
{
public sealed class SubscriptionRequest : Request<SubscriptionResponse>
{
public BehaviorId BehaviorId { get; set; }

[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public List<string> NotificationNames { get; set; } = new List<string>();
}
}
7 changes: 7 additions & 0 deletions Core/Stateflows.Common/Events/SubscriptionResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Stateflows.Common
{
public sealed class SubscriptionResponse : Response
{
public bool SubscriptionSuccessful { get; set; }
}
}
13 changes: 13 additions & 0 deletions Core/Stateflows.Common/Events/UnsubscriptionRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Stateflows.Common
{
public sealed class UnsubscriptionRequest : Request<UnsubscriptionResponse>
{
public BehaviorId BehaviorId { get; set; }

[JsonProperty(TypeNameHandling = TypeNameHandling.None)]
public List<string> NotificationNames { get; set; } = new List<string>();
}
}
7 changes: 7 additions & 0 deletions Core/Stateflows.Common/Events/UnsubscriptionResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Stateflows.Common
{
public sealed class UnsubscriptionResponse : Response
{
public bool UnsubscriptionSuccessful { get; set; }
}
}
13 changes: 11 additions & 2 deletions Core/Stateflows.Common/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@ public static class TypeExtensions
{
public static string GetReadableName(this Type type)
{
var result = string.Empty;
if (!type.IsGenericType)
{
return type.FullName;
result = type.FullName;
}
else
{
var typeName = type.GetGenericTypeDefinition().FullName.Split('`').First();
var typeNames = string.Join(", ", type.GetGenericArguments().Select(t => t.FullName));
return $"{typeName}<{typeNames}>";
result = $"{typeName}<{typeNames}>";
}

var standardPrefix = "Stateflows.Activities.";
if (result.StartsWith(standardPrefix))
{
result = result.Substring(standardPrefix.Length);
}

return result;
}

public static object GetUninitializedInstance(this Type type)
Expand Down
Loading

0 comments on commit 23b5401

Please sign in to comment.