Browse Source

Exception filter.

pull/539/head
Sebastian 6 years ago
parent
commit
b485fda90b
  1. 25
      backend/src/Squidex.Infrastructure/DomainObjectVersionException.cs
  2. 25
      backend/src/Squidex.Infrastructure/EventSourcing/WrongEventVersionException.cs
  3. 37
      backend/src/Squidex.Infrastructure/Orleans/ExceptionWrapperFilter.cs
  4. 50
      backend/src/Squidex.Infrastructure/Orleans/OrleansWrapperException.cs
  5. 25
      backend/src/Squidex.Infrastructure/States/InconsistentStateException.cs
  6. 1
      backend/src/Squidex/Config/Orleans/OrleansServices.cs
  7. 75
      backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs

25
backend/src/Squidex.Infrastructure/DomainObjectVersionException.cs

@ -13,39 +13,30 @@ namespace Squidex.Infrastructure
[Serializable] [Serializable]
public class DomainObjectVersionException : DomainObjectException public class DomainObjectVersionException : DomainObjectException
{ {
private readonly long currentVersion; public long CurrentVersion { get; }
private readonly long expectedVersion;
public long CurrentVersion public long ExpectedVersion { get; }
{
get { return currentVersion; }
}
public long ExpectedVersion
{
get { return expectedVersion; }
}
public DomainObjectVersionException(string id, Type type, long currentVersion, long expectedVersion) public DomainObjectVersionException(string id, Type type, long currentVersion, long expectedVersion)
: base(FormatMessage(id, type, currentVersion, expectedVersion), id, type) : base(FormatMessage(id, type, currentVersion, expectedVersion), id, type)
{ {
this.currentVersion = currentVersion; CurrentVersion = currentVersion;
this.expectedVersion = expectedVersion; ExpectedVersion = expectedVersion;
} }
protected DomainObjectVersionException(SerializationInfo info, StreamingContext context) protected DomainObjectVersionException(SerializationInfo info, StreamingContext context)
: base(info, context) : base(info, context)
{ {
currentVersion = info.GetInt64(nameof(currentVersion)); CurrentVersion = info.GetInt64(nameof(CurrentVersion));
expectedVersion = info.GetInt64(nameof(expectedVersion)); ExpectedVersion = info.GetInt64(nameof(ExpectedVersion));
} }
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {
info.AddValue(nameof(currentVersion), currentVersion); info.AddValue(nameof(CurrentVersion), CurrentVersion);
info.AddValue(nameof(expectedVersion), expectedVersion); info.AddValue(nameof(ExpectedVersion), ExpectedVersion);
base.GetObjectData(info, context); base.GetObjectData(info, context);
} }

25
backend/src/Squidex.Infrastructure/EventSourcing/WrongEventVersionException.cs

@ -13,39 +13,30 @@ namespace Squidex.Infrastructure.EventSourcing
[Serializable] [Serializable]
public class WrongEventVersionException : Exception public class WrongEventVersionException : Exception
{ {
private readonly long currentVersion; public long CurrentVersion { get; }
private readonly long expectedVersion;
public long CurrentVersion public long ExpectedVersion { get; }
{
get { return currentVersion; }
}
public long ExpectedVersion
{
get { return expectedVersion; }
}
public WrongEventVersionException(long currentVersion, long expectedVersion) public WrongEventVersionException(long currentVersion, long expectedVersion)
: base(FormatMessage(currentVersion, expectedVersion)) : base(FormatMessage(currentVersion, expectedVersion))
{ {
this.currentVersion = currentVersion; CurrentVersion = currentVersion;
this.expectedVersion = expectedVersion; ExpectedVersion = expectedVersion;
} }
protected WrongEventVersionException(SerializationInfo info, StreamingContext context) protected WrongEventVersionException(SerializationInfo info, StreamingContext context)
: base(info, context) : base(info, context)
{ {
currentVersion = info.GetInt64(nameof(currentVersion)); CurrentVersion = info.GetInt64(nameof(CurrentVersion));
expectedVersion = info.GetInt64(nameof(expectedVersion)); ExpectedVersion = info.GetInt64(nameof(ExpectedVersion));
} }
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {
info.AddValue(nameof(currentVersion), currentVersion); info.AddValue(nameof(CurrentVersion), CurrentVersion);
info.AddValue(nameof(expectedVersion), expectedVersion); info.AddValue(nameof(ExpectedVersion), ExpectedVersion);
base.GetObjectData(info, context); base.GetObjectData(info, context);
} }

37
backend/src/Squidex.Infrastructure/Orleans/ExceptionWrapperFilter.cs

@ -0,0 +1,37 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Orleans;
namespace Squidex.Infrastructure.Orleans
{
public sealed class ExceptionWrapperFilter : IIncomingGrainCallFilter
{
public async Task Invoke(IIncomingGrainCallContext context)
{
try
{
await context.Invoke();
}
catch (Exception ex)
{
var type = ex.GetType();
if (!type.IsSerializable)
{
throw new OrleansWrapperException(ex, type);
}
else
{
throw;
}
}
}
}
}

50
backend/src/Squidex.Infrastructure/Orleans/OrleansWrapperException.cs

@ -0,0 +1,50 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Runtime.Serialization;
using System.Text;
namespace Squidex.Infrastructure.Orleans
{
[Serializable]
public class OrleansWrapperException : Exception
{
public Type ExceptionType { get; }
public OrleansWrapperException(Exception wrapped, Type exceptionType)
: base(FormatMessage(wrapped, exceptionType))
{
ExceptionType = exceptionType;
}
protected OrleansWrapperException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
ExceptionType = Type.GetType(info.GetString(nameof(ExceptionType))!)!;
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(nameof(ExceptionType), ExceptionType.AssemblyQualifiedName);
base.GetObjectData(info, context);
}
private static string FormatMessage(Exception wrapped, Type exceptionType)
{
var sb = new StringBuilder();
sb.AppendLine($"Wrapping exception of type {exceptionType}, because original exception is not serialized.");
sb.AppendLine();
sb.AppendLine("Original exception:");
sb.AppendLine(wrapped.ToString());
return sb.ToString();
}
}
}

25
backend/src/Squidex.Infrastructure/States/InconsistentStateException.cs

@ -13,39 +13,30 @@ namespace Squidex.Infrastructure.States
[Serializable] [Serializable]
public class InconsistentStateException : Exception public class InconsistentStateException : Exception
{ {
private readonly long currentVersion; public long CurrentVersion { get; }
private readonly long expectedVersion;
public long CurrentVersion public long ExpectedVersion { get; }
{
get { return currentVersion; }
}
public long ExpectedVersion
{
get { return expectedVersion; }
}
public InconsistentStateException(long currentVersion, long expectedVersion, Exception? inner = null) public InconsistentStateException(long currentVersion, long expectedVersion, Exception? inner = null)
: base(FormatMessage(currentVersion, expectedVersion), inner) : base(FormatMessage(currentVersion, expectedVersion), inner)
{ {
this.currentVersion = currentVersion; CurrentVersion = currentVersion;
this.expectedVersion = expectedVersion; ExpectedVersion = expectedVersion;
} }
protected InconsistentStateException(SerializationInfo info, StreamingContext context) protected InconsistentStateException(SerializationInfo info, StreamingContext context)
: base(info, context) : base(info, context)
{ {
currentVersion = info.GetInt64(nameof(currentVersion)); CurrentVersion = info.GetInt64(nameof(CurrentVersion));
expectedVersion = info.GetInt64(nameof(expectedVersion)); ExpectedVersion = info.GetInt64(nameof(ExpectedVersion));
} }
public override void GetObjectData(SerializationInfo info, StreamingContext context) public override void GetObjectData(SerializationInfo info, StreamingContext context)
{ {
info.AddValue(nameof(currentVersion), currentVersion); info.AddValue(nameof(CurrentVersion), CurrentVersion);
info.AddValue(nameof(expectedVersion), expectedVersion); info.AddValue(nameof(ExpectedVersion), ExpectedVersion);
base.GetObjectData(info, context); base.GetObjectData(info, context);
} }

1
backend/src/Squidex/Config/Orleans/OrleansServices.cs

@ -59,6 +59,7 @@ namespace Squidex.Config.Orleans
options.HostSelf = false; options.HostSelf = false;
}); });
builder.AddIncomingGrainCallFilter<ExceptionWrapperFilter>();
builder.AddIncomingGrainCallFilter<ActivationLimiterFilter>(); builder.AddIncomingGrainCallFilter<ActivationLimiterFilter>();
builder.AddIncomingGrainCallFilter<LocalCacheFilter>(); builder.AddIncomingGrainCallFilter<LocalCacheFilter>();
builder.AddIncomingGrainCallFilter<LoggingFilter>(); builder.AddIncomingGrainCallFilter<LoggingFilter>();

