Browse Source

Json simplified

pull/169/head
Sebastian Stehle 9 years ago
parent
commit
4566066443
  1. 6
      src/Squidex.Domain.Apps.Read/State/Orleans/AppStateEventConsumer.cs
  2. 16
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/IAppStateGrain.cs
  3. 28
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrain.cs
  4. 17
      src/Squidex.Infrastructure/Json/Orleans/IJsonValue.cs
  5. 57
      src/Squidex.Infrastructure/Json/Orleans/J.cs
  6. 18
      src/Squidex.Infrastructure/Json/Orleans/JExtensions.cs
  7. 21
      src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs
  8. 4
      src/Squidex/Config/Orleans/CustomJsonSerializer.cs
  9. 55
      tests/Squidex.Infrastructure.Tests/Json/Orleans/JsonExternalSerializerTests.cs

6
src/Squidex.Domain.Apps.Read/State/Orleans/AppStateEventConsumer.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// StateEventConsumer.cs // AppStateEventConsumer.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -8,12 +8,12 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans; using Orleans;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.State.Orleans.Grains; using Squidex.Domain.Apps.Read.State.Orleans.Grains;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.State.Orleans namespace Squidex.Domain.Apps.Read.State.Orleans
@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Read.State.Orleans
{ {
var appGrain = factory.GetGrain<IAppStateGrain>(appEvent.AppId.Name); var appGrain = factory.GetGrain<IAppStateGrain>(appEvent.AppId.Name);
await appGrain.HandleAsync(new Immutable<Envelope<IEvent>>(@event)); await appGrain.HandleAsync(@event.AsJ());
} }
if (@event.Payload is AppContributorAssigned contributorAssigned) if (@event.Payload is AppContributorAssigned contributorAssigned)

16
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/IAppStateGrain.cs

@ -10,28 +10,28 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans; using Orleans;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules; using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains namespace Squidex.Domain.Apps.Read.State.Orleans.Grains
{ {
public interface IAppStateGrain : IGrainWithStringKey public interface IAppStateGrain : IGrainWithStringKey
{ {
Task<Immutable<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id); Task<J<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id);
Task<Immutable<IAppEntity>> GetAppAsync(); Task<J<IAppEntity>> GetAppAsync();
Task<Immutable<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false); Task<J<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false);
Task<Immutable<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false); Task<J<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false);
Task<Immutable<List<ISchemaEntity>>> GetSchemasAsync(); Task<J<List<ISchemaEntity>>> GetSchemasAsync();
Task<Immutable<List<IRuleEntity>>> GetRulesAsync(); Task<J<List<IRuleEntity>>> GetRulesAsync();
Task HandleAsync(Immutable<Envelope<IEvent>> message); Task HandleAsync(J<Envelope<IEvent>> message);
} }
} }

28
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrain.cs

