Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Event aggregator #417

Draft
wants to merge 20 commits into
base: remove-dataflow
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
it's ALIVE
adamhathcock committed Nov 28, 2024
commit 8e3b1da99fdbe570504860035155e688143ae35e
Original file line number Diff line number Diff line change
@@ -12,7 +12,11 @@ public class ArcGISSelectionBinding : ISelectionBinding
public string Name => "selectionBinding";
public IBrowserBridge Parent { get; }

public ArcGISSelectionBinding(IBrowserBridge parent, MapMembersUtils mapMemberUtils, ITopLevelExceptionHandler topLevelExceptionHandler)
public ArcGISSelectionBinding(
IBrowserBridge parent,
MapMembersUtils mapMemberUtils,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_mapMemberUtils = mapMemberUtils;
Parent = parent;
Original file line number Diff line number Diff line change
@@ -21,7 +21,12 @@ public class BasicConnectorBinding : IBasicConnectorBinding
private readonly DocumentModelStore _store;
private readonly ISpeckleApplication _speckleApplication;

public BasicConnectorBinding(DocumentModelStore store, IBrowserBridge parent, ISpeckleApplication speckleApplication, ITopLevelExceptionHandler topLevelExceptionHandler)
public BasicConnectorBinding(
DocumentModelStore store,
IBrowserBridge parent,
ISpeckleApplication speckleApplication,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_store = store;
_speckleApplication = speckleApplication;
Original file line number Diff line number Diff line change
@@ -34,8 +34,6 @@ public static void AddArcGIS(this IServiceCollection serviceCollection)
serviceCollection.AddSingleton<IBinding, ConfigBinding>();
serviceCollection.AddSingleton<IBinding, AccountBinding>();

serviceCollection.RegisterTopLevelExceptionHandler();

serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBinding>();

Original file line number Diff line number Diff line change
@@ -18,7 +18,11 @@ public class AutocadSelectionBinding : ISelectionBinding

public IBrowserBridge Parent { get; }

public AutocadSelectionBinding(IBrowserBridge parent, IThreadContext threadContext, ITopLevelExceptionHandler topLevelExceptionHandler)
public AutocadSelectionBinding(
IBrowserBridge parent,
IThreadContext threadContext,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_topLevelExceptionHandler = topLevelExceptionHandler;
Parent = parent;
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ IThreadContext threadContext
_logger = logger;
_speckleApplication = speckleApplication;
_threadContext = threadContext;
_topLevelExceptionHandler =topLevelExceptionHandler;
_topLevelExceptionHandler = topLevelExceptionHandler;
Parent = parent;
Commands = new SendBindingUICommands(parent);

Original file line number Diff line number Diff line change
@@ -59,8 +59,6 @@ public static void AddAutocadBase(this IServiceCollection serviceCollection)
serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, AutocadBasicConnectorBinding>();
serviceCollection.AddSingleton<IBinding, ConfigBinding>();

serviceCollection.RegisterTopLevelExceptionHandler();
}

public static void LoadSend(this IServiceCollection serviceCollection)
Original file line number Diff line number Diff line change
@@ -43,8 +43,6 @@ public static void AddRevit(this IServiceCollection serviceCollection)
serviceCollection.AddSingleton<IBinding, RevitReceiveBinding>();
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();

serviceCollection.RegisterTopLevelExceptionHandler();

serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, BasicConnectorBindingRevit>();

Original file line number Diff line number Diff line change
@@ -45,8 +45,6 @@ public static void AddRhino(this IServiceCollection serviceCollection)
serviceCollection.AddSingleton<IBinding, ConfigBinding>(); // POC: Easier like this for now, should be cleaned up later
serviceCollection.AddSingleton<IBinding, AccountBinding>();

serviceCollection.RegisterTopLevelExceptionHandler();

serviceCollection.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
serviceCollection.AddSingleton<IBasicConnectorBinding, RhinoBasicConnectorBinding>();

Original file line number Diff line number Diff line change
@@ -42,8 +42,6 @@ public static IServiceCollection AddTekla(this IServiceCollection services)
services.AddSingleton<IBinding, AccountBinding>();
services.AddSingleton<IBasicConnectorBinding, TeklaBasicConnectorBinding>();

services.RegisterTopLevelExceptionHandler();

services.AddSingleton<IBinding>(sp => sp.GetRequiredService<IBasicConnectorBinding>());
services.AddSingleton<IBinding, TeklaSendBinding>();
services.AddSingleton<IBinding, TeklaSelectionBinding>();
Original file line number Diff line number Diff line change
@@ -26,7 +26,8 @@ public void CatchUnhandledAction_Exception()
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
var eventAggregator = Create<ISpeckleEventAggregator>();

eventAggregator.Setup(x => x.GetEvent<ExceptionEvent>())
eventAggregator
.Setup(x => x.GetEvent<ExceptionEvent>())
.Returns(new ExceptionEvent(Create<IThreadContext>().Object));

var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
@@ -54,7 +55,8 @@ public void CatchUnhandledFunc_Exception()
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
var eventAggregator = Create<ISpeckleEventAggregator>();

eventAggregator.Setup(x => x.GetEvent<ExceptionEvent>())
eventAggregator
.Setup(x => x.GetEvent<ExceptionEvent>())
.Returns(new ExceptionEvent(Create<IThreadContext>().Object));

var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
@@ -98,7 +100,8 @@ public async Task CatchUnhandledFuncAsync_Exception()
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
var eventAggregator = Create<ISpeckleEventAggregator>();

eventAggregator.Setup(x => x.GetEvent<ExceptionEvent>())
eventAggregator
.Setup(x => x.GetEvent<ExceptionEvent>())
.Returns(new ExceptionEvent(Create<IThreadContext>().Object));

var sut = new TopLevelExceptionHandler(logger.Object, eventAggregator.Object);
31 changes: 23 additions & 8 deletions DUI3/Speckle.Connectors.DUI/Bridge/BrowserBridge.cs
Original file line number Diff line number Diff line change
@@ -63,7 +63,10 @@ public BrowserBridge(
IJsonSerializer jsonSerializer,
ILogger<BrowserBridge> logger,
IBrowserScriptExecutor browserScriptExecutor,
IThreadOptions threadOptions, ISpeckleEventAggregator eventAggregator, ITopLevelExceptionHandler topLevelExceptionHandler)
IThreadOptions threadOptions,
ISpeckleEventAggregator eventAggregator,
ITopLevelExceptionHandler topLevelExceptionHandler
)
{
_threadContext = threadContext;
_jsonSerializer = jsonSerializer;
@@ -73,13 +76,25 @@ public BrowserBridge(
_threadOptions = threadOptions;
_eventAggregator = eventAggregator;
_topLevelExceptionHandler = topLevelExceptionHandler;
eventAggregator.GetEvent<ExceptionEvent>().Subscribe(ex =>
{
Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION,
new { type = ToastNotificationType.DANGER, title = "Unhandled Exception Occurred", description = ex.ToFormattedString(), autoClose = false }
)
.ConfigureAwait(false);
}, ThreadOption.UIThread);
eventAggregator
.GetEvent<ExceptionEvent>()
.Subscribe(
ex =>
{
Send(
BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION,
new
{
type = ToastNotificationType.DANGER,
title = "Unhandled Exception Occurred",
description = ex.ToFormattedString(),
autoClose = false
}
)
.ConfigureAwait(false);
},
ThreadOption.UIThread
);
}

public void AssociateWithBinding(IBinding binding)
61 changes: 61 additions & 0 deletions DUI3/Speckle.Connectors.DUI/Bridge/SpeckleEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Speckle.Connectors.Common.Threading;

namespace Speckle.Connectors.DUI.Bridge;

public class ExceptionEvent(IThreadContext threadContext) : SpeckleEvent<Exception>(threadContext);

public class ThreadContextEventSubscription<T>(
IDelegateReference actionReference,
IDelegateReference filterReference,
IThreadContext threadContext
) : EventSubscription<T>(actionReference, filterReference)
{
public override void InvokeAction(Action<T> action, T payload) =>
threadContext.RunOnMain(() => action.Invoke(payload));
}

public class SpeckleEvent<T>(IThreadContext threadContext) : PubSubEvent<T>
{
public override SubscriptionToken Subscribe(
Action<T> action,
ThreadOption threadOption,
bool keepSubscriberReferenceAlive,
Predicate<T>? filter
)
{
IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);
IDelegateReference filterReference;
if (filter != null)
{
filterReference = new DelegateReference(filter, keepSubscriberReferenceAlive);
}
else
{
filterReference = new DelegateReference(
new Predicate<T>(
delegate
{
return true;
}
),
true
);
}
EventSubscription<T> subscription;
switch (threadOption)
{
case ThreadOption.BackgroundThread:
subscription = new BackgroundEventSubscription<T>(actionReference, filterReference);
break;
case ThreadOption.UIThread:
subscription = new ThreadContextEventSubscription<T>(actionReference, filterReference, threadContext);
break;
case ThreadOption.PublisherThread:
default:
subscription = new EventSubscription<T>(actionReference, filterReference);
break;
}

return InternalSubscribe(subscription);
}
}
35 changes: 35 additions & 0 deletions DUI3/Speckle.Connectors.DUI/Bridge/SpeckleEventAggregator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.Extensions.DependencyInjection;