75
backend/tests/Squidex.Infrastructure.Tests/Orleans/ExceptionWrapperFilterTests.cs

@ -0,0 +1,75 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Orleans;
using Squidex.Infrastructure.TestHelpers;
using Xunit;
namespace Squidex.Infrastructure.Orleans
{
public class ExceptionWrapperFilterTests
{
private readonly IIncomingGrainCallContext context = A.Fake<IIncomingGrainCallContext>();
private readonly ExceptionWrapperFilter sut;
private sealed class InvalidException : Exception
{
public InvalidException(string message)
: base(message)
{
}
}
public ExceptionWrapperFilterTests()
{
sut = new ExceptionWrapperFilter();
}
[Fact]
public async Task Should_just_forward_serializable_exception()
{
var original = new InvalidOperationException();
A.CallTo(() => context.Invoke())
.Throws(original);
var ex = await Assert.ThrowsAnyAsync<Exception>(() => sut.Invoke(context));
Assert.Same(ex, original);
}
[Fact]
public async Task Should_wrap_non_serializable_exception()
{
var original = new InvalidException("My Message");
A.CallTo(() => context.Invoke())
.Throws(original);
var ex = await Assert.ThrowsAnyAsync<OrleansWrapperException>(() => sut.Invoke(context));
Assert.Equal(original.GetType(), ex.ExceptionType);
Assert.Contains(original.Message, ex.Message);
}
[Fact]
public void Should_serialize_and_deserialize()
{
var original = new InvalidException("My Message");
var source = new OrleansWrapperException(original, original.GetType());
var result = source.SerializeAndDeserializeBinary();
Assert.Equal(result.ExceptionType, source.ExceptionType);
Assert.Equal(result.Message, source.Message);
}
}
}
Loading…
Cancel
Save