@ -9,7 +9,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans.Concurrency;
using Orleans.Runtime; using Orleans.Runtime;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
@ -18,6 +17,7 @@ using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
@ -57,49 +57,49 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
return base.OnActivateAsync(); return base.OnActivateAsync();
} }
public Task<Immutable<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id) public Task<J<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id)
{ {
var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted); var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted);
return Task.FromResult((State.GetApp(), schema).AsImmutable()); return Task.FromResult((State.GetApp(), schema).AsJ());
} }
public Task<Immutable<IAppEntity>> GetAppAsync() public Task<J<IAppEntity>> GetAppAsync()
{ {
var value = State.GetApp(); var value = State.GetApp();
return Task.FromResult(value.AsImmutable()); return Task.FromResult(value.AsJ());
} }
public Task<Immutable<List<IRuleEntity>>> GetRulesAsync() public Task<J<List<IRuleEntity>>> GetRulesAsync()
{ {
var value = State.FindRules(); var value = State.FindRules();
return Task.FromResult(value.AsImmutable()); return Task.FromResult(value.AsJ());
} }
public Task<Immutable<List<ISchemaEntity>>> GetSchemasAsync() public Task<J<List<ISchemaEntity>>> GetSchemasAsync()
{ {
var value = State.FindSchemas(x => !x.IsDeleted); var value = State.FindSchemas(x => !x.IsDeleted);
return Task.FromResult(value.AsImmutable()); return Task.FromResult(value.AsJ());
} }
public Task<Immutable<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false) public Task<J<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false)
{ {
var value = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted)); var value = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted));
return Task.FromResult(value.AsImmutable()); return Task.FromResult(value.AsJ());
} }
public Task<Immutable<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false) public Task<J<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false)
{ {
var value = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted)); var value = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted));
return Task.FromResult(value.AsImmutable()); return Task.FromResult(value.AsJ());
} }
public Task HandleAsync(Immutable<Envelope<IEvent>> message) public Task HandleAsync(J<Envelope<IEvent>> message)
{ {
if (exception != null) if (exception != null)
{ {

17
src/Squidex.Infrastructure/Json/Orleans/IJsonValue.cs

@ -0,0 +1,17 @@
// ==========================================================================
// IJsonValue.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure.Json.Orleans
{
public interface IJsonValue
{
object Value { get; }
bool IsImmutable { get; }
}
}

57
src/Squidex.Infrastructure/Json/Orleans/J.cs

@ -0,0 +1,57 @@
// ==========================================================================
// J.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Orleans
{
public struct J<T> : IJsonValue
{
private readonly T value;
private readonly bool isImmutable;
public T Value
{
get { return value; }
}
bool IJsonValue.IsImmutable
{
get { return isImmutable; }
}
object IJsonValue.Value
{
get { return Value; }
}
[JsonConstructor]
public J(T value, bool isImmutable = false)
{
this.value = value;
this.isImmutable = isImmutable;
}
public static implicit operator T(J<T> value)
{
return value.Value;
}
public static implicit operator J<T>(T d)
{
return new J<T>(d);
}
public static Task<J<T>> AsTask(T value)
{
return Task.FromResult<J<T>>(value);
}
}
}

18
src/Squidex.Infrastructure/Json/Orleans/JExtensions.cs

@ -0,0 +1,18 @@
// ==========================================================================
// JExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Infrastructure.Json.Orleans
{
public static class JExtensions
{
public static J<T> AsJ<T>(this T value, bool immutable = true)
{
return new J<T>(value, immutable);
}
}
}

21
src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs

@ -7,8 +7,8 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Orleans.Runtime; using Orleans.Runtime;
@ -19,15 +19,12 @@ namespace Squidex.Infrastructure.Json.Orleans
public class JsonExternalSerializer : IExternalSerializer public class JsonExternalSerializer : IExternalSerializer
{ {
private readonly JsonSerializer serializer; private readonly JsonSerializer serializer;
private readonly HashSet<Type> types;
public JsonExternalSerializer(JsonSerializer serializer, params Type[] types) public JsonExternalSerializer(JsonSerializer serializer)
{ {
Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(serializer, nameof(serializer));
this.serializer = serializer; this.serializer = serializer;
this.types = new HashSet<Type>(types);
} }
public void Initialize(Logger logger) public void Initialize(Logger logger)
@ -36,15 +33,25 @@ namespace Squidex.Infrastructure.Json.Orleans
public bool IsSupportedType(Type itemType) public bool IsSupportedType(Type itemType)
{ {
return types.Contains(itemType); return itemType.GetInterfaces().Contains(typeof(IJsonValue));
} }
public object DeepCopy(object source, ICopyContext context) public object DeepCopy(object source, ICopyContext context)
{ {
if (source == null) var jsonValue = source as IJsonValue;
if (jsonValue == null)
{ {
return null; return null;
} }
else if (jsonValue.IsImmutable)
{
return jsonValue;
}
else if (jsonValue.Value == null)
{
return jsonValue;
}
else else
{ {
return JObject.FromObject(source, serializer).ToObject(source.GetType(), serializer); return JObject.FromObject(source, serializer).ToObject(source.GetType(), serializer);

4
src/Squidex/Config/Orleans/CustomJsonSerializer.cs

@ -8,7 +8,6 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Config.Domain; using Squidex.Config.Domain;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans; using Squidex.Infrastructure.Json.Orleans;
namespace Squidex.Config.Orleans namespace Squidex.Config.Orleans
@ -16,8 +15,7 @@ namespace Squidex.Config.Orleans
public class CustomJsonSerializer : JsonExternalSerializer public class CustomJsonSerializer : JsonExternalSerializer
{ {
public CustomJsonSerializer() public CustomJsonSerializer()
: base(JsonSerializer.Create(SerializationServices.DefaultJsonSettings), : base(JsonSerializer.Create(SerializationServices.DefaultJsonSettings))
typeof(Envelope<IEvent>))
{ {
} }
} }

55
tests/Squidex.Infrastructure.Tests/Json/Orleans/JsonExternalSerializerTests.cs

@ -22,38 +22,54 @@ namespace Squidex.Infrastructure.Json.Orleans
[Fact] [Fact]
public void Should_serialize_js_only() public void Should_serialize_js_only()
{ {
var serializer = new JsonExternalSerializer(JsonSerializer.CreateDefault(), typeof(int), typeof(bool)); Assert.True(sut.IsSupportedType(typeof(J<int>)));
Assert.True(sut.IsSupportedType(typeof(J<List<int>>)));
Assert.True(sut.IsSupportedType(typeof(int))); Assert.False(sut.IsSupportedType(typeof(int)));
Assert.True(sut.IsSupportedType(typeof(bool))); Assert.False(sut.IsSupportedType(typeof(List<int>)));
Assert.False(sut.IsSupportedType(typeof(float)));
Assert.False(sut.IsSupportedType(typeof(double)));
} }
[Fact] [Fact]
public void Should_copy_null() public void Should_copy_null()
{ {
var value = (string)null; var v = (string)null;
var copy = sut.DeepCopy(value, null); var c = DeepCopy(v);
Assert.Null(c);
}
[Fact]
public void Should_copy_null_json()
{
var v = new J<List<int>>(null);
var c = DeepCopy(v);
Assert.Null(c.Value);
}
[Fact]
public void Should_not_copy_immutable_values()
{
var v = new List<int> { 1, 2, 3 }.AsJ(true);
var c = DeepCopy(v);
Assert.Null(copy); Assert.Same(v.Value, c.Value);
} }
[Fact] [Fact]
public void Should_copy_non_immutable_values() public void Should_copy_non_immutable_values()
{ {
var value = new List<int> { 1, 2, 3 }; var value = new J<List<int>>(new List<int> { 1, 2, 3 });
var copy = (List<int>)sut.DeepCopy(value, null); var copy = (J<List<int>>)sut.DeepCopy(value, null);
Assert.Equal(value, copy); Assert.Equal(value.Value, copy.Value);
Assert.NotSame(value, copy); Assert.NotSame(value.Value, copy.Value);
} }
[Fact] [Fact]
public void Should_serialize_and_deserialize_value() public void Should_serialize_and_deserialize_value()
{ {
var value = new List<int>(new List<int> { 1, 2, 3 }); var value = new J<List<int>>(new List<int> { 1, 2, 3 });
var writtenLength = 0; var writtenLength = 0;
var writtenBuffer = (byte[])null; var writtenBuffer = (byte[])null;
@ -78,10 +94,15 @@ namespace Squidex.Infrastructure.Json.Orleans
A.CallTo(() => reader.ReadBytes(writtenLength)) A.CallTo(() => reader.ReadBytes(writtenLength))
.Returns(writtenBuffer); .Returns(writtenBuffer);
var copy = (List<int>)sut.Deserialize(value.GetType(), readerContext); var copy = (J<List<int>>)sut.Deserialize(value.GetType(), readerContext);
Assert.Equal(value, copy); Assert.Equal(value.Value, copy.Value);
Assert.NotSame(value, copy); Assert.NotSame(value.Value, copy.Value);
}
private T DeepCopy<T>(T value)
{
return (T)sut.DeepCopy(value, null);
} }
} }
} }

Loading…
Cancel
Save