namespace Speckle.Connectors.DUI.Bridge;

public interface ISpeckleEventAggregator
{
TEventType GetEvent<TEventType>()
where TEventType : EventBase;
}

public class SpeckleEventAggregator : ISpeckleEventAggregator
{
private readonly IServiceProvider _serviceProvider;

private readonly Dictionary<Type, EventBase> _events = new();

public SpeckleEventAggregator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public TEventType GetEvent<TEventType>()
where TEventType : EventBase
{
lock (_events)
{
if (!_events.TryGetValue(typeof(TEventType), out var existingEvent))
{
existingEvent = (TEventType)_serviceProvider.GetRequiredService(typeof(TEventType));
_events[typeof(TEventType)] = existingEvent;
}
return (TEventType)existingEvent;
}
}
}
75 changes: 1 addition & 74 deletions DUI3/Speckle.Connectors.DUI/Bridge/TopLevelExceptionHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using Speckle.Connectors.Common.Threading;
using Speckle.InterfaceGenerator;
using Speckle.Sdk;

@@ -27,7 +26,7 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler

private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured";

internal TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger, ISpeckleEventAggregator eventAggregator)
public TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger, ISpeckleEventAggregator eventAggregator)
{
_logger = logger;
_eventAggregator = eventAggregator;
@@ -114,75 +113,3 @@ public async Task<Result<T>> CatchUnhandledAsync<T>(Func<Task<T>> function)
/// <param name="function"><inheritdoc cref="CatchUnhandled{T}(Func{T})"/></param>
public async void FireAndForget(Func<Task> function) => await CatchUnhandledAsync(function).ConfigureAwait(false);
}

