Create connector tests (#52)
* Use shared IdleManager * Fix usage of _hasSubscribed by using Try methods on concurrent dictionaries * use top exception handler in idle manager subscription * Connectors with TopLevel handler tests * fix handler * start idle call manager tests * add remove tests * merge fixes * add a bit more coverage * Fix tests
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Testing;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Tests.Bridge;
|
||||
|
||||
public class IdleCallManagerTests : MoqTest
|
||||
{
|
||||
[Test]
|
||||
public void SubscribeToIdleTest()
|
||||
{
|
||||
var handler = Create<ITopLevelExceptionHandler>();
|
||||
var sut = new IdleCallManager(handler.Object);
|
||||
var action = Create<Action>();
|
||||
var addEvent = Create<Action>();
|
||||
handler.Setup(x => x.CatchUnhandled(It.IsAny<Action>())).Returns(new Result());
|
||||
sut.SubscribeToIdle("id", action.Object, addEvent.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscribeInternalTest()
|
||||
{
|
||||
var handler = Create<ITopLevelExceptionHandler>();
|
||||
var sut = new IdleCallManager(handler.Object);
|
||||
var action = Create<Action>();
|
||||
var addEvent = Create<Action>();
|
||||
addEvent.Setup(x => x.Invoke());
|
||||
|
||||
sut.IdleSubscriptionCalled.Should().BeFalse();
|
||||
sut.SubscribeInternal("id", action.Object, addEvent.Object);
|
||||
sut.Calls.Count.Should().Be(1);
|
||||
sut.Calls.Should().ContainKey("id");
|
||||
sut.IdleSubscriptionCalled.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AppOnIdleTest()
|
||||
{
|
||||
var handler = Create<ITopLevelExceptionHandler>();
|
||||
var sut = new IdleCallManager(handler.Object);
|
||||
var removeEvent = Create<Action>();
|
||||
handler.Setup(x => x.CatchUnhandled(It.IsAny<Action>())).Returns(new Result());
|
||||
sut.AppOnIdle(removeEvent.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AppOnIdleInternalTest()
|
||||
{
|
||||
var handler = Create<ITopLevelExceptionHandler>();
|
||||
var sut = new IdleCallManager(handler.Object);
|
||||
var removeEvent = Create<Action>();
|
||||
removeEvent.Setup(x => x.Invoke());
|
||||
|
||||
sut.SubscribeInternal("Test", () => { }, () => { });
|
||||
sut.IdleSubscriptionCalled.Should().BeTrue();
|
||||
sut.Calls.Count.Should().Be(1);
|
||||
|
||||
sut.AppOnIdleInternal(removeEvent.Object);
|
||||
sut.Calls.Count.Should().Be(0);
|
||||
sut.IdleSubscriptionCalled.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Testing;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Tests.Bridge;
|
||||
|
||||
public class TopLevelExceptionHandlerTests : MoqTest
|
||||
{
|
||||
[Test]
|
||||
public void CatchUnhandledAction_Happy()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
sut.CatchUnhandled(() => { });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledAction_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
|
||||
bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny<object>()));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
sut.CatchUnhandled(() => throw new InvalidOperationException());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Happy()
|
||||
{
|
||||
var val = 2;
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
var returnVal = sut.CatchUnhandled(() => val);
|
||||
returnVal.Value.Should().Be(val);
|
||||
returnVal.Exception.Should().BeNull();
|
||||
returnVal.IsSuccess.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
|
||||
bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny<object>()));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
var returnVal = sut.CatchUnhandled((Func<string>)(() => throw new InvalidOperationException()));
|
||||
returnVal.Value.Should().BeNull();
|
||||
returnVal.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
returnVal.IsSuccess.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFunc_Exception_Fatal()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
#pragma warning disable CA2201
|
||||
var exception = Assert.Throws<AggregateException>(
|
||||
() => sut.CatchUnhandled(new Func<string>(() => throw new AppDomainUnloadedException()))
|
||||
);
|
||||
#pragma warning restore CA2201
|
||||
exception.InnerExceptions.Single().Should().BeOfType<AppDomainUnloadedException>();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CatchUnhandledFuncAsync_Happy()
|
||||
{
|
||||
var val = 2;
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
var returnVal = await sut.CatchUnhandled(() => Task.FromResult(val));
|
||||
returnVal.Value.Should().Be(val);
|
||||
returnVal.Exception.Should().BeNull();
|
||||
returnVal.IsSuccess.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task CatchUnhandledFuncAsync_Exception()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
|
||||
bridge.Setup(x => x.Send(BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, It.IsAny<object>()));
|
||||
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
var returnVal = await sut.CatchUnhandled(new Func<Task<string>>(() => throw new InvalidOperationException()));
|
||||
returnVal.Value.Should().BeNull();
|
||||
returnVal.Exception.Should().BeOfType<InvalidOperationException>();
|
||||
returnVal.IsSuccess.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CatchUnhandledFuncAsync_Exception_Fatal()
|
||||
{
|
||||
var logger = Create<ILogger<TopLevelExceptionHandler>>(MockBehavior.Loose);
|
||||
var bridge = Create<IBridge>();
|
||||
var sut = new TopLevelExceptionHandler(logger.Object, bridge.Object);
|
||||
|
||||
#pragma warning disable CA2201
|
||||
var exception = Assert.ThrowsAsync<AppDomainUnloadedException>(
|
||||
async () => await sut.CatchUnhandled(new Func<Task<string>>(() => throw new AppDomainUnloadedException()))
|
||||
);
|
||||
#pragma warning restore CA2201
|
||||
exception.Should().BeOfType<AppDomainUnloadedException>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
<!-- Ingored warnings, some aspirational but too noisy for now, some by design. -->
|
||||
<NoWarn>
|
||||
CA2007;
|
||||
$(NoWarn)
|
||||
</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="altcover" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit.Analyzers" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Sdk\Speckle.Testing\Speckle.Testing.csproj" />
|
||||
<ProjectReference Include="..\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,540 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"altcover": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.8.74, )",
|
||||
"resolved": "8.8.74",
|
||||
"contentHash": "e8RZNE0vZnuBk/gOAWu9K5wm3S7dOrOlZje3PHI9PJUHqvP1cxVJD1eXAAmddFVlixowB7C7/zvC16GnunC2LQ=="
|
||||
},
|
||||
"FluentAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.12.0, )",
|
||||
"resolved": "6.12.0",
|
||||
"contentHash": "ZXhHT2YwP9lajrwSKbLlFqsmCCvFJMoRSK9t7sImfnCyd0OB3MhgxdoMcVqxbq1iyxD6mD2fiackWmBb7ayiXQ==",
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "4.4.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[17.10.0, )",
|
||||
"resolved": "17.10.0",
|
||||
"contentHash": "0/2HeACkaHEYU3wc83YlcD2Fi4LMtECJjqrtvw0lPi9DCEa35zSPt1j4fuvM8NagjDqJuh1Ja35WcRtn1Um6/A==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "17.10.0",
|
||||
"Microsoft.TestPlatform.TestHost": "17.10.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Moq": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.20.70, )",
|
||||
"resolved": "4.20.70",
|
||||
"contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==",
|
||||
"dependencies": {
|
||||
"Castle.Core": "5.1.1"
|
||||
}
|
||||
},
|
||||
"NUnit": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.1.0, )",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "MT/DpAhjtiytzhTgTqIhBuWx4y26PKfDepYUHUM+5uv4TsryHC2jwFo5e6NhWkApCm/G6kZ80dRjdJFuAxq3rg=="
|
||||
},
|
||||
"NUnit.Analyzers": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.2.0, )",
|
||||
"resolved": "4.2.0",
|
||||
"contentHash": "4fJojPkzdoa4nB2+p6U+fITvPnVvwWSnsmiJ/Dl30xqiL3oxNbYvfeSLVd91hOmEjoUqSwN3Z7j1aFedjqWbUA=="
|
||||
},
|
||||
"NUnit3TestAdapter": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.5.0, )",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "s8JpqTe9bI2f49Pfr3dFRfoVSuFQyraTj68c3XXjIS/MRGvvkLnrg6RLqnTjdShX+AdFUCCU/4Xex58AdUfs6A=="
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.14.1, )",
|
||||
"resolved": "1.14.1",
|
||||
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.5, )",
|
||||
"resolved": "0.9.5",
|
||||
"contentHash": "oU/L7pN1R7q8KkbrpQ3WJnHirPHqn+9DEA7asOcUiggV5dzVg1A/VYs7GOSusD24njxXh03tE3a2oTLOjt3cVg=="
|
||||
},
|
||||
"Castle.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.1.1",
|
||||
"contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.EventLog": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.AspNetCore.Http": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.2",
|
||||
"contentHash": "BAibpoItxI5puk7YJbIGj95arZueM8B8M5xT1fXBn3hb3L2G3ucrZcYXv1gXdaroLbntUs8qeV8iuBrpjQsrKw==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http.Abstractions": "2.2.0",
|
||||
"Microsoft.AspNetCore.WebUtilities": "2.2.0",
|
||||
"Microsoft.Extensions.ObjectPool": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0",
|
||||
"Microsoft.Net.Http.Headers": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http.Features": "2.2.0",
|
||||
"System.Text.Encodings.Web": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.Http.Features": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.AspNetCore.WebUtilities": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Net.Http.Headers": "2.2.0",
|
||||
"System.Text.Encodings.Web": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.10.0",
|
||||
"contentHash": "yC7oSlnR54XO5kOuHlVOKtxomNNN1BWXX8lK1G2jaPXT9sUok7kCOoA4Pgs0qyFaCtMrNsprztYMeoEGqCm4uA=="
|
||||
},
|
||||
"Microsoft.CSharp": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.6",
|
||||
"contentHash": "YVzVtU1IoGsTiMAe0BNV9ssKyrUm6lBQJP6w2N4W29YrBYYtyCmTFmrNGjxaJtJXVqttu0sYkPxmStj/OnMFdg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "8.0.6",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.6",
|
||||
"contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.6"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "7.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.Options": "7.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.ObjectPool": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "gA8H7uQOnM5gb+L0uTNjViHYr+hRDqCdfugheGo/MxQnuHzmhhzCBTIPm19qL1z1Xe0NEMabfcOBGv9QghlZ8g=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||
"Microsoft.Extensions.Primitives": "7.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q=="
|
||||
},
|
||||
"Microsoft.Net.Http.Headers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0",
|
||||
"System.Buffers": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.10.0",
|
||||
"contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "1.6.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.10.0",
|
||||
"contentHash": "LWpMdfqhHvcUkeMCvNYJO8QlPLlYz9XPPb+ZbaXIKhdmjAV0wqTSrTiW5FLaf7RRZT50AQADDOYMOe0HxDxNgA==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "17.10.0",
|
||||
"Newtonsoft.Json": "13.0.1"
|
||||
}
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.1",
|
||||
"contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
|
||||
},
|
||||
"Polly": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.2.3",
|
||||
"contentHash": "DeCY0OFbNdNxsjntr1gTXHJ5pKUwYzp04Er2LLeN3g6pWhffsGuKVfMBLe1lw7x76HrPkLxKEFxBlpRxS2nDEQ=="
|
||||
},
|
||||
"Polly.Contrib.WaitAndRetry": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.1",
|
||||
"contentHash": "1MUQLiSo4KDkQe6nzQRhIU05lm9jlexX5BVsbuw0SL82ynZ+GzAHQxJVDPVBboxV37Po3SG077aX8DuSy8TkaA=="
|
||||
},
|
||||
"Polly.Extensions.Http": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "drrG+hB3pYFY7w1c3BD+lSGYvH2oIclH8GRSehgfyP5kjnFnHKQuuBhuHLv+PWyFuaTDyk/vfRpnxOzd11+J8g==",
|
||||
"dependencies": {
|
||||
"Polly": "7.1.0"
|
||||
}
|
||||
},
|
||||
"Sentry": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.33.0",
|
||||
"contentHash": "8vbD2o6IR2wrRrkSiRbnodWGWUOqIlwYtzpjvPNOb5raJdOf+zxMwfS8f6nx9bmrTTfDj7KrCB8C/5OuicAc8A=="
|
||||
},
|
||||
"Sentry.Serilog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.33.0",
|
||||
"contentHash": "V8BU7QGWg2qLYfNPqtuTBhC1opysny5l+Ifp6J6PhOeAxU0FssR7nYfbJVetrnLIoh2rd3DlJ6hHYYQosQYcUQ==",
|
||||
"dependencies": {
|
||||
"Sentry": "3.33.0",
|
||||
"Serilog": "2.10.0"
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.12.0",
|
||||
"contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
|
||||
},
|
||||
"Serilog.Enrichers.ClientInfo": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.3.0",
|
||||
"contentHash": "mTc7PM+wC9Hr7LWSwqt5mmnlAr7RJs+eTb3PGPRhwdOackk95MkhUZognuxXEdlW19HAFNmEBTSBY5DfLwM8jQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.Http": "2.2.2",
|
||||
"Serilog": "2.9.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Exceptions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.4.0",
|
||||
"contentHash": "nc/+hUw3lsdo0zCj0KMIybAu7perMx79vu72w0za9Nsi6mWyNkGXxYxakAjWB7nEmYL6zdmhEQRB4oJ2ALUeug==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.8.0",
|
||||
"System.Reflection.TypeExtensions": "4.7.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Formatting.Compact": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.8.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.Console": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "K6N5q+5fetjnJPvCmkWOpJ/V8IEIoMIB1s86OzBrbxwTyHxdx3pmz4H+8+O/Dc/ftUX12DM1aynx/dDowkwzqg==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.10.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.File": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.10.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.PeriodicBatching": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.1.0",
|
||||
"contentHash": "NDWR7m3PalVlGEq3rzoktrXikjFMLmpwF0HI4sowo8YDdU+gqPlTHlDQiOGxHfB0sTfjPA9JjA7ctKG9zqjGkw==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.0.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.Seq": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.2.2",
|
||||
"contentHash": "1Csmo5ua7NKUe0yXUx+zsRefjAniPWcXFhUXxXG8pwo0iMiw2gjn9SOkgYnnxbgWqmlGv236w0N/dHc2v5XwMg==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.12.0",
|
||||
"Serilog.Formatting.Compact": "1.1.0",
|
||||
"Serilog.Sinks.File": "5.0.0",
|
||||
"Serilog.Sinks.PeriodicBatching": "3.1.0"
|
||||
}
|
||||
},
|
||||
"SerilogTimings": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.0.1",
|
||||
"contentHash": "Zs28eTgszAMwpIrbBnWHBI50yuxL50p/dmAUWmy75+axdZYK/Sjm5/5m1N/CisR8acJUhTVcjPZrsB1P5iv0Uw==",
|
||||
"dependencies": {
|
||||
"Serilog": "2.10.0"
|
||||
}
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.0.1",
|
||||
"contentHash": "MzEQ1Im0zTja+tEsdRIk/WlPiKqb22NmTOJcR1ZKm/mz46pezyyID3/wRz6vJUELMpSLnG7LhsxBL+nxbr7V0w=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.6",
|
||||
"contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.6",
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.6"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.6",
|
||||
"contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.6",
|
||||
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.6",
|
||||
"contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.6"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
|
||||
},
|
||||
"System.Configuration.ConfigurationManager": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "gWwQv/Ug1qWJmHCmN17nAbxJYmQBM/E94QxKLksvUiiKB1Ld3Sc/eK1lgmbSjDFxkQhVuayI/cGFZhpBSodLrg==",
|
||||
"dependencies": {
|
||||
"System.Security.Cryptography.ProtectedData": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.EventLog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.6.0",
|
||||
"contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
|
||||
},
|
||||
"System.Reflection.TypeExtensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA=="
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.4.0",
|
||||
"contentHash": "cJV7ScGW7EhatRsjehfvvYVBvtiSMKgN8bOVI0bQhnF5bU7vnHVIsH49Kva7i7GWaWYvmEzkYVk1TC+gZYBEog=="
|
||||
},
|
||||
"System.Text.Encodings.Web": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g=="
|
||||
},
|
||||
"speckle.autofac": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[7.0.0, )",
|
||||
"Speckle.Autofac": "[1.0.0, )",
|
||||
"Speckle.Connectors.Utils": "[1.0.0, )",
|
||||
"Speckle.Core": "[3.1.0-dev.75, )",
|
||||
"System.Threading.Tasks.Dataflow": "[6.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.utils": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Serilog.Extensions.Logging": "[7.0.0, )",
|
||||
"Speckle.Autofac": "[1.0.0, )",
|
||||
"Speckle.Core": "[3.1.0-dev.75, )"
|
||||
}
|
||||
},
|
||||
"speckle.testing": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Moq": "[4.20.70, )",
|
||||
"NUnit": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.0, )",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw=="
|
||||
},
|
||||
"Serilog.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[7.0.0, )",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "9faU0zNQqU7I6soVhLUMYaGNpgWv6cKlKb2S5AnS8gXxzW/em5Ladm/6FMrWTnX41cdbdGPOWNAo6adi4WaJ6A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "7.0.0",
|
||||
"Serilog": "2.12.0"
|
||||
}
|
||||
},
|
||||
"Speckle.Core": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.75, )",
|
||||
"resolved": "3.1.0-dev.75",
|
||||
"contentHash": "RW+ZNmMoVtJnj+FxwX4pxy9oEFrYvZ6GEVA1AtyQQ3jPFVdApL9GDsfp7iNJDTtzY4sGY+a3EcudslYMfGk8jA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "8.0.6",
|
||||
"Polly": "7.2.3",
|
||||
"Polly.Contrib.WaitAndRetry": "1.1.1",
|
||||
"Polly.Extensions.Http": "3.0.0",
|
||||
"Sentry": "3.33.0",
|
||||
"Sentry.Serilog": "3.33.0",
|
||||
"Serilog": "2.12.0",
|
||||
"Serilog.Enrichers.ClientInfo": "1.3.0",
|
||||
"Serilog.Exceptions": "8.4.0",
|
||||
"Serilog.Sinks.Console": "4.1.0",
|
||||
"Serilog.Sinks.Seq": "5.2.2",
|
||||
"SerilogTimings": "3.0.1",
|
||||
"Speckle.DoubleNumerics": "4.0.1",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Dataflow": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "+tyDCU3/B1lDdOOAJywHQoFwyXIUghIaP2BxG79uvhfTnO+D9qIgjVlL/JV2NTliYbMHpd6eKDmHp2VHpij7MA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class BasicConnectorBindingCommands
|
||||
private const string NOTIFY_DOCUMENT_CHANGED_EVENT_NAME = "documentChanged";
|
||||
private const string SET_MODEL_PROGRESS_UI_COMMAND_NAME = "setModelProgress";
|
||||
private const string SET_MODEL_ERROR_UI_COMMAND_NAME = "setModelError";
|
||||
internal const string SET_GLOBAL_NOTIFICATION = "setGlobalNotification";
|
||||
public const string SET_GLOBAL_NOTIFICATION = "setGlobalNotification";
|
||||
|
||||
protected IBridge Bridge { get; }
|
||||
|
||||
|
||||
@@ -1,52 +1,62 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.InterfaceGenerator;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
public interface IIdleCallManager
|
||||
{
|
||||
void SubscribeToIdle(string id, Action action, Action addEvent);
|
||||
void AppOnIdle(Action removeEvent);
|
||||
}
|
||||
|
||||
//should be registered as singleton
|
||||
[GenerateAutoInterface]
|
||||
[SuppressMessage("ReSharper", "InconsistentlySynchronizedField")]
|
||||
public class IdleCallManager(ITopLevelExceptionHandler topLevelExceptionHandler) : IIdleCallManager
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Action> _calls = new();
|
||||
private bool _idleSubscriptionCalled;
|
||||
public ConcurrentDictionary<string, Action> Calls { get; } = new();
|
||||
|
||||
private readonly object _lock = new();
|
||||
public bool IdleSubscriptionCalled { get; private set; }
|
||||
|
||||
public void SubscribeToIdle(string id, Action action, Action addEvent) =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() => SubscribeInternal(id, action, addEvent));
|
||||
|
||||
public void SubscribeInternal(string id, Action action, Action addEvent)
|
||||
{
|
||||
Calls.TryAdd(id, action);
|
||||
if (!IdleSubscriptionCalled)
|
||||
{
|
||||
_calls.TryAdd(id, action);
|
||||
if (!_idleSubscriptionCalled)
|
||||
lock (_lock)
|
||||
{
|
||||
lock (_calls)
|
||||
if (!IdleSubscriptionCalled)
|
||||
{
|
||||
if (!_idleSubscriptionCalled)
|
||||
{
|
||||
addEvent.Invoke();
|
||||
_idleSubscriptionCalled = true;
|
||||
}
|
||||
addEvent.Invoke();
|
||||
IdleSubscriptionCalled = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void AppOnIdle(Action removeEvent) =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() =>
|
||||
topLevelExceptionHandler.CatchUnhandled(() => AppOnIdleInternal(removeEvent));
|
||||
|
||||
public void AppOnIdleInternal(Action removeEvent)
|
||||
{
|
||||
foreach (KeyValuePair<string, Action> kvp in Calls)
|
||||
{
|
||||
foreach (KeyValuePair<string, Action> kvp in _calls)
|
||||
kvp.Value.Invoke();
|
||||
}
|
||||
Calls.Clear();
|
||||
if (IdleSubscriptionCalled)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
kvp.Value.Invoke();
|
||||
}
|
||||
_calls.Clear();
|
||||
if (_idleSubscriptionCalled)
|
||||
{
|
||||
lock (_calls)
|
||||
if (IdleSubscriptionCalled)
|
||||
{
|
||||
if (_idleSubscriptionCalled)
|
||||
{
|
||||
removeEvent.Invoke();
|
||||
_idleSubscriptionCalled = false;
|
||||
}
|
||||
removeEvent.Invoke();
|
||||
IdleSubscriptionCalled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Speckle.Connectors.Utils;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// Result Pattern struct
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[ExcludeFromCodeCoverage]
|
||||
public readonly struct Result<T>
|
||||
{
|
||||
//Don't add new members to this struct, it is perfect.
|
||||
public T? Value { get; }
|
||||
public Exception? Exception { get; }
|
||||
|
||||
[MemberNotNullWhen(false, nameof(Exception))]
|
||||
public bool IsSuccess => Exception is null;
|
||||
|
||||
/// <summary>
|
||||
/// Create a successful result
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
public Result(T result)
|
||||
{
|
||||
Value = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a non-successful result
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="result"/> was null</exception>
|
||||
public Result([NotNull] Exception? result)
|
||||
{
|
||||
Exception = result.NotNull();
|
||||
}
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public readonly struct Result
|
||||
{
|
||||
//Don't add new members to this struct, it is perfect.
|
||||
public Exception? Exception { get; }
|
||||
|
||||
[MemberNotNullWhen(false, nameof(Exception))]
|
||||
public bool IsSuccess => Exception is null;
|
||||
|
||||
/// <summary>
|
||||
/// Create a non-successful result
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="result"/> was null</exception>
|
||||
public Result([NotNull] Exception? result)
|
||||
{
|
||||
Exception = result.NotNull();
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,11 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.Utils;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using Speckle.InterfaceGenerator;
|
||||
|
||||
namespace Speckle.Connectors.DUI.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// Result Pattern struct
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public readonly struct Result<T>
|
||||
{
|
||||
//Don't add new members to this struct, it is perfect.
|
||||
public T? Value { get; }
|
||||
public Exception? Exception { get; }
|
||||
|
||||
[MemberNotNullWhen(false, nameof(Exception))]
|
||||
public bool IsSuccess => Exception is null;
|
||||
|
||||
/// <summary>
|
||||
/// Create a successful result
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
public Result(T result)
|
||||
{
|
||||
Value = result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a non-sucessful result
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="result"/> was null</exception>
|
||||
public Result([NotNull] Exception? result)
|
||||
{
|
||||
Exception = result.NotNull();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The functions provided by this class are designed to be used in all "top level" scenarios (e.g. Plugin, UI, and Event callbacks)
|
||||
/// To provide "last ditch effort" handling of unexpected exceptions that have not been handled.
|
||||
@@ -55,19 +20,11 @@ public readonly struct Result<T>
|
||||
/// Attempting to swallow them may lead to data corruption, deadlocking, or things worse than a managed host app crash.
|
||||
/// </remarks>
|
||||
[GenerateAutoInterface]
|
||||
public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
public sealed class TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger, IBridge bridge)
|
||||
: ITopLevelExceptionHandler
|
||||
{
|
||||
private readonly ILogger<TopLevelExceptionHandler> _logger;
|
||||
private readonly IBridge _bridge;
|
||||
|
||||
private const string UNHANDLED_LOGGER_TEMPLATE = "An unhandled Exception occured";
|
||||
|
||||
public TopLevelExceptionHandler(ILogger<TopLevelExceptionHandler> logger, IBridge bridge)
|
||||
{
|
||||
_logger = logger;
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the given function <paramref name="function"/> within a <see langword="try"/>/<see langword="catch"/> block,
|
||||
/// and provides exception handling for unexpected exceptions that have not been handled.<br/>
|
||||
@@ -75,35 +32,18 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
/// <param name="function">The function to invoke and provide error handling for</param>
|
||||
/// <exception cref="Exception"><see cref="ExceptionHelpers.IsFatal"/> will be rethrown, these should be allowed to bubble up to the host app</exception>
|
||||
/// <seealso cref="ExceptionHelpers.IsFatal"/>
|
||||
public void CatchUnhandled(Action function)
|
||||
{
|
||||
CatchUnhandled(() =>
|
||||
{
|
||||
function.Invoke();
|
||||
return (object?)null;
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CatchUnhandled(Action)"/>
|
||||
/// <typeparam name="T"><paramref name="function"/> return type</typeparam>
|
||||
/// <returns>A result pattern struct (where exceptions have been handled)</returns>
|
||||
public Result<T> CatchUnhandled<T>(Func<T> function)
|
||||
{
|
||||
return CatchUnhandled(() => Task.FromResult(function.Invoke())).Result;
|
||||
}
|
||||
|
||||
///<inheritdoc cref="CatchUnhandled{T}(Func{T})"/>
|
||||
public async Task<Result<T>> CatchUnhandled<T>(Func<Task<T>> function)
|
||||
public Result CatchUnhandled(Action function)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return new(await function.Invoke().ConfigureAwait(false));
|
||||
function.Invoke();
|
||||
return new();
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
|
||||
SetGlobalNotification(
|
||||
ToastNotificationType.DANGER,
|
||||
@@ -116,13 +56,48 @@ public sealed class TopLevelExceptionHandler : ITopLevelExceptionHandler
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="CatchUnhandled(Action)"/>
|
||||
/// <typeparam name="T"><paramref name="function"/> return type</typeparam>
|
||||
/// <returns>A result pattern struct (where exceptions have been handled)</returns>
|
||||
public Result<T> CatchUnhandled<T>(Func<T> function) =>
|
||||
CatchUnhandled(() => Task.FromResult(function.Invoke())).Result;
|
||||
|
||||
///<inheritdoc cref="CatchUnhandled{T}(Func{T})"/>
|
||||
public async Task<Result<T>> CatchUnhandled<T>(Func<Task<T>> function)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
return new(await function.Invoke().ConfigureAwait(false));
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
logger.LogError(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
|
||||
SetGlobalNotification(
|
||||
ToastNotificationType.DANGER,
|
||||
"Unhandled Exception Occured",
|
||||
ex.ToFormattedString(),
|
||||
false
|
||||
);
|
||||
return new(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogCritical(ex, UNHANDLED_LOGGER_TEMPLATE);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetGlobalNotification(ToastNotificationType type, string title, string message, bool autoClose) =>
|
||||
_bridge.Send(
|
||||
bridge.Send(
|
||||
BasicConnectorBindingCommands.SET_GLOBAL_NOTIFICATION, //TODO: We could move these constants into a DUI3 constants static class
|
||||
new
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Speckle.Autofac.DependencyInjection;
|
||||
using Speckle.Autofac.DependencyInjection;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
|
||||
@@ -15,6 +15,6 @@ public abstract class MoqTest
|
||||
|
||||
protected MockRepository Repository { get; private set; } = new(MockBehavior.Strict);
|
||||
|
||||
protected Mock<T> Create<T>()
|
||||
where T : class => Repository.Create<T>();
|
||||
protected Mock<T> Create<T>(MockBehavior behavior = MockBehavior.Strict)
|
||||
where T : class => Repository.Create<T>(behavior);
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit202
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "2022", "2022", "{0AF38BA3-65A0-481B-8CBB-B82E406E1575}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.DUI.Tests", "DUI3\Speckle.Connectors.DUI.Tests\Speckle.Connectors.DUI.Tests.csproj", "{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.Revit2022.Tests", "Converters\Revit\Speckle.Converters.Revit2022.Tests\Speckle.Converters.Revit2022.Tests.csproj", "{D8069A23-AD2E-4C9E-8574-7E8C45296A46}"
|
||||
EndProject
|
||||
Global
|
||||
@@ -293,6 +295,10 @@ Global
|
||||
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{881D71A3-D276-4108-98C6-0FFD32129B9C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D8069A23-AD2E-4C9E-8574-7E8C45296A46}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -357,6 +363,7 @@ Global
|
||||
{19424B55-058C-4E9C-B86F-700AEF9EAEC3} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
|
||||
{881D71A3-D276-4108-98C6-0FFD32129B9C} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
|
||||
{0AF38BA3-65A0-481B-8CBB-B82E406E1575} = {D92751C8-1039-4005-90B2-913E55E0B8BD}
|
||||
{EB83A3A3-F9B6-4281-8EBF-F7289FB5D885} = {FD4D6594-D81E-456F-8F2E-35B09E04A755}
|
||||
{D8069A23-AD2E-4C9E-8574-7E8C45296A46} = {0AF38BA3-65A0-481B-8CBB-B82E406E1575}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
|
||||
Reference in New Issue
Block a user