From e5adff13e62c0db79d8cb69886b24fcf80ece772 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 14 Oct 2017 19:37:00 +0200 Subject: [PATCH] Performance improvements and JSON converter --- .../Schemas/Json/JsonSchemaModel.cs | 55 +++++++++ .../Schemas/Json/SchemaConverter.cs | 41 +++++++ .../Schemas/Json/SchemaJsonSerializer.cs | 89 -------------- .../Contents/Extensions.cs | 72 ++++++++++++ .../Contents/MongoContentEntity.cs | 83 ++----------- .../MongoContentRepository_EventHandling.cs | 26 +++-- .../Schemas/MongoSchemaEntity.cs | 41 +++---- .../Schemas/MongoSchemaRepository.cs | 13 +-- .../MongoSchemaRepository_EventHandling.cs | 24 ++-- .../MongoDb/BsonConverter.cs | 109 ++++++++++++++++++ .../Config/Domain/InfrastructureModule.cs | 4 - src/Squidex/Config/Domain/Serializers.cs | 5 + .../Schemas/Json/JsonSerializerTests.cs | 16 +-- 13 files changed, 345 insertions(+), 233 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaConverter.cs delete mode 100644 src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaJsonSerializer.cs create mode 100644 src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs create mode 100644 src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Json/JsonSchemaModel.cs b/src/Squidex.Domain.Apps.Core/Schemas/Json/JsonSchemaModel.cs index 6a3205356..2d9d79d92 100644 --- a/src/Squidex.Domain.Apps.Core/Schemas/Json/JsonSchemaModel.cs +++ b/src/Squidex.Domain.Apps.Core/Schemas/Json/JsonSchemaModel.cs @@ -7,6 +7,8 @@ // ========================================================================== using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; using Newtonsoft.Json; namespace Squidex.Domain.Apps.Core.Schemas.Json @@ -24,5 +26,58 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json [JsonProperty] public List Fields { get; set; } + + public JsonSchemaModel() + { + } + + public JsonSchemaModel(Schema schema) + { + var model = new JsonSchemaModel { Name = schema.Name, IsPublished = schema.IsPublished, Properties = schema.Properties }; + + Fields = + schema.Fields.Select(x => + new JsonFieldModel + { + Id = x.Id, + Name = x.Name, + IsHidden = x.IsHidden, + IsLocked = x.IsLocked, + IsDisabled = x.IsDisabled, + Partitioning = x.Partitioning.Key, + Properties = x.RawProperties + }).ToList(); + } + + public Schema ToSchema(FieldRegistry fieldRegistry) + { + var fields = Fields?.Select(fieldModel => + { + var parititonKey = new Partitioning(fieldModel.Partitioning); + + var field = fieldRegistry.CreateField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties); + + if (fieldModel.IsDisabled) + { + field = field.Disable(); + } + + if (fieldModel.IsLocked) + { + field = field.Lock(); + } + + if (fieldModel.IsHidden) + { + field = field.Hide(); + } + + return field; + }).ToImmutableList() ?? ImmutableList.Empty; + + var schema = new Schema(Name, IsPublished, Properties, fields); + + return schema; + } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaConverter.cs b/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaConverter.cs new file mode 100644 index 000000000..5502fd3f9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaConverter.cs @@ -0,0 +1,41 @@ +// ========================================================================== +// SchemaConverter.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Newtonsoft.Json; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Core.Schemas.Json +{ + public sealed class SchemaConverter : JsonConverter + { + private readonly FieldRegistry fieldRegistry; + + public SchemaConverter(FieldRegistry fieldRegistry) + { + Guard.NotNull(fieldRegistry, nameof(fieldRegistry)); + + this.fieldRegistry = fieldRegistry; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + serializer.Serialize(writer, new JsonSchemaModel((Schema)value)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return serializer.Deserialize(reader).ToSchema(fieldRegistry); + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Schema); + } + } +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaJsonSerializer.cs b/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaJsonSerializer.cs deleted file mode 100644 index 5b80a49ec..000000000 --- a/src/Squidex.Domain.Apps.Core/Schemas/Json/SchemaJsonSerializer.cs +++ /dev/null @@ -1,89 +0,0 @@ -// ========================================================================== -// SchemaJsonSerializer.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Collections.Immutable; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Core.Schemas.Json -{ - public sealed class SchemaJsonSerializer - { - private readonly FieldRegistry fieldRegistry; - private readonly JsonSerializer serializer; - - public SchemaJsonSerializer(FieldRegistry fieldRegistry, JsonSerializerSettings serializerSettings) - { - Guard.NotNull(fieldRegistry, nameof(fieldRegistry)); - Guard.NotNull(serializerSettings, nameof(serializerSettings)); - - this.fieldRegistry = fieldRegistry; - - serializer = JsonSerializer.Create(serializerSettings); - } - - public JToken Serialize(Schema schema) - { - var model = new JsonSchemaModel { Name = schema.Name, IsPublished = schema.IsPublished, Properties = schema.Properties }; - - model.Fields = - schema.Fields.Select(x => - new JsonFieldModel - { - Id = x.Id, - Name = x.Name, - IsHidden = x.IsHidden, - IsLocked = x.IsLocked, - IsDisabled = x.IsDisabled, - Partitioning = x.Partitioning.Key, - Properties = x.RawProperties - }).ToList(); - - return JToken.FromObject(model, serializer); - } - - public Schema Deserialize(JToken token) - { - var model = token.ToObject(serializer); - - var fields = - model.Fields.Select(fieldModel => - { - var parititonKey = new Partitioning(fieldModel.Partitioning); - - var field = fieldRegistry.CreateField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties); - - if (fieldModel.IsDisabled) - { - field = field.Disable(); - } - - if (fieldModel.IsLocked) - { - field = field.Lock(); - } - - if (fieldModel.IsHidden) - { - field = field.Hide(); - } - - return field; - }).ToImmutableList(); - - var schema = - new Schema( - model.Name, - model.IsPublished, model.Properties, fields); - - return schema; - } - } -} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs new file mode 100644 index 000000000..09d65b1b1 --- /dev/null +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs @@ -0,0 +1,72 @@ +// ========================================================================== +// Extensions.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MongoDB.Bson; +using Newtonsoft.Json.Linq; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.MongoDb; + +namespace Squidex.Domain.Apps.Read.MongoDb.Contents +{ + public static class Extensions + { + private const int MaxLength = 1024 * 1024; + + public static BsonDocument ToBsonDocument(this IdContentData data) + { + return (BsonDocument)JToken.FromObject(data).ToBson(); + } + + public static List ToReferencedIds(this IdContentData data, Schema schema) + { + return data.GetReferencedIds(schema).ToList(); + } + + public static NamedContentData ToData(this BsonDocument document, Schema schema, List deletedIds) + { + return document + .ToJson() + .ToObject() + .ToCleanedReferences(schema, new HashSet(deletedIds)) + .ToNameModel(schema, true); + } + + public static string ToFullText(this ContentData data) + { + var stringBuilder = new StringBuilder(); + + foreach (var text in data.Values.SelectMany(x => x.Values).Where(x => x != null).OfType()) + { + if (text.Type == JTokenType.String) + { + var value = text.ToString(); + + if (value.Length < 1000) + { + stringBuilder.Append(" "); + stringBuilder.Append(text); + } + } + } + + var result = stringBuilder.ToString(); + + if (result.Length > MaxLength) + { + result = result.Substring(MaxLength); + } + + return result; + } + } +} diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs index 1c4f7f0ed..de7326b63 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs @@ -8,27 +8,22 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using MongoDB.Bson; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Attributes; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Read.Contents; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; -using JsonConvert = Newtonsoft.Json.JsonConvert; namespace Squidex.Domain.Apps.Read.MongoDb.Contents { public sealed class MongoContentEntity : IContentEntity, IMongoEntity { - private const int MaxLength = 1024 * 1024; private static readonly JsonWriterSettings Settings = new JsonWriterSettings { OutputMode = JsonOutputMode.Strict }; - private NamedContentData contentData; + private NamedContentData data; [BsonId] [BsonElement] @@ -74,7 +69,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents [BsonRequired] [BsonElement("do")] - public BsonDocument DataObject { get; set; } + public BsonDocument DataDocument { get; set; } [BsonRequired] [BsonElement("rf")] @@ -86,80 +81,22 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents NamedContentData IContentEntity.Data { - get - { - return contentData; - } + get { return data; } } public void ParseData(Schema schema) { - if (DataObject != null) - { - var jsonString = DataObject.ToJson(Settings); - - contentData = - JsonConvert.DeserializeObject(jsonString) - .ToCleanedReferences(schema, new HashSet(ReferencedIdsDeleted)) - .ToNameModel(schema, true); - } - else - { - contentData = null; - } + data = DataDocument.ToData(schema, ReferencedIdsDeleted); } - public void SetData(Schema schema, NamedContentData newContentData) + public void SetData(Schema schema, NamedContentData data) { - if (newContentData != null) - { - var idModel = newContentData.ToIdModel(schema, true); - - var jsonString = JsonConvert.SerializeObject(idModel); - - DataObject = BsonDocument.Parse(jsonString); - DataText = ExtractText(idModel); - - ReferencedIds = idModel.GetReferencedIds(schema).ToList(); - } - else - { - DataObject = null; - DataText = string.Empty; - } - } + var idData = data?.ToIdModel(schema, true); - private static string ExtractText(IdContentData data) - { - if (data == null) - { - return string.Empty; - } - - var stringBuilder = new StringBuilder(); - - foreach (var text in data.Values.SelectMany(x => x.Values).Where(x => x != null).OfType()) - { - if (text.Type == JTokenType.String) - { - var value = text.ToString(); - - if (value.Length < 1000) - { - stringBuilder.Append(" "); - stringBuilder.Append(text); - } - } - } - - var result = stringBuilder.ToString(); - - if (result.Length > MaxLength) - { - result = result.Substring(MaxLength); - } - - return result; + DataText = idData?.ToFullText(); + DataDocument = idData?.ToBsonDocument(); + + ReferencedIds = idData?.ToReferencedIds(schema); } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs index cf6e11b2a..b5f394873 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs @@ -85,10 +85,17 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents { return ForSchemaAsync(@event.AppId.Id, @event.SchemaId.Id, (collection, schema) => { - return collection.UpdateAsync(@event, headers, x => - { - x.SetData(schema.SchemaDef, @event.Data); - }); + var idData = @event.Data.ToIdModel(schema.SchemaDef, true); + + return collection.UpdateOneAsync( + Filter.Eq(x => x.Id, @event.ContentId), + Update + .Set(x => x.ReferencedIds, idData.ToReferencedIds(schema.SchemaDef)) + .Set(x => x.DataText, idData.ToFullText()) + .Set(x => x.DataDocument, idData.ToBsonDocument()) + .Set(x => x.LastModified, headers.Timestamp()) + .Set(x => x.LastModifiedBy, @event.Actor) + .Set(x => x.Version, headers.EventStreamNumber())); }); } @@ -96,10 +103,13 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents { return ForAppIdAsync(@event.AppId.Id, collection => { - return collection.UpdateAsync(@event, headers, x => - { - x.Status = @event.Status; - }); + return collection.UpdateOneAsync( + Filter.Eq(x => x.Id, @event.ContentId), + Update + .Set(x => x.Status, @event.Status) + .Set(x => x.LastModified, headers.Timestamp()) + .Set(x => x.LastModifiedBy, @event.Actor) + .Set(x => x.Version, headers.EventStreamNumber())); }); } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs index 56179470a..0b2c35117 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs @@ -7,10 +7,10 @@ // ========================================================================== using System; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; @@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity { - private Lazy schema; + private Schema schema; [BsonRequired] [BsonElement] @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas [BsonRequired] [BsonElement] - public string Schema { get; set; } + public BsonDocument SchemaDocument { get; set; } [BsonRequired] [BsonElement] @@ -73,31 +73,22 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas [BsonElement] public string ScriptChange { get; set; } - Schema ISchemaEntity.SchemaDef + public Schema SchemaDef { - get { return schema.Value; } - } - - public void SerializeSchema(Schema newSchema, SchemaJsonSerializer serializer) - { - Schema = serializer.Serialize(newSchema).ToString(); - schema = new Lazy(() => newSchema); - - IsPublished = newSchema.IsPublished; - } - - public void UpdateSchema(SchemaJsonSerializer serializer, Func updater) - { - DeserializeSchema(serializer); - - SerializeSchema(updater(schema.Value), serializer); - } + get + { + if (schema == null) + { + schema = SchemaDocument.ToJson().ToObject(); + } - public void DeserializeSchema(SchemaJsonSerializer serializer) - { - if (schema == null) + return schema; + } + set { - schema = new Lazy(() => Schema != null ? serializer.Deserialize(JObject.Parse(Schema)) : null); + schema = value; + + SchemaDocument = (BsonDocument)JToken.FromObject(schema).ToBson(); } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs index 8a732c01e..a43da664f 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs @@ -12,7 +12,6 @@ using System.Linq; using System.Threading.Tasks; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas.Repositories; using Squidex.Infrastructure; @@ -23,18 +22,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { public partial class MongoSchemaRepository : MongoRepositoryBase, ISchemaRepository, IEventConsumer { - private readonly SchemaJsonSerializer serializer; private readonly FieldRegistry registry; - public MongoSchemaRepository(IMongoDatabase database, SchemaJsonSerializer serializer, FieldRegistry registry) + public MongoSchemaRepository(IMongoDatabase database, FieldRegistry registry) : base(database) { Guard.NotNull(registry, nameof(registry)); - Guard.NotNull(serializer, nameof(serializer)); this.registry = registry; - - this.serializer = serializer; } protected override string CollectionName() @@ -55,8 +50,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.AppId == appId && !s.IsDeleted) .ToListAsync(); - schemaEntities.ForEach(x => x.DeserializeSchema(serializer)); - return schemaEntities.OfType().ToList(); } @@ -66,8 +59,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name) .FirstOrDefaultAsync(); - schemaEntity?.DeserializeSchema(serializer); - return schemaEntity; } @@ -77,8 +68,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.Id == schemaId) .FirstOrDefaultAsync(); - schemaEntity?.DeserializeSchema(serializer); - return schemaEntity; } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs index fb9a5a5af..8160e7488 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs @@ -18,6 +18,8 @@ using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Reflection; +#pragma warning disable CS0612 // Type or member is obsolete + namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { public partial class MongoSchemaRepository @@ -114,31 +116,29 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); } - private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func updater) + protected Task On(WebhookAdded @event, EnvelopeHeaders headers) { - return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater)); + return Collection.UpdateAsync(@event, headers, e => { }); } - private void UpdateSchema(MongoSchemaEntity entity, Func updater) + protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) { - entity.UpdateSchema(serializer, updater); + return Collection.UpdateAsync(@event, headers, e => { }); } - private void UpdateSchema(MongoSchemaEntity entity, Schema schema) + private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func updater) { - entity.SerializeSchema(schema, serializer); + return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater)); } -#pragma warning disable CS0612 // Type or member is obsolete - protected Task On(WebhookAdded @event, EnvelopeHeaders headers) + private void UpdateSchema(MongoSchemaEntity entity, Func updater) { - return Collection.UpdateAsync(@event, headers, e => { }); + entity.SchemaDef = updater(entity.SchemaDef); } - protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) + private void UpdateSchema(MongoSchemaEntity entity, Schema schema) { - return Collection.UpdateAsync(@event, headers, e => { }); + entity.SchemaDef = schema; } -#pragma warning restore CS0612 // Type or member is obsolete } } diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs new file mode 100644 index 000000000..e195c2ab1 --- /dev/null +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs @@ -0,0 +1,109 @@ +// ========================================================================== +// BsonConverter.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using MongoDB.Bson; +using Newtonsoft.Json.Linq; + +namespace Squidex.Infrastructure.MongoDb +{ + public static class BsonConverter + { + public static BsonDocument ToBson(this JObject source) + { + var result = new BsonDocument(); + + foreach (var property in source) + { + result.Add(property.Key, property.Value.ToBson()); + } + + return result; + } + + public static JObject ToJson(this BsonDocument source) + { + var result = new JObject(); + + foreach (var property in source) + { + result.Add(property.Name, property.Value.ToJson()); + } + + return result; + } + + public static BsonArray ToBson(this JArray source) + { + var result = new BsonArray(); + + foreach (var item in source) + { + result.Add(item.ToBson()); + } + + return result; + } + + public static JArray ToJson(this BsonArray source) + { + var result = new JArray(); + + foreach (var item in source) + { + result.Add(item.ToJson()); + } + + return result; + } + + public static BsonValue ToBson(this JToken source) + { + switch (source) + { + case JObject jObject: + return jObject.ToBson(); + case JArray jArray: + return jArray.ToBson(); + case JValue jValue: + return BsonValue.Create(jValue.Value); + } + + throw new NotSupportedException($"Cannot convert {source.GetType()} to Bson."); + } + + public static JToken ToJson(this BsonValue source) + { + switch (source.BsonType) + { + case BsonType.Document: + return source.AsBsonDocument.ToJson(); + case BsonType.Array: + return source.AsBsonArray.ToJson(); + case BsonType.Double: + return new JValue(source.AsDouble); + case BsonType.String: + return new JValue(source.AsString); + case BsonType.Boolean: + return new JValue(source.AsBoolean); + case BsonType.DateTime: + return new JValue(source.ToUniversalTime()); + case BsonType.Int32: + return new JValue(source.AsInt32); + case BsonType.Int64: + return new JValue(source.AsInt64); + case BsonType.Decimal128: + return new JValue(source.AsDecimal); + case BsonType.Null: + return JValue.CreateNull(); + } + + throw new NotSupportedException($"Cannot convert {source.GetType()} to Json."); + } + } +} \ No newline at end of file diff --git a/src/Squidex/Config/Domain/InfrastructureModule.cs b/src/Squidex/Config/Domain/InfrastructureModule.cs index 86afbdd22..236e61383 100644 --- a/src/Squidex/Config/Domain/InfrastructureModule.cs +++ b/src/Squidex/Config/Domain/InfrastructureModule.cs @@ -153,10 +153,6 @@ namespace Squidex.Config.Domain .AsSelf() .SingleInstance(); - builder.RegisterType() - .AsSelf() - .SingleInstance(); - builder.RegisterType() .AsSelf() .SingleInstance(); diff --git a/src/Squidex/Config/Domain/Serializers.cs b/src/Squidex/Config/Domain/Serializers.cs index aba7c3559..89b8fe74d 100644 --- a/src/Squidex/Config/Domain/Serializers.cs +++ b/src/Squidex/Config/Domain/Serializers.cs @@ -12,6 +12,8 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using NodaTime; using NodaTime.Serialization.JsonNet; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; @@ -35,6 +37,7 @@ namespace Squidex.Config.Domain new NamedStringIdConverter(), new PropertiesBagConverter(), new RefTokenConverter(), + new SchemaConverter(new FieldRegistry(TypeNameRegistry)), new StringEnumConverter()); settings.NullValueHandling = NullValueHandling.Ignore; @@ -46,6 +49,8 @@ namespace Squidex.Config.Domain settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); + JsonConvert.DefaultSettings = () => settings; + return settings; } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs index 674aa1d06..1acfe6d06 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Schemas/Json/JsonSerializerTests.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Immutable; using FluentAssertions; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Squidex.Infrastructure; using Squidex.Infrastructure.Json; using Xunit; @@ -20,14 +21,12 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json { private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); - private readonly SchemaJsonSerializer sut; public JsonSerializerTests() { serializerSettings.TypeNameHandling = TypeNameHandling.Auto; serializerSettings.SerializationBinder = new TypeNameSerializationBinder(typeNameRegistry); - - sut = new SchemaJsonSerializer(new FieldRegistry(typeNameRegistry), serializerSettings); + serializerSettings.Converters.Add(new SchemaConverter(new FieldRegistry(typeNameRegistry))); } [Fact] @@ -36,9 +35,9 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json var schema = Schema.Create("user", new SchemaProperties { Hints = "The User" }) .AddField(new JsonField(1, "my-json", Partitioning.Invariant, - new JsonFieldProperties())) + new JsonFieldProperties())).HideField(1) .AddField(new AssetsField(2, "my-assets", Partitioning.Invariant, - new AssetsFieldProperties())) + new AssetsFieldProperties())).LockField(2) .AddField(new StringField(3, "my-string1", Partitioning.Language, new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ImmutableList.Create("a", "b") })) .AddField(new StringField(4, "my-string2", Partitioning.Invariant, @@ -46,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json .AddField(new NumberField(5, "my-number", Partitioning.Invariant, new NumberFieldProperties { MinValue = 1, MaxValue = 10 })) .AddField(new BooleanField(6, "my-boolean", Partitioning.Invariant, - new BooleanFieldProperties())) + new BooleanFieldProperties())).DisableField(3) .AddField(new DateTimeField(7, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime })) .AddField(new DateTimeField(8, "my-date", Partitioning.Invariant, @@ -55,12 +54,9 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json new ReferencesFieldProperties { SchemaId = Guid.NewGuid() })) .AddField(new GeolocationField(10, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties())) - .HideField(1) - .LockField(2) - .DisableField(3) .Publish(); - var deserialized = sut.Deserialize(sut.Serialize(schema)); + var deserialized = JToken.FromObject(schema).ToObject(); deserialized.ShouldBeEquivalentTo(schema); }