public interface ISpeckleEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase;
}
public class SpeckleEventAggregator : ISpeckleEventAggregator
{
private readonly IServiceProvider _serviceProvider;


private readonly Dictionary<Type, EventBase> _events = new();

public SpeckleEventAggregator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public TEventType GetEvent<TEventType>() where TEventType : EventBase
{
lock (_events)
{
if (!_events.TryGetValue(typeof(TEventType), out var existingEvent))
{
existingEvent = (TEventType)_serviceProvider.GetService(typeof(TEventType));
_events[typeof(TEventType)] = existingEvent;
}
return (TEventType)existingEvent;
}
}
}

public class ExceptionEvent(IThreadContext threadContext) : SpeckleEvent<Exception>(threadContext);

public class SpeckleEvent<T>(IThreadContext threadContext) : PubSubEvent<T>
{
public override SubscriptionToken Subscribe(Action<T> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive,
Predicate<T> filter)
{
IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);

EventSubscription subscription;
switch (threadOption)
{
case ThreadOption.PublisherThread:
subscription = new EventSubscription(actionReference);
break;
case ThreadOption.BackgroundThread:
subscription = new BackgroundEventSubscription(actionReference);
break;
case ThreadOption.UIThread:
subscription = new ThreadContextEventSubscription(actionReference, threadContext);
break;
default:
subscription = new EventSubscription(actionReference);
break;
}

return InternalSubscribe(subscription);

}
}

public class ThreadContextEventSubscription : EventSubscription
{
private readonly IThreadContext _threadContext;
public ThreadContextEventSubscription(IDelegateReference actionReference, IThreadContext threadContext) : base(actionReference)
{
_threadContext = threadContext;
}

public override void InvokeAction(Action action) => _threadContext.RunOnMain(action);
}
6 changes: 2 additions & 4 deletions DUI3/Speckle.Connectors.DUI/ContainerRegistration.cs
Original file line number Diff line number Diff line change
@@ -24,14 +24,12 @@ public static void AddDUI<TThreadContext, TDocumentStore>(this IServiceCollectio
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IdleCallManager)));
serviceCollection.AddMatchingInterfacesAsTransient(Assembly.GetAssembly(typeof(IServerTransportFactory)));
serviceCollection.AddSingleton<ISpeckleEventAggregator>(sp => new SpeckleEventAggregator(sp));
}

public static void RegisterTopLevelExceptionHandler(this IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<IBinding, TopLevelExceptionHandlerBinding>(sp =>
sp.GetRequiredService<TopLevelExceptionHandlerBinding>()
);
serviceCollection.AddSingleton<TopLevelExceptionHandlerBinding>();
serviceCollection.AddSingleton<ITopLevelExceptionHandler>();
serviceCollection.AddSingleton<ITopLevelExceptionHandler, TopLevelExceptionHandler>();
serviceCollection.AddTransient<ExceptionEvent>();
}
}
Loading