From ca0161543c4133526b78033c5f32e8f46dc10eca Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 25 Nov 2018 15:44:19 +0100 Subject: [PATCH 01/15] Started with json migration. --- .../Apps/Json/AppClientsConverter.cs | 2 +- .../Apps/Json/AppContributorsConverter.cs | 2 +- .../Apps/Json/AppPatternsConverter.cs | 2 +- .../Apps/Json/LanguagesConfigConverter.cs | 2 +- .../Apps/Json/RolesConverter.cs | 2 +- .../Contents/ContentData.cs | 4 +- .../Contents/ContentFieldData.cs | 22 +-- .../Rules/Json/RuleConverter.cs | 2 +- .../Rules/RuleJob.cs | 5 +- .../Schemas/Json/SchemaConverter.cs | 3 +- .../ConvertContent/ContentConverter.cs | 19 +- .../ConvertContent/ContentConverterFlat.cs | 6 +- .../ConvertContent/FieldConverters.cs | 17 +- .../ConvertContent/Value.cs | 4 +- .../ConvertContent/ValueConverters.cs | 22 +-- .../EnrichContent/ContentEnricher.cs | 11 +- .../EnrichContent/DefaultValueFactory.cs | 50 ++--- .../ContentReferencesExtensions.cs | 4 +- .../ExtractReferenceIds/ReferencesCleaner.cs | 36 ++-- .../ReferencesExtensions.cs | 27 ++- .../ReferencesExtractor.cs | 12 +- .../ValueReferencesConverter.cs | 4 +- .../EnrichedEvents/EnrichedEvent.cs | 3 - .../HandleRules/IRuleActionHandler.cs | 7 +- .../HandleRules/RuleActionHandler.cs | 26 +-- .../HandleRules/RuleEventFormatter.cs | 38 ++-- .../HandleRules/RuleService.cs | 19 +- .../ContentWrapper/ContentFieldProperty.cs | 8 +- .../Scripting/ContentWrapper/JsonMapper.cs | 94 ++++------ .../Tags/TagNormalizer.cs | 34 ++-- .../ValidateContent/ContentValidator.cs | 5 +- .../ValidateContent/JsonValueConverter.cs | 175 ++++++++++++++---- .../Validators/FieldValidator.cs | 7 +- .../Validators/ObjectValidator.cs | 2 +- .../ValidateContent/ValidatorsFactory.cs | 4 +- .../EventSourcing/Formatter.cs | 4 +- .../EventSourcing/MongoEvent.cs | 8 +- .../CQRS/Events/RabbitMqEventConsumer.cs | 12 +- .../Assets/AssetFile.cs | 2 - .../DefaultEventDataFormatter.cs | 26 +-- .../EventSourcing/EventData.cs | 6 +- .../Json/IJsonSerializer.cs | 23 +++ .../Json/JsonExtension.cs | 34 ---- .../ClaimsPrincipalConverter.cs | 2 +- .../ConverterContractResolver.cs | 2 +- .../Json/{ => Newtonsoft}/InstantConverter.cs | 2 +- .../{ => Newtonsoft}/JsonClassConverter.cs | 2 +- .../Json/Newtonsoft/JsonValueConverter.cs | 161 ++++++++++++++++ .../{ => Newtonsoft}/LanguageConverter.cs | 2 +- .../{ => Newtonsoft}/NamedGuidIdConverter.cs | 2 +- .../{ => Newtonsoft}/NamedLongIdConverter.cs | 2 +- .../NamedStringIdConverter.cs | 2 +- .../Newtonsoft/NewtonsoftJsonSerializer.cs | 60 ++++++ .../PropertiesBagConverter.cs | 2 +- .../{ => Newtonsoft}/RefTokenConverter.cs | 2 +- .../TypeNameSerializationBinder.cs | 2 +- .../Json/Objects/IJsonValue.cs | 18 ++ .../Json/Objects/JsonArray.cs | 100 ++++++++++ .../Json/Objects/JsonNull.cs | 55 ++++++ .../Json/Objects/JsonObject.cs | 129 +++++++++++++ .../Json/Objects/JsonScalar.cs | 60 ++++++ .../Json/Objects/JsonValue.cs | 114 ++++++++++++ .../Json/Objects/JsonValueType.cs | 19 ++ src/Squidex.Infrastructure/Orleans/J.cs | 6 +- src/Squidex.Infrastructure/Orleans/J{T}.cs | 20 +- .../Model/Apps/AppClientJsonTests.cs | 8 +- .../Model/Apps/AppContributorsJsonTests.cs | 6 +- .../Model/Apps/AppPatternJsonTests.cs | 8 +- .../Model/Apps/AppPlanTests.cs | 10 +- .../Model/Apps/LanguagesConfigJsonTests.cs | 10 +- .../Model/Apps/RolesJsonTests.cs | 8 +- .../Model/Contents/ContentDataTests.cs | 3 +- .../Model/Rules/RuleTests.cs | 8 +- .../Model/Schemas/SchemaTests.cs | 5 +- .../ContentConversionFlatTests.cs | 46 +++-- .../ConvertContent/ContentConversionTests.cs | 6 +- .../ConvertContent/FieldConvertersTests.cs | 90 ++++----- .../ConvertContent/ValueConvertersTests.cs | 36 ++-- .../EnrichContent/ContentEnrichmentTests.cs | 38 ++-- .../ReferenceExtractionTests.cs | 36 ++-- .../HandleRules/RuleEventFormatterTests.cs | 9 +- .../HandleRules/RuleServiceTests.cs | 17 +- .../Scripting/ContentDataObjectTests.cs | 14 +- .../Operations/Tags/TagNormalizerTests.cs | 37 ++-- .../ValidateContent/ArrayFieldTests.cs | 14 +- .../ValidateContent/AssetsFieldTests.cs | 8 +- .../ValidateContent/BooleanFieldTests.cs | 8 +- .../ValidateContent/ContentValidationTests.cs | 15 +- .../ValidateContent/DateTimeFieldTests.cs | 14 +- .../ValidateContent/GeolocationFieldTests.cs | 45 ++--- .../ValidateContent/JsonFieldTests.cs | 8 +- .../ValidateContent/NumberFieldTests.cs | 12 +- .../ValidateContent/ReferencesFieldTests.cs | 8 +- .../ValidateContent/StringFieldTests.cs | 6 +- .../ValidateContent/TagsFieldTests.cs | 8 +- .../ValidationTestExtensions.cs | 6 +- .../TestData.cs | 13 +- .../DefaultEventDataFormatterTests.cs | 14 +- .../EventSourcing/EnvelopeTests.cs | 3 +- .../Json/ClaimsPrincipalConverterTests.cs | 10 +- .../Json/InstantConverterTests.cs | 12 +- .../ConverterContractResolverTests.cs | 6 +- .../Objects/JsonValuesSerializationTests.cs | 113 +++++++++++ .../LanguageTests.cs | 9 +- .../NamedIdTests.cs | 33 ++-- .../Orleans/JsonExternalSerializerTests.cs | 6 + .../PropertiesBagTests.cs | 5 +- .../RefTokenTests.cs | 9 +- .../TestHelpers/JsonHelper.cs | 65 +++---- 109 files changed, 1649 insertions(+), 755 deletions(-) create mode 100644 src/Squidex.Infrastructure/Json/IJsonSerializer.cs delete mode 100644 src/Squidex.Infrastructure/Json/JsonExtension.cs rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/ClaimsPrincipalConverter.cs (97%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/ConverterContractResolver.cs (96%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/InstantConverter.cs (97%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/JsonClassConverter.cs (96%) create mode 100644 src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/LanguageConverter.cs (95%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/NamedGuidIdConverter.cs (96%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/NamedLongIdConverter.cs (96%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/NamedStringIdConverter.cs (96%) create mode 100644 src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/PropertiesBagConverter.cs (97%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/RefTokenConverter.cs (95%) rename src/Squidex.Infrastructure/Json/{ => Newtonsoft}/TypeNameSerializationBinder.cs (96%) create mode 100644 src/Squidex.Infrastructure/Json/Objects/IJsonValue.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonArray.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonNull.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonObject.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonValue.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs rename tests/Squidex.Infrastructure.Tests/Json/{ => Newtonsoft}/ConverterContractResolverTests.cs (94%) create mode 100644 tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs index bde833f22..ecaac12c2 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs @@ -9,7 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Apps.Json { diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs index 997bcfe11..e72ecc0b0 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs @@ -9,7 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Apps.Json { diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs index 34a23812e..465029571 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs @@ -9,7 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Apps.Json { diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs index ed396589d..a229d5593 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs @@ -7,7 +7,7 @@ using System; using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Apps.Json { diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs index 043c2b4af..51b23d192 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs @@ -6,7 +6,7 @@ // ========================================================================== using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Security; using System; using System.Collections.Generic; diff --git a/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs b/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs index 2ebc1d054..fcfe4813e 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs @@ -9,7 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Contents { @@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Core.Contents { var resultValue = new ContentFieldData(); - foreach (var partitionValue in fieldValue.Value.Where(x => !x.Value.IsNull())) + foreach (var partitionValue in fieldValue.Value.Where(x => x.Value.Type != JsonValueType.Null)) { resultValue[partitionValue.Key] = partitionValue.Value; } diff --git a/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs b/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs index 9f4cf3a75..41afa54f3 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Contents/ContentFieldData.cs @@ -7,21 +7,24 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Contents { - public sealed class ContentFieldData : Dictionary, IEquatable + public sealed class ContentFieldData : Dictionary, IEquatable { - private static readonly JTokenEqualityComparer JTokenEqualityComparer = new JTokenEqualityComparer(); - public ContentFieldData() : base(StringComparer.OrdinalIgnoreCase) { } - public ContentFieldData AddValue(string key, JToken value) + public ContentFieldData AddValue(string key, object value) + { + return AddJsonValue(key, JsonValue.Create(value)); + } + + public ContentFieldData AddJsonValue(string key, IJsonValue value) { Guard.NotNullOrEmpty(key, nameof(key)); @@ -30,11 +33,6 @@ namespace Squidex.Domain.Apps.Core.Contents return this; } - public ContentFieldData AddValue(JToken value) - { - return AddValue(InvariantPartitioning.Instance.Master.Key, value); - } - public override bool Equals(object obj) { return Equals(obj as ContentFieldData); @@ -42,12 +40,12 @@ namespace Squidex.Domain.Apps.Core.Contents public bool Equals(ContentFieldData other) { - return other != null && (ReferenceEquals(this, other) || this.EqualsDictionary(other, EqualityComparer.Default, JTokenEqualityComparer)); + return other != null && (ReferenceEquals(this, other) || this.EqualsDictionary(other)); } public override int GetHashCode() { - return this.DictionaryHashCode(EqualityComparer.Default, JTokenEqualityComparer); + return this.DictionaryHashCode(); } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs index e9b8b77e4..7ffd04107 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs @@ -7,7 +7,7 @@ using System; using Newtonsoft.Json; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Rules.Json { diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs index c83cf9626..811cd51f5 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleJob.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using Newtonsoft.Json.Linq; using NodaTime; namespace Squidex.Domain.Apps.Core.Rules @@ -23,12 +22,12 @@ namespace Squidex.Domain.Apps.Core.Rules public string ActionName { get; set; } + public string ActionData { get; set; } + public string Description { get; set; } public Instant Created { get; set; } public Instant Expires { get; set; } - - public JObject ActionData { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs index 3ca34038b..8cd129428 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs @@ -7,8 +7,7 @@ using System; using Newtonsoft.Json; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Domain.Apps.Core.Schemas.Json { diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs index de8e250a7..8a9ba47cc 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverter.cs @@ -7,13 +7,12 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ConvertContent { @@ -41,11 +40,11 @@ namespace Squidex.Domain.Apps.Core.ConvertContent return result; } - private static void AppendText(JToken value, StringBuilder stringBuilder, int maxFieldLength, string separator, bool allowObjects) + private static void AppendText(IJsonValue value, StringBuilder stringBuilder, int maxFieldLength, string separator, bool allowObjects) { - if (value?.Type == JTokenType.String) + if (value.Type == JsonValueType.String) { - var text = ((JValue)value).ToString(CultureInfo.InvariantCulture); + var text = value.ToString(); if (text.Length <= maxFieldLength) { @@ -57,18 +56,18 @@ namespace Squidex.Domain.Apps.Core.ConvertContent stringBuilder.Append(text); } } - else if (value?.Type == JTokenType.Array) + else if (value is JsonArray array) { - foreach (var item in value) + foreach (var item in array) { AppendText(item, stringBuilder, maxFieldLength, separator, true); } } - else if (value?.Type == JTokenType.Object && allowObjects) + else if (value is JsonObject obj && allowObjects) { - foreach (JProperty property in value) + foreach (var item in obj.Values) { - AppendText(property.Value, stringBuilder, maxFieldLength, separator, true); + AppendText(item, stringBuilder, maxFieldLength, separator, true); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs index 5bff83392..8a6e82895 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ContentConverterFlat.cs @@ -7,10 +7,10 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ConvertContent { @@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent languagePreferences = languagePreferences.Union(languageConfig.LanguageFallbacks).ToList(); } - var result = new Dictionary(); + var result = new Dictionary(); foreach (var fieldValue in content) { @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent foreach (var language in languagePreferences) { - if (fieldData.TryGetValue(language, out var value) && value != null) + if (fieldData.TryGetValue(language, out var value) && value.Type != JsonValueType.Null) { result[fieldValue.Key] = value; diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs index 3ccbaf753..1052b8941 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs @@ -8,13 +8,12 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; #pragma warning disable RECS0002 // Convert anonymous method to method group @@ -44,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent { foreach (var value in data.Values) { - if (value.IsNull()) + if (value.Type == JsonValueType.Null) { continue; } @@ -78,13 +77,13 @@ namespace Squidex.Domain.Apps.Core.ConvertContent { foreach (var partition in data) { - if (partition.Value is JArray array) + if (partition.Value is JsonArray array) { for (var i = 0; i < array.Count; i++) { var id = array[i].ToString(); - array[i] = urlGenerator.GenerateUrl(id); + array[i] = JsonValue.Create(urlGenerator.GenerateUrl(id)); } } } @@ -273,16 +272,16 @@ namespace Squidex.Domain.Apps.Core.ConvertContent foreach (var partition in data) { - if (!(partition.Value is JArray jArray)) + if (!(partition.Value is JsonArray array)) { continue; } - var newArray = new JArray(); + var newArray = JsonValue.Array(); - foreach (var item in jArray.OfType()) + foreach (var item in array.OfType()) { - var newItem = new JObject(); + var newItem = JsonValue.Object(); foreach (var kvp in item) { diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs index 2229afdfa..a83740e60 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/Value.cs @@ -5,12 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ConvertContent { public static class Value { - public static readonly JToken Unset = JValue.CreateUndefined(); + public static readonly IJsonValue Unset = JsonValue.Create("UNSET"); } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs index 343077c38..2db70c052 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ValueConverters.cs @@ -7,41 +7,41 @@ using System; using System.Text; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ConvertContent { - public delegate JToken ValueConverter(JToken value, IField field); + public delegate IJsonValue ValueConverter(IJsonValue value, IField field); public static class ValueConverters { - public static ValueConverter DecodeJson() + public static ValueConverter DecodeJson(IJsonSerializer jsonSerializer) { return (value, field) => { - if (!value.IsNull() && field is IField) + if (field is IField && value is JsonScalar s) { - var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(value.ToString())); + var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(s.Value)); - return JToken.Parse(decoded); + return jsonSerializer.Deserialize(decoded); } return value; }; } - public static ValueConverter EncodeJson() + public static ValueConverter EncodeJson(IJsonSerializer jsonSerializer) { return (value, field) => { - if (!value.IsNull() && field is IField) + if (value.Type != JsonValueType.Null && field is IField) { - var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(value.ToString())); + var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonSerializer.Serialize(value))); - return encoded; + return JsonValue.Create(encoded); } return value; @@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.ConvertContent { return (value, field) => { - if (value.IsNull()) + if (value.Type == JsonValueType.Null) { return value; } diff --git a/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs b/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs index 2460a2c00..22f88bb85 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/ContentEnricher.cs @@ -5,12 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.EnrichContent { @@ -56,7 +55,7 @@ namespace Squidex.Domain.Apps.Core.EnrichContent var defaultValue = DefaultValueFactory.CreateDefaultValue(field, SystemClock.Instance.GetCurrentInstant()); - if (field.RawProperties.IsRequired || defaultValue.IsNull()) + if (field.RawProperties.IsRequired || defaultValue.Type == JsonValueType.Null) { return; } @@ -65,13 +64,13 @@ namespace Squidex.Domain.Apps.Core.EnrichContent if (!fieldData.TryGetValue(key, out var value) || ShouldApplyDefaultValue(field, value)) { - fieldData.AddValue(key, defaultValue); + fieldData.AddJsonValue(key, defaultValue); } } - private static bool ShouldApplyDefaultValue(IField field, JToken value) + private static bool ShouldApplyDefaultValue(IField field, IJsonValue value) { - return value.IsNull() || (field is IField && value is JValue jValue && Equals(jValue.Value, string.Empty)); + return value.Type == JsonValueType.Null || (field is IField && value is JsonScalar s && string.IsNullOrEmpty(s.Value)); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs b/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs index a4f8be960..0d05fc571 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/EnrichContent/DefaultValueFactory.cs @@ -6,14 +6,14 @@ // ========================================================================== using System.Globalization; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.EnrichContent { - public sealed class DefaultValueFactory : IFieldVisitor + public sealed class DefaultValueFactory : IFieldVisitor { private readonly Instant now; @@ -22,71 +22,71 @@ namespace Squidex.Domain.Apps.Core.EnrichContent this.now = now; } - public static JToken CreateDefaultValue(IField field, Instant now) + public static IJsonValue CreateDefaultValue(IField field, Instant now) { Guard.NotNull(field, nameof(field)); return field.Accept(new DefaultValueFactory(now)); } - public JToken Visit(IArrayField field) + public IJsonValue Visit(IArrayField field) { - return new JArray(); + return JsonValue.Array(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return new JArray(); + return JsonValue.Array(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return field.Properties.DefaultValue; + return JsonValue.Create(field.Properties.DefaultValue); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return JValue.CreateNull(); + return JsonValue.Null; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return JValue.CreateNull(); + return JsonValue.Object(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return field.Properties.DefaultValue; + return JsonValue.Create(field.Properties.DefaultValue); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return new JArray(); + return JsonValue.Array(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return field.Properties.DefaultValue; + return JsonValue.Create(field.Properties.DefaultValue); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { - return new JArray(); + return JsonValue.Array(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { if (field.Properties.CalculatedDefaultValue == DateTimeCalculatedDefaultValue.Now) { - return now.ToString(); + return JsonValue.Create(now.ToString()); } if (field.Properties.CalculatedDefaultValue == DateTimeCalculatedDefaultValue.Today) { - return now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); + return JsonValue.Create(now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); } - return field.Properties.DefaultValue?.ToString(); + return JsonValue.Create(field.Properties.DefaultValue?.ToString()); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs index 9d4fddf8d..125d529f2 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ContentReferencesExtensions.cs @@ -11,7 +11,7 @@ using System.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds continue; } - foreach (var partitionValue in fieldData.Where(x => !x.Value.IsNull())) + foreach (var partitionValue in fieldData.Where(x => x.Value.Type != JsonValueType.Null)) { var ids = field.ExtractReferences(partitionValue.Value); diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs index a817aba20..a7c459a13 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesCleaner.cs @@ -7,44 +7,44 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { - public sealed class ReferencesCleaner : IFieldVisitor + public sealed class ReferencesCleaner : IFieldVisitor { - private readonly JToken value; + private readonly IJsonValue value; private readonly ICollection oldReferences; - private ReferencesCleaner(JToken value, ICollection oldReferences) + private ReferencesCleaner(IJsonValue value, ICollection oldReferences) { this.value = value; this.oldReferences = oldReferences; } - public static JToken CleanReferences(IField field, JToken value, ICollection oldReferences) + public static IJsonValue CleanReferences(IField field, IJsonValue value, ICollection oldReferences) { return field.Accept(new ReferencesCleaner(value, oldReferences)); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return CleanIds(); } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { if (oldReferences.Contains(field.Properties.SchemaId)) { - return new JArray(); + return JsonValue.Array(); } return CleanIds(); } - private JToken CleanIds() + private IJsonValue CleanIds() { var ids = value.ToGuidSet(); @@ -55,45 +55,45 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds isRemoved |= ids.Remove(oldReference); } - return isRemoved ? ids.ToJToken() : value; + return isRemoved ? ids.ToJsonArray() : value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IField field) + public IJsonValue Visit(IField field) { return value; } - public JToken Visit(IArrayField field) + public IJsonValue Visit(IArrayField field) { return value; } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs index b7170f9aa..0a701501f 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtensions.cs @@ -7,22 +7,21 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { public static class ReferencesExtensions { - public static IEnumerable ExtractReferences(this IField field, JToken value) + public static IEnumerable ExtractReferences(this IField field, IJsonValue value) { return ReferencesExtractor.ExtractReferences(field, value); } - public static JToken CleanReferences(this IField field, JToken value, ICollection oldReferences) + public static IJsonValue CleanReferences(this IField field, IJsonValue value, ICollection oldReferences) { - if (value.IsNull()) + if (value.Type == JsonValueType.Null) { return value; } @@ -30,31 +29,27 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds return ReferencesCleaner.CleanReferences(field, value, oldReferences); } - public static JToken ToJToken(this HashSet ids) + public static JsonArray ToJsonArray(this HashSet ids) { - var result = new JArray(); + var result = JsonValue.Array(); foreach (var id in ids) { - result.Add(new JValue(id)); + result.Add(JsonValue.Create(id.ToString())); } return result; } - public static HashSet ToGuidSet(this JToken value) + public static HashSet ToGuidSet(this IJsonValue value) { - if (value is JArray ids) + if (value is JsonArray array) { var result = new HashSet(); - foreach (var id in ids) + foreach (var id in array) { - if (id.Type == JTokenType.Guid) - { - result.Add((Guid)id); - } - else if (id.Type == JTokenType.String && Guid.TryParse((string)id, out var guid)) + if (id.Type == JsonValueType.String && Guid.TryParse(id.ToString(), out var guid)) { result.Add(guid); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs index 1f661ef9c..d92b70b86 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs @@ -8,21 +8,21 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { public sealed class ReferencesExtractor : IFieldVisitor> { - private readonly JToken value; + private readonly IJsonValue value; - private ReferencesExtractor(JToken value) + private ReferencesExtractor(IJsonValue value) { this.value = value; } - public static IEnumerable ExtractReferences(IField field, JToken value) + public static IEnumerable ExtractReferences(IField field, IJsonValue value) { return field.Accept(new ReferencesExtractor(value)); } @@ -31,9 +31,9 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { var result = new List(); - if (value is JArray items) + if (value is JsonArray array) { - foreach (JObject item in items) + foreach (JsonObject item in array) { foreach (var nestedField in field.Fields) { diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs index e99459cc7..384a90ccb 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ValueReferencesConverter.cs @@ -8,7 +8,7 @@ using System; using System.Collections.Generic; using Squidex.Domain.Apps.Core.ConvertContent; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ExtractReferenceIds { @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds return (value, field) => { - if (value.IsNull()) + if (value.Type == JsonValueType.Null) { return value; } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs index f5d03da66..6c02810a4 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using Newtonsoft.Json; using NodaTime; using Squidex.Infrastructure; using Squidex.Shared.Users; @@ -25,10 +24,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents public long Version { get; set; } - [JsonIgnore] public abstract Guid AggregateId { get; } - [JsonIgnore] public IUser User { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs index deced9228..7ca0dc581 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/IRuleActionHandler.cs @@ -7,7 +7,6 @@ using System; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules; @@ -17,8 +16,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules { Type ActionType { get; } - Task<(string Description, JObject Data)> CreateJobAsync(EnrichedEvent @event, RuleAction action); + Type DataType { get; } - Task<(string Dump, Exception Exception)> ExecuteJobAsync(JObject data); + Task<(string Description, object Data)> CreateJobAsync(EnrichedEvent @event, RuleAction action); + + Task<(string Dump, Exception Exception)> ExecuteJobAsync(object data); } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs index 0143f1956..e9632578c 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs @@ -7,7 +7,6 @@ using System; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules; using Squidex.Infrastructure; @@ -25,6 +24,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules get { return typeof(TAction); } } + Type IRuleActionHandler.DataType + { + get { return typeof(TData); } + } + protected RuleActionHandler(RuleEventFormatter formatter) { Guard.NotNull(formatter, nameof(formatter)); @@ -33,21 +37,11 @@ namespace Squidex.Domain.Apps.Core.HandleRules } protected virtual string ToPayloadJson(T @event) - { - return formatter.ToPayload(@event).ToString(); - } - - protected virtual string ToEnvelopeJson(EnrichedEvent @event) - { - return formatter.ToEnvelope(@event).ToString(); - } - - protected virtual JObject ToPayload(T @event) { return formatter.ToPayload(@event); } - protected virtual JObject ToEnvelope(EnrichedEvent @event) + protected virtual string ToEnvelopeJson(EnrichedEvent @event) { return formatter.ToEnvelope(@event); } @@ -62,16 +56,16 @@ namespace Squidex.Domain.Apps.Core.HandleRules return formatter.Format(text, @event); } - async Task<(string Description, JObject Data)> IRuleActionHandler.CreateJobAsync(EnrichedEvent @event, RuleAction action) + async Task<(string Description, object Data)> IRuleActionHandler.CreateJobAsync(EnrichedEvent @event, RuleAction action) { var (description, data) = await CreateJobAsync(@event, (TAction)action); - return (description, JObject.FromObject(data)); + return (description, data); } - async Task<(string Dump, Exception Exception)> IRuleActionHandler.ExecuteJobAsync(JObject data) + async Task<(string Dump, Exception Exception)> IRuleActionHandler.ExecuteJobAsync(object data) { - var typedData = data.ToObject(); + var typedData = (TData)data; return await ExecuteJobAsync(typedData); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs index 895a722ae..8f3bb8919 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs @@ -10,11 +10,11 @@ using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Core.HandleRules @@ -27,15 +27,15 @@ namespace Squidex.Domain.Apps.Core.HandleRules private static readonly Regex ContentDataPlaceholderOld = new Regex(@"^CONTENT_DATA(\.([0-9A-Za-z\-_]*)){2,}", RegexOptions.Compiled); private static readonly Regex ContentDataPlaceholderNew = new Regex(@"^\{CONTENT_DATA(\.([0-9A-Za-z\-_]*)){2,}\}", RegexOptions.Compiled); private readonly List<(char[] Pattern, Func Replacer)> patterns = new List<(char[] Pattern, Func Replacer)>(); - private readonly JsonSerializer serializer; + private readonly IJsonSerializer jsonSerializer; private readonly IRuleUrlGenerator urlGenerator; - public RuleEventFormatter(JsonSerializer serializer, IRuleUrlGenerator urlGenerator) + public RuleEventFormatter(IJsonSerializer jsonSerializer, IRuleUrlGenerator urlGenerator) { - Guard.NotNull(serializer, nameof(serializer)); + Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); Guard.NotNull(urlGenerator, nameof(urlGenerator)); - this.serializer = serializer; + this.jsonSerializer = jsonSerializer; this.urlGenerator = urlGenerator; AddPattern("APP_ID", AppId); @@ -55,17 +55,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules patterns.Add((placeholder.ToCharArray(), generator)); } - public virtual JObject ToPayload(T @event) + public virtual string ToPayload(T @event) { - return JObject.FromObject(@event, serializer); + return jsonSerializer.Serialize(@event); } - public virtual JObject ToEnvelope(EnrichedEvent @event) + public virtual string ToEnvelope(EnrichedEvent @event) { - return new JObject( - new JProperty("type", @event.Name), - new JProperty("payload", ToPayload(@event)), - new JProperty("timestamp", @event.Timestamp.ToString())); + return jsonSerializer.Serialize(new { type = @event.Name, payload = @event, timestamp = @event.Timestamp }); } public string Format(string text, EnrichedEvent @event) @@ -264,14 +261,14 @@ namespace Squidex.Domain.Apps.Core.HandleRules for (var j = 2; j < path.Length; j++) { - if (value is JObject obj && obj.TryGetValue(path[j], out value)) + if (value is JsonObject obj && obj.TryGetValue(path[j], out value)) { continue; } - if (value is JArray arr && int.TryParse(path[j], out var idx) && idx >= 0 && idx < arr.Count) + if (value is JsonArray array && int.TryParse(path[j], out var idx) && idx >= 0 && idx < array.Count) { - value = arr[idx]; + value = array[idx]; } else { @@ -279,17 +276,12 @@ namespace Squidex.Domain.Apps.Core.HandleRules } } - if (value == null || value.Type == JTokenType.Null || value.Type == JTokenType.Undefined) + if (value == null || value.Type == JsonValueType.Null) { return Undefined; } - if (value is JValue jValue) - { - return jValue.Value.ToString(); - } - - return value.ToString(Formatting.Indented) ?? Undefined; + return value.ToString() ?? Undefined; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index 18329060a..e85ca990d 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -11,12 +11,12 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; namespace Squidex.Domain.Apps.Core.HandleRules { @@ -26,15 +26,18 @@ namespace Squidex.Domain.Apps.Core.HandleRules private readonly Dictionary ruleTriggerHandlers; private readonly TypeNameRegistry typeNameRegistry; private readonly IEventEnricher eventEnricher; + private readonly IJsonSerializer jsonSerializer; private readonly IClock clock; public RuleService( IEnumerable ruleTriggerHandlers, IEnumerable ruleActionHandlers, IEventEnricher eventEnricher, + IJsonSerializer jsonSerializer, IClock clock, TypeNameRegistry typeNameRegistry) { + Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); Guard.NotNull(ruleTriggerHandlers, nameof(ruleTriggerHandlers)); Guard.NotNull(ruleActionHandlers, nameof(ruleActionHandlers)); Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry)); @@ -48,6 +51,8 @@ namespace Squidex.Domain.Apps.Core.HandleRules this.eventEnricher = eventEnricher; + this.jsonSerializer = jsonSerializer; + this.clock = clock; } @@ -104,11 +109,13 @@ namespace Squidex.Domain.Apps.Core.HandleRules var actionName = typeNameRegistry.GetName(actionType); var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); + var json = jsonSerializer.Serialize(actionData); + var job = new RuleJob { JobId = Guid.NewGuid(), ActionName = actionName, - ActionData = actionData.Data, + ActionData = json, AggregateId = enrichedEvent.AggregateId, AppId = appEvent.AppId.Id, Created = now, @@ -120,14 +127,18 @@ namespace Squidex.Domain.Apps.Core.HandleRules return job; } - public virtual async Task<(string Dump, RuleResult Result, TimeSpan Elapsed)> InvokeAsync(string actionName, JObject job) + public virtual async Task<(string Dump, RuleResult Result, TimeSpan Elapsed)> InvokeAsync(string actionName, string job) { try { var actionType = typeNameRegistry.GetType(actionName); var actionWatch = Stopwatch.StartNew(); - var result = await ruleActionHandlers[actionType].ExecuteJobAsync(job); + var actionHandler = ruleActionHandlers[actionType]; + + var deserialized = jsonSerializer.Deserialize(job, actionHandler.DataType); + + var result = await actionHandler.ExecuteJobAsync(deserialized); actionWatch.Stop(); diff --git a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldProperty.cs b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldProperty.cs index 11ec3918e..ee279b40a 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldProperty.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/ContentFieldProperty.cs @@ -7,14 +7,14 @@ using Jint.Native; using Jint.Runtime.Descriptors; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper { public sealed class ContentFieldProperty : PropertyDescriptor { private readonly ContentFieldObject contentField; - private JToken contentValue; + private IJsonValue contentValue; private JsValue value; private bool isChanged; @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper } } - public JToken ContentValue + public IJsonValue ContentValue { get { return contentValue ?? (contentValue = JsonMapper.Map(value)); } } @@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper get { return isChanged; } } - public ContentFieldProperty(ContentFieldObject contentField, JToken contentValue = null) + public ContentFieldProperty(ContentFieldObject contentField, IJsonValue contentValue = null) : base(null, true, true, true) { this.contentField = contentField; diff --git a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/JsonMapper.cs b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/JsonMapper.cs index 6ea3cd584..ec9d52054 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/JsonMapper.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Scripting/ContentWrapper/JsonMapper.cs @@ -9,61 +9,52 @@ using System; using Jint; using Jint.Native; using Jint.Native.Object; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper { public static class JsonMapper { - public static JsValue Map(JToken value, Engine engine) + public static JsValue Map(IJsonValue value, Engine engine) { if (value == null) { return JsValue.Null; } - switch (value.Type) + switch (value) { - case JTokenType.Date: - case JTokenType.Guid: - case JTokenType.String: - case JTokenType.Uri: - case JTokenType.TimeSpan: - return new JsValue((string)value); - case JTokenType.Null: + case JsonNull n: return JsValue.Null; - case JTokenType.Undefined: - return JsValue.Undefined; - case JTokenType.Integer: - return new JsValue((long)value); - case JTokenType.Float: - return new JsValue((double)value); - case JTokenType.Boolean: - return new JsValue((bool)value); - case JTokenType.Object: - return FromObject(value, engine); - case JTokenType.Array: - { - var arr = (JArray)value; - - var target = new JsValue[arr.Count]; - - for (var i = 0; i < arr.Count; i++) - { - target[i] = Map(arr[i], engine); - } - - return engine.Array.Construct(target); - } + case JsonScalar s: + return new JsValue(s.Value); + case JsonScalar b: + return new JsValue(b.Value); + case JsonScalar b: + return new JsValue(b.Value); + case JsonObject obj: + return FromObject(obj, engine); + case JsonArray arr: + return FromArray(arr, engine); } throw new ArgumentException("Invalid json type.", nameof(value)); } - private static JsValue FromObject(JToken value, Engine engine) + private static JsValue FromArray(JsonArray arr, Engine engine) { - var obj = (JObject)value; + var target = new JsValue[arr.Count]; + for (var i = 0; i < arr.Count; i++) + { + target[i] = Map(arr[i], engine); + } + + return engine.Array.Construct(target); + } + + private static JsValue FromObject(JsonObject obj, Engine engine) + { var target = new ObjectInstance(engine); foreach (var property in obj) @@ -74,69 +65,64 @@ namespace Squidex.Domain.Apps.Core.Scripting.ContentWrapper return target; } - public static JToken Map(JsValue value) + public static IJsonValue Map(JsValue value) { - if (value == null || value.IsNull()) - { - return JValue.CreateNull(); - } - - if (value.IsUndefined()) + if (value == null || value.IsNull() || value.IsUndefined()) { - return JValue.CreateUndefined(); + return JsonValue.Null; } if (value.IsString()) { - return new JValue(value.AsString()); + return JsonValue.Create(value.AsString()); } if (value.IsBoolean()) { - return new JValue(value.AsBoolean()); + return JsonValue.Create(value.AsBoolean()); } if (value.IsNumber()) { - return new JValue(value.AsNumber()); + return JsonValue.Create(value.AsNumber()); } if (value.IsDate()) { - return new JValue(value.AsDate().ToDateTime()); + return JsonValue.Create(value.AsDate().ToString()); } if (value.IsRegExp()) { - return JValue.CreateString(value.AsRegExp().Value?.ToString()); + return JsonValue.Create(value.AsRegExp().Value?.ToString()); } if (value.IsArray()) { var arr = value.AsArray(); - var target = new JArray(); + var result = JsonValue.Array(); for (var i = 0; i < arr.GetLength(); i++) { - target.Add(Map(arr.Get(i.ToString()))); + result.Add(Map(arr.Get(i.ToString()))); } - return target; + return result; } if (value.IsObject()) { var obj = value.AsObject(); - var target = new JObject(); + var result = JsonValue.Object(); foreach (var kvp in obj.GetOwnProperties()) { - target[kvp.Key] = Map(kvp.Value.Value); + result[kvp.Key] = Map(kvp.Value.Value); } - return target; + return result; } throw new ArgumentException("Invalid json type.", nameof(value)); diff --git a/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs index ed9cd3f05..7ecac0baf 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/Tags/TagNormalizer.cs @@ -8,10 +8,10 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Tags { @@ -24,10 +24,10 @@ namespace Squidex.Domain.Apps.Core.Tags Guard.NotNull(newData, nameof(newData)); var newValues = new HashSet(); - var newArrays = new List(); + var newArrays = new List(); var oldValues = new HashSet(); - var oldArrays = new List(); + var oldArrays = new List(); GetValues(schema, newValues, newArrays, newData); @@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Core.Tags { if (normalized.TryGetValue(array[i].ToString(), out var result)) { - array[i] = result; + array[i] = JsonValue.Create(result); } } } @@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Core.Tags Guard.NotNull(schema, nameof(schema)); var tagsValues = new HashSet(); - var tagsArrays = new List(); + var tagsArrays = new List(); GetValues(schema, tagsValues, tagsArrays, datas); @@ -73,14 +73,14 @@ namespace Squidex.Domain.Apps.Core.Tags { if (denormalized.TryGetValue(array[i].ToString(), out var result)) { - array[i] = result; + array[i] = JsonValue.Create(result); } } } } } - private static void GetValues(Schema schema, HashSet values, List arrays, params NamedContentData[] datas) + private static void GetValues(Schema schema, HashSet values, List arrays, params NamedContentData[] datas) { foreach (var field in schema.Fields) { @@ -109,14 +109,12 @@ namespace Squidex.Domain.Apps.Core.Tags { foreach (var partition in fieldData) { - if (partition.Value is JArray jArray) + if (partition.Value is JsonArray array) { - foreach (var value in jArray) + foreach (var value in array) { - if (value.Type == JTokenType.Object) + if (value is JsonObject nestedObject) { - var nestedObject = (JObject)value; - if (nestedObject.TryGetValue(nestedField.Name, out var nestedValue)) { ExtractTags(nestedValue, values, arrays); @@ -133,19 +131,19 @@ namespace Squidex.Domain.Apps.Core.Tags } } - private static void ExtractTags(JToken token, ISet values, ICollection arrays) + private static void ExtractTags(IJsonValue value, ISet values, ICollection arrays) { - if (token is JArray jArray) + if (value is JsonArray array) { - foreach (var value in jArray) + foreach (var item in array) { - if (value.Type == JTokenType.String) + if (item.Type == JsonValueType.String) { - values.Add(value.ToString()); + values.Add(item.ToString()); } } - arrays.Add(jArray); + arrays.Add(array); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs index 38517df83..ab089b5d7 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs @@ -9,11 +9,11 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; #pragma warning disable SA1028, IDE0004 // Code must not contain trailing whitespace @@ -22,7 +22,6 @@ namespace Squidex.Domain.Apps.Core.ValidateContent public sealed class ContentValidator { private static readonly ContentFieldData DefaultFieldData = new ContentFieldData(); - private static readonly JToken DefaultValue = JValue.CreateNull(); private readonly Schema schema; private readonly PartitionResolver partitionResolver; private readonly ValidationContext context; @@ -96,7 +95,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent var type = isLanguage ? "language" : "invariant value"; - return new ObjectValidator(fieldsValidators, isPartial, type, DefaultValue); + return new ObjectValidator(fieldsValidators, isPartial, type, JsonValue.Null); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs index a43f8a095..397ed286f 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/JsonValueConverter.cs @@ -7,45 +7,80 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json.Linq; using NodaTime.Text; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ValidateContent { public sealed class JsonValueConverter : IFieldVisitor { - private readonly JToken value; + private readonly IJsonValue value; - private JsonValueConverter(JToken value) + private JsonValueConverter(IJsonValue value) { this.value = value; } - public static object ConvertValue(IField field, JToken json) + public static object ConvertValue(IField field, IJsonValue json) { return field.Accept(new JsonValueConverter(json)); } public object Visit(IArrayField field) { - return value.ToObject>(); + return ConvertToObjectList(); } public object Visit(IField field) { - return value.ToObject>(); + return ConvertToGuidList(); + } + + public object Visit(IField field) + { + return ConvertToGuidList(); + } + + public object Visit(IField field) + { + return ConvertToStringList(); } public object Visit(IField field) { - return (bool?)value; + if (value is JsonScalar b) + { + return b.Value; + } + + throw new InvalidCastException("Invalid json type, expected boolean."); + } + + public object Visit(IField field) + { + if (value is JsonScalar b) + { + return b.Value; + } + + throw new InvalidCastException("Invalid json type, expected number."); + } + + public object Visit(IField field) + { + if (value is JsonScalar b) + { + return b.Value; + } + + throw new InvalidCastException("Invalid json type, expected string."); } public object Visit(IField field) { - if (value.Type == JTokenType.String) + if (value.Type == JsonValueType.String) { var parseResult = InstantPattern.General.Parse(value.ToString()); @@ -62,31 +97,49 @@ namespace Squidex.Domain.Apps.Core.ValidateContent public object Visit(IField field) { - var geolocation = (JObject)value; - - foreach (var property in geolocation.Properties()) + if (value is JsonObject geolocation) { - if (!string.Equals(property.Name, "latitude", StringComparison.OrdinalIgnoreCase) && - !string.Equals(property.Name, "longitude", StringComparison.OrdinalIgnoreCase)) + foreach (var propertyName in geolocation.Keys) { - throw new InvalidCastException("Geolocation can only have latitude and longitude property."); + if (!string.Equals(propertyName, "latitude", StringComparison.OrdinalIgnoreCase) && + !string.Equals(propertyName, "longitude", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidCastException("Geolocation can only have latitude and longitude property."); + } } - } - var lat = (double)geolocation["latitude"]; - var lon = (double)geolocation["longitude"]; + if (geolocation.TryGetValue("latitude", out var latValue) && latValue is JsonScalar latNumber) + { + var lat = latNumber.Value; - if (!lat.IsBetween(-90, 90)) - { - throw new InvalidCastException("Latitude must be between -90 and 90."); - } + if (!lat.IsBetween(-90, 90)) + { + throw new InvalidCastException("Latitude must be between -90 and 90."); + } + } + else + { + throw new InvalidCastException("Invalid json type, expected latitude/longitude object."); + } - if (!lon.IsBetween(-180, 180)) - { - throw new InvalidCastException("Longitude must be between -180 and 180."); + if (geolocation.TryGetValue("longitude", out var lonValue) && lonValue is JsonScalar lonNumber) + { + var lon = lonNumber.Value; + + if (!lon.IsBetween(-180, 180)) + { + throw new InvalidCastException("Longitude must be between -180 and 180."); + } + } + else + { + throw new InvalidCastException("Invalid json type, expected latitude/longitude object."); + } + + return value; } - return value; + throw new InvalidCastException("Invalid json type, expected latitude/longitude object."); } public object Visit(IField field) @@ -94,24 +147,76 @@ namespace Squidex.Domain.Apps.Core.ValidateContent return value; } - public object Visit(IField field) + private object ConvertToGuidList() { - return (double?)value; - } + if (value is JsonArray array) + { + var result = new List(); - public object Visit(IField field) - { - return value.ToObject>(); + foreach (var item in array) + { + if (item is JsonScalar s && Guid.TryParse(s.Value, out var guid)) + { + result.Add(guid); + } + else + { + throw new InvalidCastException("Invalid json type, expected array of guid strings."); + } + } + + return result; + } + + throw new InvalidCastException("Invalid json type, expected array of guid strings."); } - public object Visit(IField field) + private object ConvertToStringList() { - return value.ToString(); + if (value is JsonArray array) + { + var result = new List(); + + foreach (var item in array) + { + if (item is JsonScalar s) + { + result.Add(s.Value); + } + else + { + throw new InvalidCastException("Invalid json type, expected array of strings."); + } + } + + return result; + } + + throw new InvalidCastException("Invalid json type, expected array of strings."); } - public object Visit(IField field) + private object ConvertToObjectList() { - return value.ToObject>(); + if (value is JsonArray array) + { + var result = new List(); + + foreach (var item in array) + { + if (item is JsonObject obj) + { + result.Add(obj); + } + else + { + throw new InvalidCastException("Invalid json type, expected array of objects."); + } + } + + return result; + } + + throw new InvalidCastException("Invalid json type, expected array of objects."); } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs index 97471857e..40bf1a89d 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/FieldValidator.cs @@ -7,9 +7,8 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { @@ -30,9 +29,9 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators { object typedValue = null; - if (value is JToken jToken) + if (value is IJsonValue jsonValue) { - typedValue = jToken.IsNull() ? null : JsonValueConverter.ConvertValue(field, jToken); + typedValue = jsonValue.Type == JsonValueType.Null ? null : JsonValueConverter.ConvertValue(field, jsonValue); } var tasks = new List(); diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs index 6dd8a9c28..429962996 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/Validators/ObjectValidator.cs @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent.Validators public async Task ValidateAsync(object value, ValidationContext context, AddError addError) { - if (value is IDictionary values) + if (value is IReadOnlyDictionary values) { foreach (var fieldData in values) { diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs index fc104e7e5..3a40a2b61 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidatorsFactory.cs @@ -8,11 +8,11 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent.Validators; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.ValidateContent { @@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.ValidateContent nestedSchema[nestedField.Name] = (false, new FieldValidator(nestedField.Accept(this).ToArray(), nestedField)); } - yield return new CollectionItemValidator(new ObjectValidator(nestedSchema, false, "field", JValue.CreateNull())); + yield return new CollectionItemValidator(new ObjectValidator(nestedSchema, false, "field", JsonValue.Null)); } public IEnumerable Visit(IField field) diff --git a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs index c28abf358..6fdfaa288 100644 --- a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs +++ b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs @@ -32,8 +32,8 @@ namespace Squidex.Infrastructure.EventSourcing public static EventStoreData Write(EventData eventData) { - var body = Encoding.UTF8.GetBytes(eventData.Payload.ToString()); - var meta = Encoding.UTF8.GetBytes(eventData.Metadata.ToString()); + var body = Encoding.UTF8.GetBytes(eventData.Payload); + var meta = Encoding.UTF8.GetBytes(eventData.Metadata); return new EventStoreData(Guid.NewGuid(), eventData.Type, true, body, meta); } diff --git a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs index 395fa594f..8748ba8b1 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs @@ -5,8 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using Newtonsoft.Json.Linq; using Squidex.Infrastructure.MongoDb; namespace Squidex.Infrastructure.EventSourcing @@ -23,16 +23,16 @@ namespace Squidex.Infrastructure.EventSourcing [BsonElement] [BsonRequired] - public JToken Metadata { get; set; } + public BsonDocument Metadata { get; set; } public static MongoEvent FromEventData(EventData data) { - return new MongoEvent { Type = data.Type, Metadata = data.Metadata, Payload = data.Payload.ToString() }; + return new MongoEvent { Type = data.Type, Metadata = BsonDocument.Parse(data.Payload), Payload = data.Payload }; } public EventData ToEventData() { - return new EventData { Type = Type, Metadata = Metadata, Payload = JObject.Parse(Payload) }; + return new EventData { Type = Type, Metadata = Metadata.ToJson().ToString(), Payload = Payload }; } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs b/src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs index 238698bda..b378743c1 100644 --- a/src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs +++ b/src/Squidex.Infrastructure.RabbitMq/CQRS/Events/RabbitMqEventConsumer.cs @@ -9,16 +9,16 @@ using System; using System.Text; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; using RabbitMQ.Client; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Tasks; namespace Squidex.Infrastructure.CQRS.Events { public sealed class RabbitMqEventConsumer : DisposableObjectBase, IInitializable, IEventConsumer { - private readonly JsonSerializerSettings serializerSettings; + private readonly IJsonSerializer jsonSerializer; private readonly string eventPublisherName; private readonly string exchange; private readonly string eventsFilter; @@ -36,12 +36,12 @@ namespace Squidex.Infrastructure.CQRS.Events get { return eventsFilter; } } - public RabbitMqEventConsumer(JsonSerializerSettings serializerSettings, string eventPublisherName, string uri, string exchange, string eventsFilter) + public RabbitMqEventConsumer(IJsonSerializer jsonSerializer, string eventPublisherName, string uri, string exchange, string eventsFilter) { Guard.NotNullOrEmpty(uri, nameof(uri)); Guard.NotNullOrEmpty(eventPublisherName, nameof(eventPublisherName)); Guard.NotNullOrEmpty(exchange, nameof(exchange)); - Guard.NotNull(serializerSettings, nameof(serializerSettings)); + Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); connectionFactory = new ConnectionFactory { Uri = new Uri(uri, UriKind.Absolute) }; connection = new Lazy(connectionFactory.CreateConnection); @@ -49,8 +49,8 @@ namespace Squidex.Infrastructure.CQRS.Events this.exchange = exchange; this.eventsFilter = eventsFilter; + this.jsonSerializer = jsonSerializer; this.eventPublisherName = eventPublisherName; - this.serializerSettings = serializerSettings; } protected override void DisposeObject(bool disposing) @@ -88,7 +88,7 @@ namespace Squidex.Infrastructure.CQRS.Events public Task On(Envelope @event) { - var jsonString = JsonConvert.SerializeObject(@event, serializerSettings); + var jsonString = jsonSerializer.Serialize(@event); var jsonBytes = Encoding.UTF8.GetBytes(jsonString); channel.Value.BasicPublish(exchange, string.Empty, null, jsonBytes); diff --git a/src/Squidex.Infrastructure/Assets/AssetFile.cs b/src/Squidex.Infrastructure/Assets/AssetFile.cs index bbaa0917d..4f5ef010f 100644 --- a/src/Squidex.Infrastructure/Assets/AssetFile.cs +++ b/src/Squidex.Infrastructure/Assets/AssetFile.cs @@ -7,7 +7,6 @@ using System; using System.IO; -using Newtonsoft.Json; namespace Squidex.Infrastructure.Assets { @@ -21,7 +20,6 @@ namespace Squidex.Infrastructure.Assets public long FileSize { get; } - [JsonConstructor] public AssetFile(string fileName, string mimeType, long fileSize, Func openAction) { Guard.NotNullOrEmpty(fileName, nameof(fileName)); diff --git a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs index 98b7919b1..22735b68d 100644 --- a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs @@ -6,38 +6,38 @@ // ========================================================================== using System; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json; namespace Squidex.Infrastructure.EventSourcing { public class DefaultEventDataFormatter : IEventDataFormatter { - private readonly JsonSerializer serializer; + private readonly IJsonSerializer serializer; private readonly TypeNameRegistry typeNameRegistry; - public DefaultEventDataFormatter(TypeNameRegistry typeNameRegistry, JsonSerializer serializer = null) + public DefaultEventDataFormatter(TypeNameRegistry typeNameRegistry, IJsonSerializer serializer) { Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry)); + Guard.NotNull(serializer, nameof(serializer)); this.typeNameRegistry = typeNameRegistry; - this.serializer = serializer ?? JsonSerializer.CreateDefault(); + this.serializer = serializer; } public Envelope Parse(EventData eventData, bool migrate = true) { var eventType = typeNameRegistry.GetType(eventData.Type); - var headers = eventData.Metadata.ToObject(serializer); - var content = eventData.Payload.ToObject(eventType, serializer) as IEvent; + var eventHeaders = serializer.Deserialize(eventData.Metadata); + var eventContent = serializer.Deserialize(eventData.Payload, eventType); - if (migrate && content is IMigratedEvent migratedEvent) + if (migrate && eventContent is IMigratedEvent migratedEvent) { - content = migratedEvent.Migrate(); + eventContent = migratedEvent.Migrate(); } - var envelope = new Envelope(content, headers); + var envelope = new Envelope(eventContent, eventHeaders); return envelope; } @@ -55,10 +55,10 @@ namespace Squidex.Infrastructure.EventSourcing envelope.SetCommitId(commitId); - var headers = JToken.FromObject(envelope.Headers, serializer); - var content = JToken.FromObject(envelope.Payload, serializer); + var eventHeaders = serializer.Serialize(envelope.Headers); + var eventContent = serializer.Serialize(envelope.Payload); - return new EventData { Type = eventType, Payload = content, Metadata = headers }; + return new EventData { Type = eventType, Payload = eventContent, Metadata = eventHeaders }; } } } diff --git a/src/Squidex.Infrastructure/EventSourcing/EventData.cs b/src/Squidex.Infrastructure/EventSourcing/EventData.cs index 739ea8068..a3d82188f 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EventData.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EventData.cs @@ -5,15 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; - namespace Squidex.Infrastructure.EventSourcing { public class EventData { - public JToken Payload { get; set; } + public string Payload { get; set; } - public JToken Metadata { get; set; } + public string Metadata { get; set; } public string Type { get; set; } } diff --git a/src/Squidex.Infrastructure/Json/IJsonSerializer.cs b/src/Squidex.Infrastructure/Json/IJsonSerializer.cs new file mode 100644 index 000000000..e42fb9ea3 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/IJsonSerializer.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.IO; + +namespace Squidex.Infrastructure.Json +{ + public interface IJsonSerializer + { + string Serialize(T value); + + void Serialize(T value, Stream stream); + + T Deserialize(string value, Type actualType = null); + + T Deserialize(Stream stream, Type actualType = null); + } +} diff --git a/src/Squidex.Infrastructure/Json/JsonExtension.cs b/src/Squidex.Infrastructure/Json/JsonExtension.cs deleted file mode 100644 index 23d8cea4c..000000000 --- a/src/Squidex.Infrastructure/Json/JsonExtension.cs +++ /dev/null @@ -1,34 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Newtonsoft.Json.Linq; - -namespace Squidex.Infrastructure.Json -{ - public static class JsonExtension - { - public static bool IsNull(this JToken token) - { - if (token == null) - { - return true; - } - - if (token.Type == JTokenType.Null) - { - return true; - } - - if (token is JValue value) - { - return value.Value == null; - } - - return false; - } - } -} diff --git a/src/Squidex.Infrastructure/Json/ClaimsPrincipalConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/ClaimsPrincipalConverter.cs similarity index 97% rename from src/Squidex.Infrastructure/Json/ClaimsPrincipalConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/ClaimsPrincipalConverter.cs index c183e6424..bde9e0013 100644 --- a/src/Squidex.Infrastructure/Json/ClaimsPrincipalConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/ClaimsPrincipalConverter.cs @@ -10,7 +10,7 @@ using System.Linq; using System.Security.Claims; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class ClaimsPrincipalConverter : JsonClassConverter { diff --git a/src/Squidex.Infrastructure/Json/ConverterContractResolver.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/ConverterContractResolver.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs index 55609ed0d..e626d5e1d 100644 --- a/src/Squidex.Infrastructure/Json/ConverterContractResolver.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs @@ -9,7 +9,7 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class ConverterContractResolver : CamelCasePropertyNamesContractResolver { diff --git a/src/Squidex.Infrastructure/Json/InstantConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs similarity index 97% rename from src/Squidex.Infrastructure/Json/InstantConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs index deca1ee95..463d24411 100644 --- a/src/Squidex.Infrastructure/Json/InstantConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs @@ -10,7 +10,7 @@ using Newtonsoft.Json; using NodaTime; using NodaTime.Text; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class InstantConverter : JsonConverter { diff --git a/src/Squidex.Infrastructure/Json/JsonClassConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/JsonClassConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs index 954e472dd..078bd451e 100644 --- a/src/Squidex.Infrastructure/Json/JsonClassConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public abstract class JsonClassConverter : JsonConverter where T : class { diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs new file mode 100644 index 000000000..949a3a90c --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -0,0 +1,161 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Globalization; +using System.Linq; +using Newtonsoft.Json; +using Squidex.Infrastructure.Json.Objects; + +namespace Squidex.Infrastructure.Json.Newtonsoft +{ + public sealed class JsonValueConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(IJsonValue).IsAssignableFrom(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return ReadJson(reader); + } + + private IJsonValue ReadJson(JsonReader reader) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + reader.Read(); + break; + case JsonToken.StartObject: + { + var result = JsonValue.Object(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + var propertyName = reader.Value.ToString(); + + if (!reader.Read()) + { + throw new JsonSerializationException("Unexpected end when reading Object."); + } + + var value = ReadJson(reader); + + result[propertyName] = value; + break; + case JsonToken.EndObject: + return result; + } + } + + throw new JsonSerializationException("Unexpected end when reading Object."); + } + + case JsonToken.StartArray: + { + var result = JsonValue.Array(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + break; + default: + var value = ReadJson(reader); + + result.Add(value); + break; + case JsonToken.EndArray: + return result; + } + } + + throw new JsonSerializationException("Unexpected end when reading Object."); + } + + case JsonToken.Integer: + return JsonValue.Create((long)reader.Value); + case JsonToken.Float: + return JsonValue.Create((double)reader.Value); + case JsonToken.Boolean: + return JsonValue.Create((bool)reader.Value); + case JsonToken.Date: + return JsonValue.Create(((DateTime)reader.Value).ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture)); + case JsonToken.String: + return JsonValue.Create(reader.Value.ToString()); + case JsonToken.Null: + case JsonToken.Undefined: + return JsonValue.Null; + } + + throw new NotSupportedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + WriteJson(writer, (IJsonValue)value); + } + + private void WriteJson(JsonWriter writer, IJsonValue value) + { + switch (value) + { + case JsonNull n: + writer.WriteNull(); + break; + case JsonScalar s: + writer.WriteValue(s.Value); + break; + case JsonScalar s: + writer.WriteValue(s.Value); + break; + case JsonScalar s: + writer.WriteValue(s.Value); + break; + case JsonArray array: + { + writer.WriteStartArray(); + + foreach (var item in array) + { + WriteJson(writer, item); + } + + writer.WriteEndArray(); + break; + } + + case JsonObject obj: + { + writer.WriteStartObject(); + + foreach (var kvp in obj) + { + writer.WritePropertyName(kvp.Key); + + WriteJson(writer, kvp.Value); + } + + writer.WriteEndObject(); + break; + } + } + } + } +} diff --git a/src/Squidex.Infrastructure/Json/LanguageConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/LanguageConverter.cs similarity index 95% rename from src/Squidex.Infrastructure/Json/LanguageConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/LanguageConverter.cs index c2c11ac78..11d45a037 100644 --- a/src/Squidex.Infrastructure/Json/LanguageConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/LanguageConverter.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class LanguageConverter : JsonClassConverter { diff --git a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs index 1695a96f9..f40e50481 100644 --- a/src/Squidex.Infrastructure/Json/NamedGuidIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class NamedGuidIdConverter : JsonClassConverter> { diff --git a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs index cc85acd9b..b90415b71 100644 --- a/src/Squidex.Infrastructure/Json/NamedLongIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class NamedLongIdConverter : JsonClassConverter> { diff --git a/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs index 3076ef02c..78f39fb1f 100644 --- a/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs @@ -9,7 +9,7 @@ using System; using System.Linq; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class NamedStringIdConverter : JsonClassConverter> { diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs new file mode 100644 index 000000000..a9a045ed3 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs @@ -0,0 +1,60 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.IO; +using Newtonsoft.Json; + +namespace Squidex.Infrastructure.Json.Newtonsoft +{ + public sealed class NewtonsoftJsonSerializer : IJsonSerializer + { + private readonly JsonSerializerSettings settings; + private readonly JsonSerializer serializer; + + public NewtonsoftJsonSerializer(JsonSerializerSettings settings) + { + Guard.NotNull(settings, nameof(settings)); + + this.settings = settings; + + serializer = JsonSerializer.Create(settings); + } + + public T Deserialize(string value, Type actualType = null) + { + actualType = actualType ?? typeof(T); + + return (T)JsonConvert.DeserializeObject(value, actualType, settings); + } + + public T Deserialize(Stream stream, Type actualType = null) + { + using (var streamReader = new StreamReader(stream)) + { + actualType = actualType ?? typeof(T); + + return (T)serializer.Deserialize(streamReader, actualType); + } + } + + public string Serialize(T value) + { + return JsonConvert.SerializeObject(value, settings); + } + + public void Serialize(T value, Stream stream) + { + using (var writer = new StreamWriter(stream)) + { + serializer.Serialize(writer, value); + + writer.Flush(); + } + } + } +} diff --git a/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs similarity index 97% rename from src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs index f42642fb0..27bda90ae 100644 --- a/src/Squidex.Infrastructure/Json/PropertiesBagConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs @@ -10,7 +10,7 @@ using Newtonsoft.Json; using NodaTime; using NodaTime.Extensions; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class PropertiesBagConverter : JsonClassConverter where T : PropertiesBag, new() { diff --git a/src/Squidex.Infrastructure/Json/RefTokenConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs similarity index 95% rename from src/Squidex.Infrastructure/Json/RefTokenConverter.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs index 1e2576faa..9f12baacc 100644 --- a/src/Squidex.Infrastructure/Json/RefTokenConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class RefTokenConverter : JsonClassConverter { diff --git a/src/Squidex.Infrastructure/Json/TypeNameSerializationBinder.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/TypeNameSerializationBinder.cs similarity index 96% rename from src/Squidex.Infrastructure/Json/TypeNameSerializationBinder.cs rename to src/Squidex.Infrastructure/Json/Newtonsoft/TypeNameSerializationBinder.cs index 303bd51a0..8e5d41dc2 100644 --- a/src/Squidex.Infrastructure/Json/TypeNameSerializationBinder.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/TypeNameSerializationBinder.cs @@ -8,7 +8,7 @@ using System; using Newtonsoft.Json.Serialization; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public class TypeNameSerializationBinder : DefaultSerializationBinder { diff --git a/src/Squidex.Infrastructure/Json/Objects/IJsonValue.cs b/src/Squidex.Infrastructure/Json/Objects/IJsonValue.cs new file mode 100644 index 000000000..743cf4a2a --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/IJsonValue.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; + +namespace Squidex.Infrastructure.Json.Objects +{ + public interface IJsonValue : IEquatable + { + JsonValueType Type { get; } + + string ToJsonString(); + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs b/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs new file mode 100644 index 000000000..00880e34a --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs @@ -0,0 +1,100 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonArray : Collection, IJsonValue, IEquatable + { + public JsonValueType Type + { + get { return JsonValueType.Array; } + } + + public JsonArray() + { + } + + public JsonArray(IList values) + : base(values) + { + } + + public JsonArray(params object[] values) + : base(values?.Select(JsonValue.Create).ToList()) + { + } + + protected override void InsertItem(int index, IJsonValue item) + { + Guard.NotNull(item, nameof(item)); + + base.InsertItem(index, item); + } + + protected override void SetItem(int index, IJsonValue item) + { + Guard.NotNull(item, nameof(item)); + + base.SetItem(index, item); + } + + public override bool Equals(object obj) + { + return Equals(obj as JsonArray); + } + + public bool Equals(IJsonValue other) + { + return Equals(other as JsonArray); + } + + public bool Equals(JsonArray array) + { + if (array == null || array.Count != Count) + { + return false; + } + + for (var i = 0; i < Count; i++) + { + if (!this[i].Equals(array[i])) + { + return false; + } + } + + return true; + } + + public override int GetHashCode() + { + var hashCode = 0; + + for (var i = 0; i < Count; i++) + { + hashCode = (hashCode * 23) + this[i].GetHashCode(); + } + + return hashCode; + } + + public string ToJsonString() + { + return ToString(); + } + + public override string ToString() + { + return $"[{string.Join(", ", this)}]"; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonNull.cs b/src/Squidex.Infrastructure/Json/Objects/JsonNull.cs new file mode 100644 index 000000000..884462b3c --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonNull.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonNull : IJsonValue, IEquatable + { + public static readonly JsonNull Null = new JsonNull(); + + public JsonValueType Type + { + get { return JsonValueType.Null; } + } + + private JsonNull() + { + } + + public override bool Equals(object obj) + { + return Equals(obj as JsonNull); + } + + public bool Equals(IJsonValue other) + { + return Equals(other as JsonNull); + } + + public bool Equals(JsonNull other) + { + return other != null; + } + + public override int GetHashCode() + { + return 0; + } + + public string ToJsonString() + { + return ToString(); + } + + public override string ToString() + { + return "null"; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs new file mode 100644 index 000000000..fe70c43ff --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs @@ -0,0 +1,129 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonObject : IReadOnlyDictionary, IJsonValue, IEquatable + { + private readonly Dictionary inner = new Dictionary(); + + public IJsonValue this[string key] + { + get + { + return inner[key]; + } + set + { + Guard.NotNullOrEmpty(key, nameof(key)); + Guard.NotNull(value, nameof(value)); + + inner[key] = value; + } + } + + public IEnumerable Keys + { + get { return inner.Keys; } + } + + public IEnumerable Values + { + get { return inner.Values; } + } + + public int Count + { + get { return inner.Count; } + } + + public JsonValueType Type + { + get { return JsonValueType.Array; } + } + + public JsonObject Add(string key, object value) + { + return Add(key, JsonValue.Create(value)); + } + + public JsonObject Add(string key, IJsonValue value) + { + Guard.NotNullOrEmpty(key, nameof(key)); + Guard.NotNull(value, nameof(value)); + + inner.Add(key, value); + + return this; + } + + public void Clear() + { + inner.Clear(); + } + + public bool Remove(string key) + { + return inner.Remove(key); + } + + public bool ContainsKey(string key) + { + return inner.ContainsKey(key); + } + + public bool TryGetValue(string key, out IJsonValue value) + { + return inner.TryGetValue(key, out value); + } + + public IEnumerator> GetEnumerator() + { + return inner.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return inner.GetEnumerator(); + } + + public override bool Equals(object obj) + { + return Equals(obj as JsonObject); + } + + public bool Equals(IJsonValue other) + { + return Equals(other as JsonObject); + } + + public bool Equals(JsonObject other) + { + return other != null && inner.EqualsDictionary(other.inner); + } + + public override int GetHashCode() + { + return inner.DictionaryHashCode(); + } + + public string ToJsonString() + { + return ToString(); + } + + public override string ToString() + { + return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\"={x.Value}\""))}}}"; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs new file mode 100644 index 000000000..0689f7ecd --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs @@ -0,0 +1,60 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonScalar : IJsonValue, IEquatable> + { + private readonly T value; + + public JsonValueType Type { get; } + + public T Value + { + get { return value; } + } + + internal JsonScalar(JsonValueType type, T value) + { + Type = type; + + this.value = value; + } + + public override bool Equals(object obj) + { + return Equals(obj as JsonScalar); + } + + public bool Equals(IJsonValue other) + { + return Equals(other as JsonScalar); + } + + public bool Equals(JsonScalar other) + { + return other != null && other.Type == Type && Equals(other.value, value); + } + + public override int GetHashCode() + { + return value.GetHashCode(); + } + + public string ToJsonString() + { + return Type == JsonValueType.String ? $"\"{value}\"" : ToString(); + } + + public override string ToString() + { + return value.ToString(); + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs new file mode 100644 index 000000000..06c3325b8 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs @@ -0,0 +1,114 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; + +namespace Squidex.Infrastructure.Json.Objects +{ + public static class JsonValue + { + public static readonly JsonScalar Empty = new JsonScalar(JsonValueType.String, string.Empty); + + public static readonly JsonScalar True = new JsonScalar(JsonValueType.Boolean, true); + public static readonly JsonScalar False = new JsonScalar(JsonValueType.Boolean, false); + + public static readonly JsonNull Null = JsonNull.Null; + + public static JsonArray Array() + { + return new JsonArray(); + } + + public static JsonArray Array(params object[] values) + { + return new JsonArray(values); + } + + public static JsonObject Object() + { + return new JsonObject(); + } + + public static IJsonValue Create(object value) + { + if (value == null) + { + return Null; + } + + if (value is IJsonValue v) + { + return v; + } + + switch (value) + { + case string s: + return Create(s); + case bool b: + return Create(b); + case float f: + return Create(f); + case double d: + return Create(d); + case int i: + return Create(i); + case long l: + return Create(l); + } + + throw new ArgumentException("Invalid json type"); + } + + public static IJsonValue Create(bool value) + { + return value ? True : False; + } + + public static IJsonValue Create(double value) + { + Guard.ValidNumber(value, nameof(value)); + + return new JsonScalar(JsonValueType.Number, value); + } + + public static IJsonValue Create(double? value) + { + if (value == null) + { + return Null; + } + + return Create(value.Value); + } + + public static IJsonValue Create(bool? value) + { + if (value == null) + { + return Null; + } + + return Create(value.Value); + } + + public static IJsonValue Create(string value) + { + if (value == null) + { + return Null; + } + + if (value.Length == 0) + { + return Empty; + } + + return new JsonScalar(JsonValueType.String, value); + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs b/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs new file mode 100644 index 000000000..8b4a13022 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Json.Objects +{ + public enum JsonValueType + { + Array, + Boolean, + Null, + Number, + Object, + String + } +} diff --git a/src/Squidex.Infrastructure/Orleans/J.cs b/src/Squidex.Infrastructure/Orleans/J.cs index 9f89f09d6..aee59a82d 100644 --- a/src/Squidex.Infrastructure/Orleans/J.cs +++ b/src/Squidex.Infrastructure/Orleans/J.cs @@ -6,13 +6,15 @@ // ========================================================================== using System.Threading.Tasks; -using Newtonsoft.Json; +using Squidex.Infrastructure.Json; + +#pragma warning disable SA1401 // Fields must be private namespace Squidex.Infrastructure.Orleans { public static class J { - internal static readonly JsonSerializer DefaultSerializer = JsonSerializer.CreateDefault(); + public static IJsonSerializer DefaultSerializer; public static J AsJ(this T value) { diff --git a/src/Squidex.Infrastructure/Orleans/J{T}.cs b/src/Squidex.Infrastructure/Orleans/J{T}.cs index e39b12664..bfef10aac 100644 --- a/src/Squidex.Infrastructure/Orleans/J{T}.cs +++ b/src/Squidex.Infrastructure/Orleans/J{T}.cs @@ -6,13 +6,12 @@ // ========================================================================== using System; -using System.IO; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Orleans.CodeGeneration; using Orleans.Concurrency; using Orleans.Serialization; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; namespace Squidex.Infrastructure.Orleans @@ -22,7 +21,6 @@ namespace Squidex.Infrastructure.Orleans { public T Value { get; } - [JsonConstructor] public J(T value) { Value = value; @@ -63,12 +61,7 @@ namespace Squidex.Infrastructure.Orleans var stream = new StreamWriterWrapper(context.StreamWriter); - using (var writer = new JsonTextWriter(new StreamWriter(stream))) - { - jsonSerializer.Serialize(writer, input); - - writer.Flush(); - } + jsonSerializer.Serialize(input, stream); } } @@ -81,18 +74,15 @@ namespace Squidex.Infrastructure.Orleans var stream = new StreamReaderWrapper(context.StreamReader); - using (var reader = new JsonTextReader(new StreamReader(stream))) - { - return jsonSerializer.Deserialize(reader, expected); - } + return jsonSerializer.Deserialize(stream, expected); } } - private static JsonSerializer GetSerializer(ISerializerContext context) + private static IJsonSerializer GetSerializer(ISerializerContext context) { try { - return context?.ServiceProvider?.GetService() ?? J.DefaultSerializer; + return context?.ServiceProvider?.GetService() ?? J.DefaultSerializer; } catch { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs index 5a3d8cb6d..5a2ac24a7 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs @@ -6,8 +6,6 @@ // ========================================================================== using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Xunit; @@ -15,8 +13,6 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class AppClientJsonTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { @@ -34,9 +30,9 @@ namespace Squidex.Domain.Apps.Core.Model.Apps clients = clients.Revoke("4"); - var appClients = JToken.FromObject(clients, serializer).ToObject(serializer); + var serialized = clients.SerializeAndDeserialize(); - appClients.Should().BeEquivalentTo(clients); + serialized.Should().BeEquivalentTo(clients); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs index 2f42622f7..d8f86cde3 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs @@ -6,8 +6,6 @@ // ========================================================================== using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Xunit; @@ -15,8 +13,6 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class AppContributorsJsonTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { @@ -26,7 +22,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps contributors = contributors.Assign("2", Role.Editor); contributors = contributors.Assign("3", Role.Owner); - var serialized = JToken.FromObject(contributors, serializer).ToObject(serializer); + var serialized = contributors.SerializeAndDeserialize(); serialized.Should().BeEquivalentTo(contributors); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternJsonTests.cs index 4a3739352..9ffe845bc 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternJsonTests.cs @@ -7,8 +7,6 @@ using System; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Xunit; @@ -16,8 +14,6 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class AppPatternJsonTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { @@ -35,9 +31,9 @@ namespace Squidex.Domain.Apps.Core.Model.Apps patterns = patterns.Remove(guid1); - var appPatterns = JToken.FromObject(patterns, serializer).ToObject(serializer); + var serialized = patterns.SerializeAndDeserialize(); - appPatterns.Should().BeEquivalentTo(patterns); + serialized.Should().BeEquivalentTo(patterns); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs index 4cf183ab3..35588c136 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs @@ -6,8 +6,6 @@ // ========================================================================== using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure; using Xunit; @@ -16,16 +14,14 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class AppPlanTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { - var sut = new AppPlan(new RefToken("user", "Me"), "free"); + var plan = new AppPlan(new RefToken("user", "Me"), "free"); - var serialized = JToken.FromObject(sut, serializer).ToObject(serializer); + var serialized = plan.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(sut); + serialized.Should().BeEquivalentTo(plan); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs index c0e36b181..0e5f6c51d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigJsonTests.cs @@ -7,8 +7,6 @@ using System.Linq; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure; using Xunit; @@ -17,20 +15,18 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class LanguagesConfigJsonTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { - var sut = LanguagesConfig.Build( + var languages = LanguagesConfig.Build( new LanguageConfig(Language.EN), new LanguageConfig(Language.DE, true, Language.EN), new LanguageConfig(Language.IT, false, Language.DE)) .MakeMaster(Language.IT); - var serialized = JToken.FromObject(sut, serializer).ToObject(serializer); + var serialized = languages.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(sut); + serialized.Should().BeEquivalentTo(languages); Assert.Same(serialized.FirstOrDefault(x => x.Key == "it"), serialized.Master); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs index 82b47099a..d79477f0a 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs @@ -6,8 +6,6 @@ // ========================================================================== using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Xunit; @@ -15,16 +13,14 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { public class RolesJsonTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - [Fact] public void Should_serialize_and_deserialize() { var sut = Roles.CreateDefaults("my-app"); - var serialized = JToken.FromObject(sut, serializer).ToObject(serializer); + var roles = sut.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(sut); + roles.Should().BeEquivalentTo(sut); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs index 3c0356896..fe4b91dc5 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.Json.Objects; using Xunit; #pragma warning disable xUnit2013 // Do not use equality check to check for collection size. @@ -23,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents .AddField("field2", new ContentFieldData() .AddValue("en", 2) - .AddValue("it", null)); + .AddValue("it", JsonValue.Null)); var actual = input.ToCleaned(); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs index 5a1cd96f3..70d28c50b 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs @@ -9,8 +9,6 @@ using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Triggers; using Xunit; @@ -21,8 +19,6 @@ namespace Squidex.Domain.Apps.Core.Model.Rules { public class RuleTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); - public static readonly List Triggers = typeof(Rule).Assembly.GetTypes() .Where(x => x.BaseType == typeof(RuleTrigger)) @@ -123,9 +119,9 @@ namespace Squidex.Domain.Apps.Core.Model.Rules { var rule_1 = rule_0.Disable(); - var appClients = JToken.FromObject(rule_1, serializer).ToObject(serializer); + var serialized = rule_1.SerializeAndDeserialize(); - appClients.Should().BeEquivalentTo(rule_1); + serialized.Should().BeEquivalentTo(rule_1); } [Theory] diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs index 8dacfd80a..1f8073699 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs @@ -9,8 +9,6 @@ using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Xunit; @@ -20,7 +18,6 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas { public class SchemaTests { - private readonly JsonSerializer serializer = TestData.DefaultSerializer(); private readonly Schema schema_0 = new Schema("my-schema"); [Fact] @@ -282,7 +279,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas public void Should_serialize_and_deserialize_schema() { var schemaSource = TestData.MixedSchema(); - var schemaTarget = JToken.FromObject(schemaSource, serializer).ToObject(serializer); + var schemaTarget = schemaSource.SerializeAndDeserialize(); schemaTarget.Should().BeEquivalentTo(schemaSource); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs index 54e71a9b9..0429afaab 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs @@ -6,11 +6,11 @@ // ========================================================================== using System.Collections.Generic; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Xunit; #pragma warning disable xUnit2013 // Do not use equality check to check for collection size. @@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent .AddValue("en", 2)) .AddField("field2", new ContentFieldData() - .AddValue("de", null) + .AddValue("de", JsonValue.Null) .AddValue("en", 4)) .AddField("field3", new ContentFieldData() @@ -57,10 +57,20 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var expected = new Dictionary { - { "field1", new ContentFieldData().AddValue("de", 1).AddValue("en", 2) }, - { "field2", new ContentFieldData().AddValue("de", null).AddValue("en", 4) }, - { "field3", (JValue)6 }, - { "field4", (JValue)7 } + { + "field1", + new ContentFieldData() + .AddValue("de", 1) + .AddValue("en", 2) + }, + { + "field2", + new ContentFieldData() + .AddValue("de", JsonValue.Null) + .AddValue("en", 4) + }, + { "field3", JsonValue.Create(6) }, + { "field4", JsonValue.Create(7) } }; Assert.True(expected.EqualsDictionary(output)); @@ -77,7 +87,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent .AddValue("en", 2)) .AddField("field2", new ContentFieldData() - .AddValue("de", null) + .AddValue("de", JsonValue.Null) .AddValue("en", 4)) .AddField("field3", new ContentFieldData() @@ -91,13 +101,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new LanguageConfig(Language.EN), new LanguageConfig(Language.DE, false, Language.EN)); - var output = (Dictionary)data.ToFlatLanguageModel(fallbackConfig, new List { Language.DE }); + var output = (Dictionary)data.ToFlatLanguageModel(fallbackConfig, new List { Language.DE }); - var expected = new Dictionary + var expected = new Dictionary { - { "field1", 1 }, - { "field2", 4 }, - { "field3", 6 } + { "field1", JsonValue.Create(1) }, + { "field2", JsonValue.Create(4) }, + { "field3", JsonValue.Create(6) } }; Assert.True(expected.EqualsDictionary(output)); @@ -114,7 +124,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent .AddValue("en", 2)) .AddField("field2", new ContentFieldData() - .AddValue("de", null) + .AddValue("de", JsonValue.Null) .AddValue("en", 4)) .AddField("field3", new ContentFieldData() @@ -123,13 +133,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new ContentFieldData() .AddValue("it", 7)); - var output = (Dictionary)data.ToFlatLanguageModel(languagesConfig, new List { Language.DE, Language.EN }); + var output = (Dictionary)data.ToFlatLanguageModel(languagesConfig, new List { Language.DE, Language.EN }); - var expected = new Dictionary + var expected = new Dictionary { - { "field1", 1 }, - { "field2", 4 }, - { "field3", 6 } + { "field1", JsonValue.Create(1) }, + { "field2", JsonValue.Create(4) }, + { "field3", JsonValue.Create(6) } }; Assert.True(expected.EqualsDictionary(output)); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs index 2415089c4..43700a84c 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs @@ -5,10 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ConvertContent @@ -174,7 +174,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new NamedContentData() .AddField("field1", new ContentFieldData() - .AddValue("en", new JArray("hello", "loved"))) + .AddValue("en", new JsonArray("hello", "loved"))) .AddField("field2", new ContentFieldData() .AddValue("iv", "world")); @@ -191,7 +191,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new NamedContentData() .AddField("field1", new ContentFieldData() - .AddValue("en", new JArray(new JObject(new JProperty("p1", "hello"))))) + .AddValue("en", new JsonArray(JsonValue.Object().Add("p1", "hello")))) .AddField("field2", new ContentFieldData() .AddValue("iv", "world")); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index 58417c0c9..d5b96a58d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs @@ -8,12 +8,13 @@ using System.Collections.Generic; using System.Linq; using FakeItEasy; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ConvertContent @@ -21,6 +22,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent public class FieldConvertersTests { private readonly IAssetUrlGenerator assetUrlGenerator = A.Fake(); + private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE); private readonly RootField jsonField = Fields.Json(1, "1", Partitioning.Invariant); private readonly RootField stringLanguageField = Fields.String(1, "1", Partitioning.Language); @@ -42,7 +44,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var input = new ContentFieldData() - .AddValue("iv", new JObject()); + .AddValue("iv", JsonValue.Object()); var actual = FieldConverters.ForValues((f, i) => Value.Unset)(input, stringInvariantField); @@ -56,9 +58,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var input = new ContentFieldData() - .AddValue("iv", new JObject()); + .AddValue("iv", JsonValue.Object()); - var actual = FieldConverters.ForValues(ValueConverters.EncodeJson())(input, jsonField); + var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(serializer))(input, jsonField); var expected = new ContentFieldData() @@ -73,20 +75,20 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("field1", 100), - new JProperty("field2", 200), - new JProperty("invalid", 300)))); + new JsonArray( + JsonValue.Object() + .Add("field1", 100) + .Add("field2", 200) + .Add("invalid", 300))); var actual = FieldConverters.ForNestedName2Id(ValueConverters.ExcludeHidden())(input, arrayField); var expected = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("1", 100)))); + new JsonArray( + JsonValue.Object() + .Add("1", 100))); Assert.Equal(expected, actual); } @@ -97,20 +99,20 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("field1", 100), - new JProperty("field2", 200), - new JProperty("invalid", 300)))); + new JsonArray( + JsonValue.Object() + .Add("field1", 100) + .Add("field2", 200) + .Add("invalid", 300))); var actual = FieldConverters.ForNestedName2Name(ValueConverters.ExcludeHidden())(input, arrayField); var expected = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("field1", 100)))); + new JsonArray( + JsonValue.Object() + .Add("field1", 100))); Assert.Equal(expected, actual); } @@ -121,20 +123,20 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("1", 100), - new JProperty("2", 200), - new JProperty("99", 300)))); + new JsonArray( + JsonValue.Object() + .Add("1", 100) + .Add("2", 200) + .Add("99", 300))); var actual = FieldConverters.ForNestedId2Id(ValueConverters.ExcludeHidden())(input, arrayField); var expected = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("1", 100)))); + new JsonArray( + JsonValue.Object() + .Add("1", 100))); Assert.Equal(expected, actual); } @@ -145,20 +147,20 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("1", 100), - new JProperty("2", 200), - new JProperty("99", 300)))); + new JsonArray( + JsonValue.Object() + .Add("1", 100) + .Add("2", 200) + .Add("99", 300))); var actual = FieldConverters.ForNestedId2Name(ValueConverters.ExcludeHidden())(input, arrayField); var expected = new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("field1", 100)))); + new JsonArray( + JsonValue.Object() + .Add("field1", 100))); Assert.Equal(expected, actual); } @@ -422,11 +424,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JArray("url/to/1", "url/to/2")); + .AddValue("iv", new JsonArray("url/to/1", "url/to/2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "1" }), assetUrlGenerator)(source, assetsField); @@ -438,11 +440,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JArray("url/to/1", "url/to/2")); + .AddValue("iv", new JsonArray("url/to/1", "url/to/2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "*" }), assetUrlGenerator)(source, assetsField); @@ -454,11 +456,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "2" }), assetUrlGenerator)(source, assetsField); @@ -470,11 +472,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JArray("1", "2")); + .AddValue("iv", new JsonArray("1", "2")); var rtesult = FieldConverters.ResolveAssetUrls(null, assetUrlGenerator)(source, assetsField); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs index e5cd27572..ada3f0861 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs @@ -5,9 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ConvertContent @@ -17,23 +18,24 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent private readonly RootField stringField = Fields.String(1, "1", Partitioning.Invariant); private readonly RootField jsonField = Fields.Json(1, "1", Partitioning.Invariant); private readonly RootField numberField = Fields.Number(1, "1", Partitioning.Invariant); + private readonly IJsonSerializer jsonSerializer = TestData.DefaultSerializer(); [Fact] public void Should_encode_json_value() { - var source = new JObject(); + var source = JsonValue.Object(); - var result = ValueConverters.EncodeJson()(source, jsonField); + var result = ValueConverters.EncodeJson(jsonSerializer)(source, jsonField); - Assert.Equal("e30=", result); + Assert.Equal(JsonValue.Create("e30="), result); } [Fact] public void Should_return_same_value_if_encoding_null_value() { - var source = JValue.CreateNull(); + var source = JsonValue.Null; - var result = ValueConverters.EncodeJson()(source, jsonField); + var result = ValueConverters.EncodeJson(jsonSerializer)(source, jsonField); Assert.Same(source, result); } @@ -41,9 +43,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent [Fact] public void Should_return_same_value_if_encoding_non_json_field() { - var source = (JToken)"NO-JSON"; + var source = JsonValue.Create("NO-JSON"); - var result = ValueConverters.EncodeJson()(source, stringField); + var result = ValueConverters.EncodeJson(jsonSerializer)(source, stringField); Assert.Same(source, result); } @@ -51,19 +53,19 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent [Fact] public void Should_decode_json_values() { - var source = "e30="; + var source = JsonValue.Create("e30="); - var result = ValueConverters.DecodeJson()(source, jsonField); + var result = ValueConverters.DecodeJson(jsonSerializer)(source, jsonField); - Assert.Equal(new JObject(), result); + Assert.Equal(JsonValue.Object(), result); } [Fact] public void Should_return_same_value_if_decoding_null_value() { - var source = JValue.CreateNull(); + var source = JsonValue.Null; - var result = ValueConverters.DecodeJson()(source, jsonField); + var result = ValueConverters.DecodeJson(jsonSerializer)(source, jsonField); Assert.Same(source, result); } @@ -71,9 +73,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent [Fact] public void Should_return_same_value_if_decoding_non_json_field() { - var source = JValue.CreateNull(); + var source = JsonValue.Null; - var result = ValueConverters.EncodeJson()(source, stringField); + var result = ValueConverters.EncodeJson(jsonSerializer)(source, stringField); Assert.Same(source, result); } @@ -81,7 +83,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent [Fact] public void Should_return_unset_if_field_hidden() { - var source = 123; + var source = JsonValue.Create(123); var result = ValueConverters.ExcludeHidden()(source, stringField.Hide()); @@ -91,7 +93,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent [Fact] public void Should_return_unset_if_field_has_wrong_type() { - var source = "invalid"; + var source = JsonValue.Create("invalid"); var result = ValueConverters.ExcludeChangedTypes()(source, numberField); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs index 8c35f5e19..fff6720c4 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/EnrichContent/ContentEnrichmentTests.cs @@ -6,13 +6,13 @@ // ========================================================================== using System; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.EnrichContent; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Xunit; #pragma warning disable xUnit2004 // Do not use equality check to test for boolean conditions @@ -53,14 +53,14 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent data.Enrich(schema, languagesConfig.ToResolver()); - Assert.Equal(456, (int)data["my-number"]["iv"]); + Assert.Equal(456, ((JsonScalar)data["my-number"]["iv"]).Value); - Assert.Equal("de-string", (string)data["my-string"]["de"]); - Assert.Equal("en-string", (string)data["my-string"]["en"]); + Assert.Equal("de-string", data["my-string"]["de"].ToString()); + Assert.Equal("en-string", data["my-string"]["en"].ToString()); - Assert.Equal(Now.ToString(), (string)data["my-datetime"]["iv"]); + Assert.Equal(Now.ToString(), data["my-datetime"]["iv"].ToString()); - Assert.True((bool)data["my-boolean"]["iv"]); + Assert.True(((JsonScalar)data["my-boolean"]["iv"]).Value); } [Fact] @@ -77,8 +77,8 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent data.Enrich(schema, languagesConfig.ToResolver()); - Assert.Equal("en-string", (string)data["my-string"]["de"]); - Assert.Equal("en-string", (string)data["my-string"]["en"]); + Assert.Equal("en-string", data["my-string"]["de"].ToString()); + Assert.Equal("en-string", data["my-string"]["en"].ToString()); } [Fact] @@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Assets(1, "1", Partitioning.Invariant, new AssetsFieldProperties()); - Assert.Equal(new JArray(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Boolean(1, "1", Partitioning.Invariant, new BooleanFieldProperties { DefaultValue = true }); - Assert.Equal(true, DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.True, DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.DateTime(1, "1", Partitioning.Invariant, new DateTimeFieldProperties { DefaultValue = FutureDays(15) }); - Assert.Equal(FutureDays(15).ToString(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Create(FutureDays(15).ToString()), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -118,7 +118,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.DateTime(1, "1", Partitioning.Invariant, new DateTimeFieldProperties { CalculatedDefaultValue = DateTimeCalculatedDefaultValue.Today }); - Assert.Equal("2017-10-12", DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Create("2017-10-12"), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -128,7 +128,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.DateTime(1, "1", Partitioning.Invariant, new DateTimeFieldProperties { CalculatedDefaultValue = DateTimeCalculatedDefaultValue.Now }); - Assert.Equal("2017-10-12T16:30:10Z", DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Create("2017-10-12T16:30:10Z"), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -138,7 +138,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Json(1, "1", Partitioning.Invariant, new JsonFieldProperties()); - Assert.Equal(new JObject(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Object(), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -148,7 +148,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Geolocation(1, "1", Partitioning.Invariant, new GeolocationFieldProperties()); - Assert.Equal(JValue.CreateNull(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Null, DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -158,7 +158,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Number(1, "1", Partitioning.Invariant, new NumberFieldProperties { DefaultValue = 12 }); - Assert.Equal(12, DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Create(12), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -168,7 +168,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.References(1, "1", Partitioning.Invariant, new ReferencesFieldProperties()); - Assert.Equal(new JArray(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -178,7 +178,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.String(1, "1", Partitioning.Invariant, new StringFieldProperties { DefaultValue = "default" }); - Assert.Equal("default", DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Create("default"), DefaultValueFactory.CreateDefaultValue(field, Now)); } [Fact] @@ -188,7 +188,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EnrichContent Fields.Tags(1, "1", Partitioning.Invariant, new TagsFieldProperties()); - Assert.Equal(new JArray(), DefaultValueFactory.CreateDefaultValue(field, Now)); + Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, Now)); } private static Instant FutureDays(int days) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs index 1d83fcd1f..684035e91 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs @@ -7,12 +7,12 @@ using System; using System.Linq; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ExtractReferenceIds; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Xunit; #pragma warning disable xUnit2013 // Do not use equality check to check for collection size. @@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds new IdContentData() .AddField(5, new ContentFieldData() - .AddValue("iv", new JArray(id1.ToString(), id2.ToString()))); + .AddValue("iv", JsonValue.Array(id1.ToString(), id2.ToString()))); var ids = input.GetReferencedIds(schema).ToArray(); @@ -66,16 +66,16 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds new IdContentData() .AddField(5, new ContentFieldData() - .AddValue("iv", new JArray(id1.ToString(), id2.ToString()))); + .AddValue("iv", JsonValue.Array(id1.ToString(), id2.ToString()))); var converter = FieldConverters.ForValues(ValueReferencesConverter.CleanReferences(new[] { id2 })); var actual = input.ConvertId2Id(schema, converter); - var cleanedValue = (JArray)actual[5]["iv"]; + var cleanedValue = (JsonArray)actual[5]["iv"]; Assert.Equal(1, cleanedValue.Count); - Assert.Equal(id1.ToString(), cleanedValue[0]); + Assert.Equal(id1.ToString(), cleanedValue[0].ToString()); } [Fact] @@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds { var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant); - var result = sut.ExtractReferences("invalid").ToArray(); + var result = sut.ExtractReferences(JsonValue.Create("invalid")).ToArray(); Assert.Empty(result); } @@ -116,7 +116,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds { var sut = Fields.String(1, "my-string", Partitioning.Invariant); - var result = sut.ExtractReferences("invalid").ToArray(); + var result = sut.ExtractReferences(JsonValue.Create("invalid")).ToArray(); Assert.Empty(result); } @@ -126,9 +126,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds { var sut = Fields.Assets(1, "my-asset", Partitioning.Invariant); - var result = sut.CleanReferences(null, null); + var result = sut.CleanReferences(JsonValue.Null, null); - Assert.Null(result); + Assert.Equal(JsonValue.Null, result); } [Fact] @@ -170,9 +170,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds new ReferencesFieldProperties { SchemaId = schemaId })); var value = - new JArray( - new JObject( - new JProperty("my-refs", CreateValue(id1, id2)))); + JsonValue.Array( + JsonValue.Object() + .Add("my-refs", CreateValue(id1, id2))); var result = sut.ExtractReferences(value).ToArray(); @@ -199,7 +199,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds var sut = Fields.References(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId }); - var result = sut.ExtractReferences(null).ToArray(); + var result = sut.ExtractReferences(JsonValue.Null).ToArray(); Assert.Equal(new[] { schemaId }, result); } @@ -210,7 +210,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds var sut = Fields.References(1, "my-refs", Partitioning.Invariant, new ReferencesFieldProperties { SchemaId = schemaId }); - var result = sut.ExtractReferences("invalid").ToArray(); + var result = sut.ExtractReferences(JsonValue.Create("invalid")).ToArray(); Assert.Equal(new[] { schemaId }, result); } @@ -220,9 +220,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds { var sut = Fields.References(1, "my-refs", Partitioning.Invariant); - var result = sut.CleanReferences(null, null); + var result = sut.CleanReferences(JsonValue.Null, null); - Assert.Null(result); + Assert.Equal(JsonValue.Null, result); } [Fact] @@ -267,9 +267,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ExtractReferenceIds Assert.Same(token, result); } - private static JToken CreateValue(params Guid[] ids) + private static IJsonValue CreateValue(params Guid[] ids) { - return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType().ToArray()); + return ids == null ? (IJsonValue)JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray()); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index 7cdb92b62..bee39a8eb 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; using Squidex.Shared.Identity; using Squidex.Shared.Users; using Xunit; @@ -24,7 +25,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { public class RuleEventFormatterTests { - private readonly JsonSerializer serializer = JsonSerializer.CreateDefault(); + private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly IUser user = A.Fake(); private readonly IRuleUrlGenerator urlGenerator = A.Fake(); private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); @@ -48,7 +49,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { var result = sut.ToPayload(new { Value = 1 }); - Assert.True(result is JObject); + Assert.True(result is string); } [Fact] @@ -58,7 +59,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var result = sut.ToPayload(@event); - Assert.True(result is JObject); + Assert.True(result is string); } [Fact] @@ -68,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var result = sut.ToEnvelope(@event); - Assert.Equal("MyEventName", result["type"]); + Assert.Contains("MyEventName", result); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index f21de9c6b..5b2d5b77b 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; using FakeItEasy; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; @@ -18,6 +17,7 @@ using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Xunit; #pragma warning disable xUnit2009 // Do not use boolean check to check for string equality @@ -28,6 +28,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { private readonly IRuleTriggerHandler ruleTriggerHandler = A.Fake(); private readonly IRuleActionHandler ruleActionHandler = A.Fake(); + private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly IEventEnricher eventEnricher = A.Fake(); private readonly IClock clock = A.Fake(); private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); @@ -67,7 +68,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => ruleTriggerHandler.TriggerType) .Returns(typeof(ContentChangedTrigger)); - sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, clock, typeNameRegistry); + sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, serializer, clock, typeNameRegistry); } [Fact] @@ -129,7 +130,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules ruleEnvelope.SetTimestamp(now.Minus(Duration.FromDays(3))); - var actionData = new JObject(); + var actionData = "{}"; var actionDescription = "MyDescription"; A.CallTo(() => clock.GetCurrentInstant()) @@ -159,7 +160,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules ruleEnvelope.SetTimestamp(now); var actionName = "ValidAction"; - var actionData = new JObject(); + var actionData = "{}"; var actionDescription = "MyDescription"; A.CallTo(() => clock.GetCurrentInstant()) @@ -188,7 +189,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_succeeded_job_with_full_dump_when_handler_returns_no_exception() { - var ruleJob = new JObject(); + var ruleJob = "{}"; var actionDump = "MyDump"; @@ -206,7 +207,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_failed_job_with_full_dump_when_handler_returns_exception() { - var ruleJob = new JObject(); + var ruleJob = "{}"; var actionDump = "MyDump"; @@ -224,7 +225,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_timedout_job_with_full_dump_when_exception_from_handler_indicates_timeout() { - var ruleJob = new JObject(); + var ruleJob = "{}"; var actionDump = "MyDump"; @@ -243,7 +244,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_create_exception_details_when_job_to_execute_failed() { - var ruleJob = new JObject(); + var ruleJob = "{}"; var ruleError = new InvalidOperationException(); A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs index 592e0765e..488c32b9f 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs @@ -7,9 +7,9 @@ using Jint; using Jint.Runtime; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Scripting.ContentWrapper; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.Scripting @@ -155,13 +155,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JArray(1.0, 2.0))); + .AddValue("iv", new JsonArray(1.0, 2.0))); var expected = new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JArray(1.0, 4.0, 5.0))); + .AddValue("iv", new JsonArray(1.0, 4.0, 5.0))); var result = ExecuteScript(original, @"data.number.iv = [data.number.iv[0], data.number.iv[1] + 2, 5]"); @@ -175,13 +175,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JObject(new JProperty("lat", 1.0)))); + .AddValue("iv", JsonValue.Object().Add("lat", 1.0))); var expected = new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JObject(new JProperty("lat", 1.0), new JProperty("lon", 4.0)))); + .AddValue("iv", JsonValue.Object().Add("lat", 1.0).Add("lon", 4.0))); var result = ExecuteScript(original, @"data.number.iv = { lat: data.number.iv.lat, lon: data.number.iv.lat + 3 }"); @@ -265,7 +265,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("obj", new ContentFieldData() - .AddValue("iv", new JObject(new JProperty("readonly", 1)))); + .AddValue("iv", JsonValue.Object().Add("readonly", 1))); Assert.Throws(() => ExecuteScript(original, "data.obj.iv.invalid = 1")); Assert.Throws(() => ExecuteScript(original, "data.obj.iv.readonly = 2")); @@ -278,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("obj", new ContentFieldData() - .AddValue("iv", new JArray())); + .AddValue("iv", new JsonArray())); ExecuteScript(original, "data.obj.iv[0] = 1"); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs index 7f0843ee2..6dfc8dba0 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Tags/TagNormalizerTests.cs @@ -9,17 +9,16 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using FakeItEasy; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Tags; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.Tags { public class TagNormalizerTests { - private static readonly JTokenEqualityComparer JTokenEqualityComparer = new JTokenEqualityComparer(); private readonly ITagService tagService = A.Fake(); private readonly Guid appId = Guid.NewGuid(); private readonly Guid schemaId = Guid.NewGuid(); @@ -56,8 +55,8 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags await tagService.NormalizeAsync(appId, schemaId, schema, newData, oldData); - Assert.Equal(new JArray("id2_1", "id2_2"), newData["tags2"]["iv"], JTokenEqualityComparer); - Assert.Equal(new JArray("id4"), newData["array"]["iv"][0]["nestedTags2"], JTokenEqualityComparer); + Assert.Equal(JsonValue.Array("id2_1", "id2_2"), newData["tags2"]["iv"]); + Assert.Equal(JsonValue.Array("id4"), GetNestedTags(newData)); } [Fact] @@ -77,8 +76,8 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags await tagService.NormalizeAsync(appId, schemaId, schema, newData, null); - Assert.Equal(new JArray("id2_1", "id2_2"), newData["tags2"]["iv"], JTokenEqualityComparer); - Assert.Equal(new JArray("id4"), newData["array"]["iv"][0]["nestedTags2"], JTokenEqualityComparer); + Assert.Equal(JsonValue.Array("id2_1", "id2_2"), newData["tags2"]["iv"]); + Assert.Equal(JsonValue.Array("id4"), GetNestedTags(newData)); } [Fact] @@ -98,8 +97,16 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags await tagService.NormalizeAsync(appId, schemaId, schema, newData, null); - Assert.Equal(new JArray("name2_1", "name2_2"), newData["tags2"]["iv"], JTokenEqualityComparer); - Assert.Equal(new JArray("name4"), newData["array"]["iv"][0]["nestedTags2"], JTokenEqualityComparer); + Assert.Equal(JsonValue.Array("name2_1", "name2_2"), newData["tags2"]["iv"]); + Assert.Equal(JsonValue.Array("name4"), GetNestedTags(newData)); + } + + private IJsonValue GetNestedTags(NamedContentData newData) + { + var array = (JsonArray)newData["array"]["iv"]; + var arrayItem = (JsonObject)array[0]; + + return arrayItem["nestedTags2"]; } private static NamedContentData GenerateData(string prefix) @@ -107,21 +114,21 @@ namespace Squidex.Domain.Apps.Core.Operations.Tags return new NamedContentData() .AddField("tags1", new ContentFieldData() - .AddValue("iv", new JArray($"{prefix}1"))) + .AddValue("iv", JsonValue.Array($"{prefix}1"))) .AddField("tags2", new ContentFieldData() - .AddValue("iv", new JArray($"{prefix}2_1", $"{prefix}2_2"))) + .AddValue("iv", JsonValue.Array($"{prefix}2_1", $"{prefix}2_2"))) .AddField("string", new ContentFieldData() .AddValue("iv", $"{prefix}stringValue")) .AddField("array", new ContentFieldData() .AddValue("iv", - new JArray( - new JObject( - new JProperty("nestedTags1", new JArray($"{prefix}3")), - new JProperty("nestedTags2", new JArray($"{prefix}4")), - new JProperty("string", $"{prefix}nestedStringValue"))))); + JsonValue.Array( + JsonValue.Object() + .Add("nestedTags1", JsonValue.Array($"{prefix}3")) + .Add("nestedTags2", JsonValue.Array($"{prefix}4")) + .Add("string", $"{prefix}nestedStringValue")))); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs index 44ee87e03..05897ce5c 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs @@ -9,8 +9,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new ArrayFieldProperties()); - await sut.ValidateAsync(CreateValue(new JObject()), errors, ValidationTestExtensions.ValidContext); + await sut.ValidateAsync(CreateValue(JsonValue.Object()), errors, ValidationTestExtensions.ValidContext); Assert.Empty(errors); } @@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new ArrayFieldProperties()); - await sut.ValidateAsync("invalid", errors); + await sut.ValidateAsync(JsonValue.Create("invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new ArrayFieldProperties { MinItems = 3 }); - await sut.ValidateAsync(CreateValue(new JObject(), new JObject()), errors); + await sut.ValidateAsync(CreateValue(JsonValue.Object(), JsonValue.Object()), errors); errors.Should().BeEquivalentTo( new[] { "Must have at least 3 item(s)." }); @@ -96,15 +96,15 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new ArrayFieldProperties { MaxItems = 1 }); - await sut.ValidateAsync(CreateValue(new JObject(), new JObject()), errors); + await sut.ValidateAsync(CreateValue(JsonValue.Object(), JsonValue.Object()), errors); errors.Should().BeEquivalentTo( new[] { "Must have not more than 1 item(s)." }); } - private static JToken CreateValue(params JObject[] ids) + private static IJsonValue CreateValue(params JsonObject[] ids) { - return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType().ToArray()); + return ids == null ? (IJsonValue)JsonValue.Null : JsonValue.Array(ids.OfType().ToArray()); } private static RootField Field(ArrayFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs index 36aa3c0e0..7268df598 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs @@ -10,10 +10,10 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -119,7 +119,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new AssetsFieldProperties()); - await sut.ValidateAsync("invalid", errors); + await sut.ValidateAsync(JsonValue.Create("invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -263,9 +263,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent }); } - private static JToken CreateValue(params Guid[] ids) + private static IJsonValue CreateValue(params Guid[] ids) { - return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType().ToArray()); + return ids == null ? (IJsonValue)JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray()); } private static RootField Field(AssetsFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs index 537f8d9a7..0d9d708c1 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/BooleanFieldTests.cs @@ -8,8 +8,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -62,15 +62,15 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new BooleanFieldProperties()); - await sut.ValidateAsync(CreateValue("Invalid"), errors); + await sut.ValidateAsync(JsonValue.Create("Invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); } - private static JValue CreateValue(object v) + private static IJsonValue CreateValue(bool? v) { - return new JValue(v); + return JsonValue.Create(v); } private static RootField Field(BooleanFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs index 441810ec7..8d6893cdd 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs @@ -8,12 +8,12 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new NamedContentData() .AddField("my-field", new ContentFieldData() - .AddValue(1000)); + .AddValue("iv", 1000)); await data.ValidateAsync(context, schema, languagesConfig.ToResolver(), errors); @@ -214,7 +214,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new NamedContentData() .AddField("my-field", new ContentFieldData() - .AddValue(1000)); + .AddValue("iv", 1000)); await data.ValidatePartialAsync(context, schema, languagesConfig.ToResolver(), errors); @@ -329,10 +329,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new NamedContentData() .AddField("my-field", new ContentFieldData() - .AddValue("iv", new JArray( - new JObject(), - new JObject(new JProperty("my-nested", 1)), - new JObject()))); + .AddValue("iv", + JsonValue.Array( + JsonValue.Object(), + JsonValue.Object().Add("my-nested", 1), + JsonValue.Object()))); await data.ValidatePartialAsync(context, schema, languagesConfig.ToResolver(), errors); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs index ac74d57ad..f2df2df3d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/DateTimeFieldTests.cs @@ -9,9 +9,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new DateTimeFieldProperties()); - await sut.ValidateAsync(CreateValue(null), errors); + await sut.ValidateAsync(JsonValue.Null, errors); Assert.Empty(errors); } @@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new DateTimeFieldProperties { IsRequired = true }); - await sut.ValidateAsync(CreateValue(null), errors); + await sut.ValidateAsync(JsonValue.Null, errors); errors.Should().BeEquivalentTo( new[] { "Field is required." }); @@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new DateTimeFieldProperties()); - await sut.ValidateAsync(CreateValue("Invalid"), errors); + await sut.ValidateAsync(JsonValue.Create("Invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new DateTimeFieldProperties()); - await sut.ValidateAsync(CreateValue(123), errors); + await sut.ValidateAsync(JsonValue.Create(123), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -98,9 +98,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent return Instant.FromDateTimeUtc(DateTime.UtcNow.Date.AddDays(days)); } - private static JValue CreateValue(object v) + private static IJsonValue CreateValue(Instant v) { - return v is Instant ? new JValue(v.ToString()) : new JValue(v); + return JsonValue.Create(v.ToString()); } private static RootField Field(DateTimeFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs index c19149e6b..cfff1afef 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/GeolocationFieldTests.cs @@ -8,8 +8,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties()); - await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); + await sut.ValidateAsync(JsonValue.Null, errors); Assert.Empty(errors); } @@ -41,11 +41,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties()); - var geolocation = new JObject( - new JProperty("latitude", 0), - new JProperty("longitude", 0)); + var geolocation = JsonValue.Object() + .Add("latitude", 0) + .Add("longitude", 0); - await sut.ValidateAsync(CreateValue(geolocation), errors); + await sut.ValidateAsync(geolocation, errors); Assert.Empty(errors); } @@ -55,11 +55,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties { IsRequired = true }); - var geolocation = new JObject( - new JProperty("latitude", 200), - new JProperty("longitude", 0)); + var geolocation = JsonValue.Object() + .Add("latitude", 200) + .Add("longitude", 0); - await sut.ValidateAsync(CreateValue(geolocation), errors); + await sut.ValidateAsync(geolocation, errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -70,11 +70,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties { IsRequired = true }); - var geolocation = new JObject( - new JProperty("latitude", 0), - new JProperty("longitude", 200)); + var geolocation = JsonValue.Object() + .Add("latitude", 0) + .Add("longitude", 200); - await sut.ValidateAsync(CreateValue(geolocation), errors); + await sut.ValidateAsync(geolocation, errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -85,12 +85,12 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties { IsRequired = true }); - var geolocation = new JObject( - new JProperty("invalid", 0), - new JProperty("latitude", 0), - new JProperty("longitude", 0)); + var geolocation = JsonValue.Object() + .Add("invalid", 0) + .Add("latitude", 0) + .Add("longitude", 0); - await sut.ValidateAsync(CreateValue(geolocation), errors); + await sut.ValidateAsync(geolocation, errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -101,17 +101,12 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new GeolocationFieldProperties { IsRequired = true }); - await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); + await sut.ValidateAsync(JsonValue.Null, errors); errors.Should().BeEquivalentTo( new[] { "Field is required." }); } - private static JToken CreateValue(JToken v) - { - return v; - } - private static RootField Field(GeolocationFieldProperties properties) { return Fields.Geolocation(1, "my-geolocation", Partitioning.Invariant, properties); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs index b6cb06f80..16cba1e50 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/JsonFieldTests.cs @@ -8,8 +8,8 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new JsonFieldProperties()); - await sut.ValidateAsync(CreateValue(new JValue(1)), errors); + await sut.ValidateAsync(CreateValue(JsonValue.Create(1)), errors); Assert.Empty(errors); } @@ -41,13 +41,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new JsonFieldProperties { IsRequired = true }); - await sut.ValidateAsync(CreateValue(JValue.CreateNull()), errors); + await sut.ValidateAsync(CreateValue(JsonValue.Null), errors); errors.Should().BeEquivalentTo( new[] { "Field is required." }); } - private static JValue CreateValue(JValue v) + private static IJsonValue CreateValue(IJsonValue v) { return v; } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs index d236198f9..617032bf6 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs @@ -8,9 +8,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new NumberFieldProperties()); - await sut.ValidateAsync(CreateValue(null), errors); + await sut.ValidateAsync(JsonValue.Null, errors); Assert.Empty(errors); } @@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new NumberFieldProperties { IsRequired = true }); - await sut.ValidateAsync(CreateValue(null), errors); + await sut.ValidateAsync(JsonValue.Null, errors); errors.Should().BeEquivalentTo( new[] { "Field is required." }); @@ -86,15 +86,15 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new NumberFieldProperties()); - await sut.ValidateAsync(CreateValue("Invalid"), errors); + await sut.ValidateAsync(JsonValue.Create("Invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); } - private static JValue CreateValue(object v) + private static IJsonValue CreateValue(double v) { - return new JValue(v); + return JsonValue.Create(v); } private static RootField Field(NumberFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs index 768b781bc..48301f83e 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs @@ -10,8 +10,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new ReferencesFieldProperties()); - await sut.ValidateAsync("invalid", errors); + await sut.ValidateAsync(JsonValue.Create("invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -117,9 +117,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new[] { $"Contains invalid reference '{referenceId}'." }); } - private static JToken CreateValue(params Guid[] ids) + private static IJsonValue CreateValue(params Guid[] ids) { - return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType().ToArray()); + return ids == null ? (IJsonValue)JsonValue.Null : JsonValue.Array(ids.Select(x => (object)x.ToString()).ToArray()); } private static RootField Field(ReferencesFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs index 46f9a17de..56d9169e7 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs @@ -8,9 +8,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -103,9 +103,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new[] { "Custom Error Message." }); } - private static JValue CreateValue(object v) + private static IJsonValue CreateValue(string v) { - return new JValue(v); + return JsonValue.Create(v); } private static RootField Field(StringFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs index 3b2a4d3c2..23988ca4f 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs @@ -9,9 +9,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent @@ -75,7 +75,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { var sut = Field(new TagsFieldProperties()); - await sut.ValidateAsync("invalid", errors); + await sut.ValidateAsync(JsonValue.Create("invalid"), errors); errors.Should().BeEquivalentTo( new[] { "Not a valid value." }); @@ -114,9 +114,9 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent new[] { "[1]: Not an allowed value." }); } - private static JToken CreateValue(params string[] ids) + private static IJsonValue CreateValue(params string[] ids) { - return ids == null ? JValue.CreateNull() : (JToken)new JArray(ids.OfType().ToArray()); + return ids == null ? (IJsonValue)JsonValue.Null : JsonValue.Array(ids.OfType().ToArray()); } private static RootField Field(TagsFieldProperties properties) diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs index 3b1e1c454..ba1d0d2ca 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ValidationTestExtensions.cs @@ -9,10 +9,10 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent.Validators; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Operations.ValidateContent { @@ -37,14 +37,14 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent CreateFormatter(errors)); } - public static Task ValidateAsync(this IField field, JToken value, IList errors, ValidationContext context = null) + public static Task ValidateAsync(this IField field, IJsonValue value, IList errors, ValidationContext context = null) { return new FieldValidator(ValidatorsFactory.CreateValidators(field).ToArray(), field).ValidateAsync(value, CreateContext(context), CreateFormatter(errors)); } - public static Task ValidateOptionalAsync(this IField field, JToken value, IList errors, ValidationContext context = null) + public static Task ValidateOptionalAsync(this IField field, IJsonValue value, IList errors, ValidationContext context = null) { return new FieldValidator(ValidatorsFactory.CreateValidators(field).ToArray(), field).ValidateAsync(value, CreateContext(context).Optional(true), diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs index 6b10c3145..530bc3a74 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs @@ -17,13 +17,14 @@ using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; using Xunit; namespace Squidex.Domain.Apps.Core { public static class TestData { - public static JsonSerializer DefaultSerializer() + public static IJsonSerializer DefaultSerializer() { var typeNameRegistry = new TypeNameRegistry(); @@ -35,6 +36,7 @@ namespace Squidex.Domain.Apps.Core new AppClientsConverter(), new AppContributorsConverter(), new AppPatternsConverter(), + new ClaimsPrincipalConverter(), new InstantConverter(), new LanguageConverter(), new LanguagesConfigConverter(), @@ -50,7 +52,7 @@ namespace Squidex.Domain.Apps.Core TypeNameHandling = TypeNameHandling.Auto }; - return JsonSerializer.Create(serializerSettings); + return new NewtonsoftJsonSerializer(serializerSettings); } public static Schema MixedSchema() @@ -100,6 +102,13 @@ namespace Squidex.Domain.Apps.Core return schema; } + public static T SerializeAndDeserialize(this T value) + { + var serializer = DefaultSerializer(); + + return serializer.Deserialize(serializer.Serialize(value)); + } + public static void TestFreeze(IFreezable freezable) { var sut = new AssetsFieldProperties(); diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs index aef438ac2..1dad34b54 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs @@ -7,9 +7,7 @@ using System; using System.Linq; -using Newtonsoft.Json; using NodaTime; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -27,18 +25,16 @@ namespace Squidex.Infrastructure.EventSourcing } } - private readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings(); - private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); private readonly DefaultEventDataFormatter sut; public DefaultEventDataFormatterTests() { - serializerSettings.Converters.Add(new PropertiesBagConverter()); + var typeNameRegistry = + new TypeNameRegistry() + .Map(typeof(MyEvent), "Event") + .Map(typeof(MyOldEvent), "OldEvent"); - typeNameRegistry.Map(typeof(MyEvent), "Event"); - typeNameRegistry.Map(typeof(MyOldEvent), "OldEvent"); - - sut = new DefaultEventDataFormatter(typeNameRegistry, JsonSerializer.Create(serializerSettings)); + sut = new DefaultEventDataFormatter(typeNameRegistry, JsonHelper.DefaultSerializer(typeNameRegistry)); } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeTests.cs index 804d57f14..f9d3a75e5 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -23,7 +22,7 @@ namespace Squidex.Infrastructure.EventSourcing { var value = new Envelope(new MyEvent { Value = 1 }); - var deserialized = value.SerializeAndDeserializeAndReturn(new PropertiesBagConverter()); + var deserialized = value.SerializeAndDeserialize(); Assert.Equal(1, deserialized.To().Payload.Value); } diff --git a/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs b/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs index 6abf98d8a..76e65659e 100644 --- a/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs @@ -36,10 +36,10 @@ namespace Squidex.Infrastructure.Json "Google") }); - var result = value.SerializeAndDeserializeAndReturn(new ClaimsPrincipalConverter()); + var serialized = value.SerializeAndDeserialize(); - Assert.Equal(value.Identities.ElementAt(0).AuthenticationType, result.Identities.ElementAt(0).AuthenticationType); - Assert.Equal(value.Identities.ElementAt(1).AuthenticationType, result.Identities.ElementAt(1).AuthenticationType); + Assert.Equal(value.Identities.ElementAt(0).AuthenticationType, serialized.Identities.ElementAt(0).AuthenticationType); + Assert.Equal(value.Identities.ElementAt(1).AuthenticationType, serialized.Identities.ElementAt(1).AuthenticationType); } [Fact] @@ -47,7 +47,9 @@ namespace Squidex.Infrastructure.Json { ClaimsPrincipal value = null; - value.SerializeAndDeserialize(new ClaimsPrincipalConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Null(value); } } } diff --git a/tests/Squidex.Infrastructure.Tests/Json/InstantConverterTests.cs b/tests/Squidex.Infrastructure.Tests/Json/InstantConverterTests.cs index b207d365a..aba2d8269 100644 --- a/tests/Squidex.Infrastructure.Tests/Json/InstantConverterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Json/InstantConverterTests.cs @@ -19,7 +19,9 @@ namespace Squidex.Infrastructure.Json { var value = Instant.FromDateTimeUtc(DateTime.UtcNow.Date); - value.SerializeAndDeserialize(new InstantConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -27,7 +29,9 @@ namespace Squidex.Infrastructure.Json { Instant? value = Instant.FromDateTimeUtc(DateTime.UtcNow.Date); - value.SerializeAndDeserialize(new InstantConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -35,7 +39,9 @@ namespace Squidex.Infrastructure.Json { Instant? value = null; - value.SerializeAndDeserialize(new InstantConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } } } diff --git a/tests/Squidex.Infrastructure.Tests/Json/ConverterContractResolverTests.cs b/tests/Squidex.Infrastructure.Tests/Json/Newtonsoft/ConverterContractResolverTests.cs similarity index 94% rename from tests/Squidex.Infrastructure.Tests/Json/ConverterContractResolverTests.cs rename to tests/Squidex.Infrastructure.Tests/Json/Newtonsoft/ConverterContractResolverTests.cs index d96f8a780..4ea3d7c0f 100644 --- a/tests/Squidex.Infrastructure.Tests/Json/ConverterContractResolverTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Json/Newtonsoft/ConverterContractResolverTests.cs @@ -11,7 +11,7 @@ using NodaTime; using Squidex.Infrastructure.TestHelpers; using Xunit; -namespace Squidex.Infrastructure.Json +namespace Squidex.Infrastructure.Json.Newtonsoft { public class ConverterContractResolverTests { @@ -77,7 +77,9 @@ namespace Squidex.Infrastructure.Json { var value = Instant.FromDateTimeUtc(DateTime.UtcNow.Date); - value.SerializeAndDeserialize(new ConverterContractResolver(new InstantConverter())); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } } } diff --git a/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs new file mode 100644 index 000000000..08150c3a8 --- /dev/null +++ b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonValuesSerializationTests.cs @@ -0,0 +1,113 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.TestHelpers; +using Xunit; + +namespace Squidex.Infrastructure.Json.Objects +{ + public class JsonValuesSerializationTests + { + [Fact] + public void Should_deserialize_integer() + { + var serialized = JsonHelper.Deserialize(123); + + Assert.Equal(JsonValue.Create(123), serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_null() + { + var value = JsonValue.Null; + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_date() + { + var value = JsonValue.Create("2008-09-15T15:53:00"); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_string() + { + var value = JsonValue.Create("my-string"); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_boolean() + { + var value = JsonValue.Create(true); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_number() + { + var value = JsonValue.Create(123); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_array() + { + var value = JsonValue.Array(1, 2); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_object() + { + var value = + JsonValue.Object() + .Add("1", 1) + .Add("2", 1); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + + [Fact] + public void Should_serialize_and_deserialize_complex_object() + { + var value = + JsonValue.Object() + .Add("1", + JsonValue.Array( + JsonValue.Object().Add("1_1", 11), + JsonValue.Object().Add("1_2", 12))) + .Add("2", + JsonValue.Object().Add("2_1", 11)); + + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); + } + } +} diff --git a/tests/Squidex.Infrastructure.Tests/LanguageTests.cs b/tests/Squidex.Infrastructure.Tests/LanguageTests.cs index cddf49510..f57e30fe7 100644 --- a/tests/Squidex.Infrastructure.Tests/LanguageTests.cs +++ b/tests/Squidex.Infrastructure.Tests/LanguageTests.cs @@ -7,7 +7,6 @@ using System; using System.Linq; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -125,7 +124,9 @@ namespace Squidex.Infrastructure { Language value = null; - value.SerializeAndDeserialize(new LanguageConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -133,7 +134,9 @@ namespace Squidex.Infrastructure { var value = Language.DE; - value.SerializeAndDeserialize(new LanguageConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } } } diff --git a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs index 09a9d1548..bdf62cae4 100644 --- a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs +++ b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -91,7 +90,9 @@ namespace Squidex.Infrastructure { NamedId value = null; - value.SerializeAndDeserialize(new NamedGuidIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -99,7 +100,9 @@ namespace Squidex.Infrastructure { var value = NamedId.Of(Guid.NewGuid(), "my-name"); - value.SerializeAndDeserialize(new NamedGuidIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -107,7 +110,9 @@ namespace Squidex.Infrastructure { NamedId value = null; - value.SerializeAndDeserialize(new NamedLongIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -115,7 +120,9 @@ namespace Squidex.Infrastructure { var value = NamedId.Of(123L, "my-name"); - value.SerializeAndDeserialize(new NamedLongIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -123,7 +130,9 @@ namespace Squidex.Infrastructure { NamedId value = null; - value.SerializeAndDeserialize(new NamedStringIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -131,27 +140,27 @@ namespace Squidex.Infrastructure { var value = NamedId.Of(Guid.NewGuid().ToString(), "my-name"); - value.SerializeAndDeserialize(new NamedStringIdConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] public void Should_throw_exception_if_string_id_is_not_valid() { - JsonHelper.DoesNotDeserialize>("123", new NamedStringIdConverter()); + Assert.ThrowsAny(() => JsonHelper.Deserialize>("123")); } [Fact] public void Should_throw_exception_if_long_id_is_not_valid() { - JsonHelper.DoesNotDeserialize>("123", new NamedLongIdConverter()); - JsonHelper.DoesNotDeserialize>("invalid-long,name", new NamedLongIdConverter()); + Assert.ThrowsAny(() => JsonHelper.Deserialize>("invalid-long,name")); } [Fact] public void Should_throw_exception_if_guid_id_is_not_valid() { - JsonHelper.DoesNotDeserialize>("123", new NamedGuidIdConverter()); - JsonHelper.DoesNotDeserialize>("invalid-guid,name", new NamedGuidIdConverter()); + Assert.ThrowsAny(() => JsonHelper.Deserialize>("invalid-guid,name")); } } } diff --git a/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs b/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs index 27e06eb9d..608347c0c 100644 --- a/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs @@ -11,12 +11,18 @@ using System.Collections.Generic; using System.IO; using FakeItEasy; using Orleans.Serialization; +using Squidex.Infrastructure.TestHelpers; using Xunit; namespace Squidex.Infrastructure.Orleans { public class JsonExternalSerializerTests { + public JsonExternalSerializerTests() + { + J.DefaultSerializer = JsonHelper.DefaultSerializer(); + } + [Fact] public void Should_not_copy_null() { diff --git a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs b/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs index d6888f058..defe9c236 100644 --- a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs +++ b/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs @@ -10,7 +10,6 @@ using System.Globalization; using System.Linq; using Microsoft.CSharp.RuntimeBinder; using NodaTime; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -30,7 +29,7 @@ namespace Squidex.Infrastructure [Fact] public void Should_serialize_and_deserialize_empty_bag() { - var output = bag.SerializeAndDeserializeAndReturn(new PropertiesBagConverter()); + var output = bag.SerializeAndDeserialize(); Assert.Equal(bag.Count, output.Count); } @@ -46,7 +45,7 @@ namespace Squidex.Infrastructure bag.Set("Key4", true); bag.Set("Key5", Guid.NewGuid()); - var output = bag.SerializeAndDeserializeAndReturn(new PropertiesBagConverter()); + var output = bag.SerializeAndDeserialize(); foreach (var kvp in output.Properties.Take(4)) { diff --git a/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs b/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs index b985b7a97..09620ee0f 100644 --- a/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs +++ b/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.TestHelpers; using Xunit; @@ -111,7 +110,9 @@ namespace Squidex.Infrastructure { RefToken value = null; - value.SerializeAndDeserialize(new RefTokenConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } [Fact] @@ -119,7 +120,9 @@ namespace Squidex.Infrastructure { var value = RefToken.Parse("client:client1"); - value.SerializeAndDeserialize(new RefTokenConverter()); + var serialized = value.SerializeAndDeserialize(); + + Assert.Equal(value, serialized); } } } diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs index d78a249d8..76bb82676 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs @@ -7,64 +7,59 @@ using System; using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Xunit; +using Newtonsoft.Json.Converters; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Infrastructure.TestHelpers { public static class JsonHelper { - public static void SerializeAndDeserialize(this T value, IContractResolver contractResolver) + public static IJsonSerializer DefaultSerializer(TypeNameRegistry typeNameRegistry = null) { var serializerSettings = new JsonSerializerSettings { - ContractResolver = contractResolver, - NullValueHandling = NullValueHandling.Include - }; - - var result = JsonConvert.SerializeObject(Tuple.Create(value), serializerSettings); - var output = JsonConvert.DeserializeObject>(result, serializerSettings); + SerializationBinder = new TypeNameSerializationBinder(typeNameRegistry ?? new TypeNameRegistry()), - Assert.Equal(value, output.Item1); - } + ContractResolver = new ConverterContractResolver( + new ClaimsPrincipalConverter(), + new InstantConverter(), + new JsonValueConverter(), + new LanguageConverter(), + new NamedGuidIdConverter(), + new NamedLongIdConverter(), + new NamedStringIdConverter(), + new PropertiesBagConverter(), + new PropertiesBagConverter(), + new RefTokenConverter(), + new StringEnumConverter()), - public static void SerializeAndDeserialize(this T value, JsonConverter converter) - { - var output = SerializeAndDeserializeAndReturn(value, converter); + TypeNameHandling = TypeNameHandling.Auto + }; - Assert.Equal(value, output); + return new NewtonsoftJsonSerializer(serializerSettings); } - public static T SerializeAndDeserializeAndReturn(this T value, JsonConverter converter) + public static T SerializeAndDeserialize(this T value) { - var serializerSettings = CreateSettings(converter); + var serializer = DefaultSerializer(); - var result = JsonConvert.SerializeObject(Tuple.Create(value), serializerSettings); - var output = JsonConvert.DeserializeObject>(result, serializerSettings); - - return output.Item1; + return serializer.Deserialize>(serializer.Serialize(Tuple.Create(value))).Item1; } - public static void DoesNotDeserialize(string value, JsonConverter converter) + public static T Deserialize(string value) { - var serializerSettings = CreateSettings(converter); + var serializer = DefaultSerializer(); - Assert.ThrowsAny(() => JsonConvert.DeserializeObject>($"{{ \"Item1\": \"{value}\" }}", serializerSettings)); + return serializer.Deserialize>($"{{ \"Item1\": \"{value}\" }}").Item1; } - private static JsonSerializerSettings CreateSettings(JsonConverter converter) + public static T Deserialize(object value) { - var serializerSettings = new JsonSerializerSettings(); - - if (converter != null) - { - serializerSettings.Converters.Add(converter); - } - - serializerSettings.NullValueHandling = NullValueHandling.Include; - serializerSettings.TypeNameHandling = TypeNameHandling.Auto; + var serializer = DefaultSerializer(); - return serializerSettings; + return serializer.Deserialize>($"{{ \"Item1\": {value} }}").Item1; } } } From c8e77194b79949a350e618cb2f151e85cd6a4a8b Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 25 Nov 2018 16:09:41 +0100 Subject: [PATCH 02/15] All tests green for core. --- .../HandleRules/RuleService.cs | 2 +- .../Json/Newtonsoft/JsonValueConverter.cs | 1 - .../Json/Objects/JsonObject.cs | 2 +- .../ConvertContent/FieldConvertersTests.cs | 4 +- .../ConvertContent/ValueConvertersTests.cs | 14 ++- .../HandleRules/RuleEventFormatterTests.cs | 41 ++------- .../HandleRules/RuleServiceTests.cs | 91 +++++++++---------- .../TestData.cs | 9 +- .../DefaultEventDataFormatterTests.cs | 2 +- .../Orleans/JsonExternalSerializerTests.cs | 2 +- .../TestHelpers/JsonHelper.cs | 16 ++-- 11 files changed, 74 insertions(+), 110 deletions(-) diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index e85ca990d..8a5dbac53 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules var actionName = typeNameRegistry.GetName(actionType); var actionData = await actionHandler.CreateJobAsync(enrichedEvent, rule.Action); - var json = jsonSerializer.Serialize(actionData); + var json = jsonSerializer.Serialize(actionData.Data); var job = new RuleJob { diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index 949a3a90c..81825b5e1 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -7,7 +7,6 @@ using System; using System.Globalization; -using System.Linq; using Newtonsoft.Json; using Squidex.Infrastructure.Json.Objects; diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs index fe70c43ff..a2cacce74 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs @@ -123,7 +123,7 @@ namespace Squidex.Infrastructure.Json.Objects public override string ToString() { - return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\"={x.Value}\""))}}}"; + return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\":{x.Value.ToJsonString()}"))}}}"; } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index d5b96a58d..0c2d23704 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs @@ -13,7 +13,6 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Objects; using Xunit; @@ -22,7 +21,6 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent public class FieldConvertersTests { private readonly IAssetUrlGenerator assetUrlGenerator = A.Fake(); - private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE); private readonly RootField jsonField = Fields.Json(1, "1", Partitioning.Invariant); private readonly RootField stringLanguageField = Fields.String(1, "1", Partitioning.Language); @@ -60,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new ContentFieldData() .AddValue("iv", JsonValue.Object()); - var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(serializer))(input, jsonField); + var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestData.DefaultSerializer))(input, jsonField); var expected = new ContentFieldData() diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs index ada3f0861..09d0c4bdc 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs @@ -7,7 +7,6 @@ using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Objects; using Xunit; @@ -18,14 +17,13 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent private readonly RootField stringField = Fields.String(1, "1", Partitioning.Invariant); private readonly RootField jsonField = Fields.Json(1, "1", Partitioning.Invariant); private readonly RootField numberField = Fields.Number(1, "1", Partitioning.Invariant); - private readonly IJsonSerializer jsonSerializer = TestData.DefaultSerializer(); [Fact] public void Should_encode_json_value() { var source = JsonValue.Object(); - var result = ValueConverters.EncodeJson(jsonSerializer)(source, jsonField); + var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField); Assert.Equal(JsonValue.Create("e30="), result); } @@ -35,7 +33,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.EncodeJson(jsonSerializer)(source, jsonField); + var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField); Assert.Same(source, result); } @@ -45,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Create("NO-JSON"); - var result = ValueConverters.EncodeJson(jsonSerializer)(source, stringField); + var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField); Assert.Same(source, result); } @@ -55,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Create("e30="); - var result = ValueConverters.DecodeJson(jsonSerializer)(source, jsonField); + var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField); Assert.Equal(JsonValue.Object(), result); } @@ -65,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.DecodeJson(jsonSerializer)(source, jsonField); + var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField); Assert.Same(source, result); } @@ -75,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.EncodeJson(jsonSerializer)(source, stringField); + var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField); Assert.Same(source, result); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index bee39a8eb..b2b58a49d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -9,14 +9,12 @@ using System; using System.Collections.Generic; using System.Security.Claims; using FakeItEasy; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Squidex.Shared.Identity; using Squidex.Shared.Users; using Xunit; @@ -25,7 +23,6 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { public class RuleEventFormatterTests { - private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly IUser user = A.Fake(); private readonly IRuleUrlGenerator urlGenerator = A.Fake(); private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); @@ -41,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => user.Claims) .Returns(new List { new Claim(SquidexClaimTypes.DisplayName, "me") }); - sut = new RuleEventFormatter(serializer, urlGenerator); + sut = new RuleEventFormatter(TestData.DefaultSerializer, urlGenerator); } [Fact] @@ -198,7 +195,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", new JArray())) + .AddValue("iv", JsonValue.Array())) }; var result = sut.Format("$CONTENT_DATA.city.de.10", @event); @@ -215,8 +212,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", new JObject( - new JProperty("name", "Berlin")))) + .AddValue("iv", JsonValue.Object().Add("name", "Berlin"))) }; var result = sut.Format("$CONTENT_DATA.city.de.Name", @event); @@ -267,24 +263,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", JValue.CreateNull())) - }; - - var result = sut.Format("$CONTENT_DATA.city.iv", @event); - - Assert.Equal("UNDEFINED", result); - } - - [Fact] - public void Should_return_undefined_when_undefined() - { - var @event = new EnrichedContentEvent - { - Data = - new NamedContentData() - .AddField("city", - new ContentFieldData() - .AddValue("iv", JValue.CreateUndefined())) + .AddValue("iv", JsonValue.Null)) }; var result = sut.Format("$CONTENT_DATA.city.iv", @event); @@ -301,13 +280,12 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", new JObject( - new JProperty("name", "Berlin")))) + .AddValue("iv", JsonValue.Object().Add("name", "Berlin"))) }; var result = sut.Format("$CONTENT_DATA.city.iv", @event); - Assert.Equal(JObject.FromObject(new { name = "Berlin" }).ToString(Formatting.Indented), result); + Assert.Equal("{\"name\":\"Berlin\"}", result); } [Fact] @@ -319,7 +297,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", new JArray( + .AddValue("iv", JsonValue.Array( "Berlin"))) }; @@ -337,8 +315,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules new NamedContentData() .AddField("city", new ContentFieldData() - .AddValue("iv", new JObject( - new JProperty("name", "Berlin")))) + .AddValue("iv", JsonValue.Object().Add("name", "Berlin"))) }; var result = sut.Format("$CONTENT_DATA.city.iv.name", @event); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index 5b2d5b77b..35226e184 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -17,7 +17,6 @@ using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Json; using Xunit; #pragma warning disable xUnit2009 // Do not use boolean check to check for string equality @@ -28,9 +27,12 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { private readonly IRuleTriggerHandler ruleTriggerHandler = A.Fake(); private readonly IRuleActionHandler ruleActionHandler = A.Fake(); - private readonly IJsonSerializer serializer = TestData.DefaultSerializer(); private readonly IEventEnricher eventEnricher = A.Fake(); private readonly IClock clock = A.Fake(); + private readonly string actionData = "{\"value\":10}"; + private readonly string actionDump = "MyDump"; + private readonly string actionName = "ValidAction"; + private readonly string actionDescription = "MyDescription"; private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); private readonly RuleService sut; @@ -38,12 +40,18 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { } + public sealed class InvalidAction : RuleAction + { + } + public sealed class ValidAction : RuleAction { + public int Value { get; set; } } - public sealed class InvalidAction : RuleAction + public sealed class ValidData { + public int Value { get; set; } } public sealed class InvalidTrigger : RuleTrigger @@ -57,7 +65,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules public RuleServiceTests() { typeNameRegistry.Map(typeof(ContentCreated)); - typeNameRegistry.Map(typeof(ValidAction), "ValidAction"); + typeNameRegistry.Map(typeof(ValidAction), actionName); A.CallTo(() => eventEnricher.EnrichAsync(A>.Ignored)) .Returns(new EnrichedContentEvent()); @@ -65,16 +73,30 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => ruleActionHandler.ActionType) .Returns(typeof(ValidAction)); + A.CallTo(() => ruleActionHandler.DataType) + .Returns(typeof(ValidData)); + A.CallTo(() => ruleTriggerHandler.TriggerType) .Returns(typeof(ContentChangedTrigger)); - sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, serializer, clock, typeNameRegistry); + sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestData.DefaultSerializer, clock, typeNameRegistry); + } + + [Fact] + public async Task Should_not_create_if_rule_disabled() + { + var ruleConfig = ValidRule().Disable(); + var ruleEnvelope = Envelope.Create(new ContentCreated()); + + var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); + + Assert.Null(job); } [Fact] public async Task Should_not_create_job_for_invalid_event() { - var ruleConfig = new Rule(new ContentChangedTrigger(), new ValidAction()); + var ruleConfig = ValidRule(); var ruleEnvelope = Envelope.Create(new InvalidEvent()); var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); @@ -107,7 +129,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_not_create_if_not_triggered() { - var ruleConfig = new Rule(new ContentChangedTrigger(), new ValidAction()); + var ruleConfig = ValidRule(); var ruleEnvelope = Envelope.Create(new ContentCreated()); A.CallTo(() => ruleTriggerHandler.Triggers(A>.Ignored, ruleConfig.Trigger)) @@ -125,14 +147,11 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var now = SystemClock.Instance.GetCurrentInstant(); - var ruleConfig = new Rule(new ContentChangedTrigger(), new ValidAction()); + var ruleConfig = ValidRule(); var ruleEnvelope = Envelope.Create(@event); ruleEnvelope.SetTimestamp(now.Minus(Duration.FromDays(3))); - var actionData = "{}"; - var actionDescription = "MyDescription"; - A.CallTo(() => clock.GetCurrentInstant()) .Returns(now); @@ -154,15 +173,11 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules var now = SystemClock.Instance.GetCurrentInstant(); - var ruleConfig = new Rule(new ContentChangedTrigger(), new ValidAction()); + var ruleConfig = ValidRule(); var ruleEnvelope = Envelope.Create(@event); ruleEnvelope.SetTimestamp(now); - var actionName = "ValidAction"; - var actionData = "{}"; - var actionDescription = "MyDescription"; - A.CallTo(() => clock.GetCurrentInstant()) .Returns(now); @@ -170,7 +185,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules .Returns(true); A.CallTo(() => ruleActionHandler.CreateJobAsync(A.Ignored, ruleConfig.Action)) - .Returns((actionDescription, actionData)); + .Returns((actionDescription, new ValidData { Value = 10 })); var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); @@ -189,14 +204,10 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_succeeded_job_with_full_dump_when_handler_returns_no_exception() { - var ruleJob = "{}"; - - var actionDump = "MyDump"; - - A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(A.That.Matches(x => x.Value == 10))) .Returns((actionDump, null)); - var result = await sut.InvokeAsync("ValidAction", ruleJob); + var result = await sut.InvokeAsync(actionName, actionData); Assert.Equal(RuleResult.Success, result.Result); @@ -207,14 +218,10 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_failed_job_with_full_dump_when_handler_returns_exception() { - var ruleJob = "{}"; - - var actionDump = "MyDump"; - - A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(A.That.Matches(x => x.Value == 10))) .Returns((actionDump, new InvalidOperationException())); - var result = await sut.InvokeAsync("ValidAction", ruleJob); + var result = await sut.InvokeAsync(actionName, actionData); Assert.Equal(RuleResult.Failed, result.Result); @@ -225,47 +232,35 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_return_timedout_job_with_full_dump_when_exception_from_handler_indicates_timeout() { - var ruleJob = "{}"; - - var actionDump = "MyDump"; - - A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(A.That.Matches(x => x.Value == 10))) .Returns((actionDump, new TimeoutException())); - var result = await sut.InvokeAsync("ValidAction", ruleJob); + var result = await sut.InvokeAsync(actionName, actionData); Assert.Equal(RuleResult.Timeout, result.Result); Assert.True(result.Elapsed >= TimeSpan.Zero); Assert.True(result.Dump.StartsWith(actionDump, StringComparison.OrdinalIgnoreCase)); + Assert.True(result.Dump.IndexOf("Action timed out.", StringComparison.OrdinalIgnoreCase) >= 0); } [Fact] public async Task Should_create_exception_details_when_job_to_execute_failed() { - var ruleJob = "{}"; var ruleError = new InvalidOperationException(); - A.CallTo(() => ruleActionHandler.ExecuteJobAsync(ruleJob)) + A.CallTo(() => ruleActionHandler.ExecuteJobAsync(A.That.Matches(x => x.Value == 10))) .Throws(ruleError); - var result = await sut.InvokeAsync("ValidAction", ruleJob); + var result = await sut.InvokeAsync(actionName, actionData); Assert.Equal((ruleError.ToString(), RuleResult.Failed, TimeSpan.Zero), result); } - [Fact] - public async Task Should_not_create_if_rule_disabled() + private static Rule ValidRule() { - var ruleConfig = new Rule(new ContentChangedTrigger(), new ValidAction()); - var ruleEnvelope = Envelope.Create(new ContentCreated()); - - ruleConfig = ruleConfig.Disable(); - - var job = await sut.CreateJobAsync(ruleConfig, ruleEnvelope); - - Assert.Null(job); + return new Rule(new ContentChangedTrigger(), new ValidAction()); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs index 530bc3a74..ca33bbd45 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs @@ -24,7 +24,9 @@ namespace Squidex.Domain.Apps.Core { public static class TestData { - public static IJsonSerializer DefaultSerializer() + public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); + + private static IJsonSerializer CreateSerializer() { var typeNameRegistry = new TypeNameRegistry(); @@ -38,6 +40,7 @@ namespace Squidex.Domain.Apps.Core new AppPatternsConverter(), new ClaimsPrincipalConverter(), new InstantConverter(), + new JsonValueConverter(), new LanguageConverter(), new LanguagesConfigConverter(), new NamedGuidIdConverter(), @@ -104,9 +107,7 @@ namespace Squidex.Domain.Apps.Core public static T SerializeAndDeserialize(this T value) { - var serializer = DefaultSerializer(); - - return serializer.Deserialize(serializer.Serialize(value)); + return DefaultSerializer.Deserialize(DefaultSerializer.Serialize(value)); } public static void TestFreeze(IFreezable freezable) diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs index 1dad34b54..212352d04 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs @@ -34,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing .Map(typeof(MyEvent), "Event") .Map(typeof(MyOldEvent), "OldEvent"); - sut = new DefaultEventDataFormatter(typeNameRegistry, JsonHelper.DefaultSerializer(typeNameRegistry)); + sut = new DefaultEventDataFormatter(typeNameRegistry, JsonHelper.CreateSerializer(typeNameRegistry)); } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs b/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs index 608347c0c..11deb02cc 100644 --- a/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Orleans/JsonExternalSerializerTests.cs @@ -20,7 +20,7 @@ namespace Squidex.Infrastructure.Orleans { public JsonExternalSerializerTests() { - J.DefaultSerializer = JsonHelper.DefaultSerializer(); + J.DefaultSerializer = JsonHelper.DefaultSerializer; } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs index 76bb82676..9d913bdda 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs @@ -16,7 +16,9 @@ namespace Squidex.Infrastructure.TestHelpers { public static class JsonHelper { - public static IJsonSerializer DefaultSerializer(TypeNameRegistry typeNameRegistry = null) + public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); + + public static IJsonSerializer CreateSerializer(TypeNameRegistry typeNameRegistry = null) { var serializerSettings = new JsonSerializerSettings { @@ -43,23 +45,17 @@ namespace Squidex.Infrastructure.TestHelpers public static T SerializeAndDeserialize(this T value) { - var serializer = DefaultSerializer(); - - return serializer.Deserialize>(serializer.Serialize(Tuple.Create(value))).Item1; + return DefaultSerializer.Deserialize>(DefaultSerializer.Serialize(Tuple.Create(value))).Item1; } public static T Deserialize(string value) { - var serializer = DefaultSerializer(); - - return serializer.Deserialize>($"{{ \"Item1\": \"{value}\" }}").Item1; + return DefaultSerializer.Deserialize>($"{{ \"Item1\": \"{value}\" }}").Item1; } public static T Deserialize(object value) { - var serializer = DefaultSerializer(); - - return serializer.Deserialize>($"{{ \"Item1\": {value} }}").Item1; + return DefaultSerializer.Deserialize>($"{{ \"Item1\": {value} }}").Item1; } } } From 289f2747cfe006ffa7d46c6416a4aeab98d46f61 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 25 Nov 2018 19:49:13 +0100 Subject: [PATCH 03/15] All tests fixed. --- .../Actions/Algolia/AlgoliaActionHandler.cs | 4 +- .../Discourse/DiscourseActionHandler.cs | 19 ++- .../ElasticSearchActionHandler.cs | 4 +- .../Prerender/PrerenderActionHandler.cs | 9 +- .../Actions/Slack/SlackActionHandler.cs | 8 +- .../EnrichedEvents/EnrichedEvent.cs | 3 + .../HandleRules/RuleActionHandler.cs | 2 +- ...Squidex.Domain.Apps.Core.Operations.csproj | 1 - .../Contents/Extensions.cs | 13 +- .../Contents/MongoContentCollection.cs | 11 +- .../Contents/MongoContentDraftCollection.cs | 9 +- .../Contents/MongoContentEntity.cs | 7 +- .../MongoContentPublishedCollection.cs | 7 +- .../Contents/MongoContentRepository.cs | 11 +- .../MongoContentRepository_SnapshotStore.cs | 4 +- .../Apps/AppUISettingsGrain.cs | 18 +- .../Apps/BackupApps.cs | 19 ++- .../Apps/IAppUISettingsGrain.cs | 8 +- .../Apps/State/AppState.cs | 18 +- .../Assets/BackupAssets.cs | 7 +- .../Assets/State/AssetState.cs | 24 +-- .../Backup/BackupGrain.cs | 7 +- .../Backup/BackupReader.cs | 51 ++---- .../Backup/BackupWriter.cs | 32 ++-- .../Backup/GuidMapper.cs | 89 +--------- .../Backup/Helpers/Downloader.cs | 5 +- .../Backup/RestoreGrain.cs | 17 +- .../Backup/State/BackupState.cs | 4 +- .../Backup/State/BackupStateJob.cs | 14 +- .../Backup/State/RestoreState.cs | 4 +- .../Backup/State/RestoreStateJob.cs | 21 +-- .../GraphQL/GraphQLExecutionContext.cs | 13 +- .../Contents/GraphQL/Types/AllTypes.cs | 2 +- .../Contents/GraphQL/Types/AssetGraphType.cs | 4 +- .../GraphQL/Types/ContentDataGraphType.cs | 4 +- .../GraphQL/Types/ContentGraphType.cs | 4 +- .../Contents/GraphQL/Types/NestedGraphType.cs | 6 +- .../GraphQL/Types/QueryGraphTypeVisitor.cs | 4 +- .../GraphQL/Types/Utils/InstantGraphType.cs | 41 +++++ .../GraphQL/Types/Utils/InstantValue.cs | 25 +++ .../GraphQL/Types/Utils/JsonConverter.cs | 6 +- .../Contents/GraphQL/Types/Utils/JsonValue.cs | 8 +- .../Contents/State/ContentState.cs | 18 +- .../DomainObjectState.cs | 14 +- .../Rules/State/RuleState.cs | 8 +- .../Schemas/State/SchemaState.cs | 28 +-- .../SquidexHeaderExtensions.cs | 3 +- .../RedisPubSub.cs | 9 +- .../RedisSubscription.cs | 10 +- .../DefaultEventDataFormatter.cs | 6 +- .../EventSourcing/EnvelopeExtensions.cs | 11 +- .../EventSourcing/IEventDataFormatter.cs | 2 +- .../Json/IJsonSerializer.cs | 6 +- .../Json/Newtonsoft/JsonValueConverter.cs | 57 ++++--- .../Newtonsoft/NewtonsoftJsonSerializer.cs | 68 ++++++-- .../Json/Newtonsoft/PropertiesBagConverter.cs | 4 +- .../Json/Objects/JsonValue.cs | 13 ++ src/Squidex.Infrastructure/Language.cs | 2 +- src/Squidex.Infrastructure/Orleans/J{T}.cs | 2 +- src/Squidex.Infrastructure/PropertyValue.cs | 92 +++++----- .../Api/Config/Swagger/SwaggerExtensions.cs | 2 +- .../Api/Config/Swagger/SwaggerServices.cs | 2 +- .../Controllers/Apps/Models/AppCreatedDto.cs | 1 - .../Api/Controllers/Apps/Models/AppDto.cs | 1 - .../Apps/Models/AssignContributorDto.cs | 1 - .../Api/Controllers/Apps/Models/ClientDto.cs | 1 - .../Controllers/Apps/Models/ContributorDto.cs | 1 - .../Controllers/UI/Models/UpdateSettingDto.cs | 4 +- .../Areas/Api/Controllers/UI/UIController.cs | 7 +- .../Config/IdentityServerExtensions.cs | 8 +- .../Config/Domain/EventPublishersServices.cs | 4 +- .../Config/Domain/SerializationServices.cs | 5 +- src/Squidex/Config/Domain/StoreServices.cs | 3 +- .../Config/Domain/SubscriptionServices.cs | 2 +- .../Model/Rules/RuleTests.cs | 2 +- .../Model/Schemas/SchemaFieldTests.cs | 2 +- .../Model/Schemas/SchemaTests.cs | 2 +- .../ConvertContent/FieldConvertersTests.cs | 2 +- .../ConvertContent/ValueConvertersTests.cs | 12 +- .../Operations/GenerateEdmSchema/EdmTests.cs | 2 +- .../GenerateJsonSchema/JsonSchemaTests.cs | 2 +- .../HandleRules/RuleEventFormatterTests.cs | 2 +- .../HandleRules/RuleServiceTests.cs | 2 +- .../{TestData.cs => TestUtils.cs} | 9 +- .../Apps/AppUISettingsGrainTests.cs | 34 ++-- .../Backup/BackupReaderWriterTests.cs | 123 +++++++++---- .../Backup/GuidMapperTests.cs | 161 ------------------ .../Contents/ContentGrainTests.cs | 4 +- .../Contents/GraphQL/GraphQLQueriesTests.cs | 35 ++-- .../Contents/GraphQL/GraphQLTestBase.cs | 42 ++--- .../Rules/RuleDequeuerTests.cs | 5 +- .../Squidex.Domain.Apps.Entities.Tests.csproj | 1 + .../EventSourcing/EnvelopeExtensionsTests.cs | 12 +- .../Grains/EventConsumerGrainTests.cs | 6 +- .../PropertiesBagTests.cs | 44 +++-- .../States/PersistenceEventSourcingTests.cs | 4 +- tools/Migrate_01/Rebuilder.cs | 8 +- 97 files changed, 718 insertions(+), 760 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs rename tests/Squidex.Domain.Apps.Core.Tests/{TestData.cs => TestUtils.cs} (94%) delete mode 100644 tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs diff --git a/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs b/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs index 80d7e23d1..46c31f642 100644 --- a/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs @@ -56,7 +56,9 @@ namespace Squidex.Extensions.Actions.Algolia { ruleDescription = $"Add entry to Algolia index: {action.IndexName}"; - ruleJob.Content = ToPayload(contentEvent); + var json = ToJson(contentEvent); + + ruleJob.Content = JObject.Parse(json); ruleJob.Content["objectID"] = contentId; } diff --git a/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs b/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs index 22e9131a1..b0811ba17 100644 --- a/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs @@ -6,10 +6,10 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; @@ -32,25 +32,28 @@ namespace Squidex.Extensions.Actions.Discourse { var url = $"{action.Url.ToString().TrimEnd('/')}/posts.json?api_key={action.ApiKey}&api_username={action.ApiUsername}"; - var json = - new JObject( - new JProperty("raw", Format(action.Text, @event)), - new JProperty("title", Format(action.Title, @event))); + var json = new Dictionary + { + ["raw"] = Format(action.Text, @event), + ["title"] = Format(action.Title, @event) + }; if (action.Topic.HasValue) { - json.Add(new JProperty("topic_id", action.Topic.Value)); + json.Add("topic_id", action.Topic.Value); } if (action.Category.HasValue) { - json.Add(new JProperty("category", action.Category.Value)); + json.Add("category", action.Category.Value); } + var requestBody = ToJson(json); + var ruleJob = new DiscourseJob { RequestUrl = url, - RequestBody = json.ToString() + RequestBody = requestBody }; var description = diff --git a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs index 78655e4fd..3688f7f75 100644 --- a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs @@ -60,7 +60,9 @@ namespace Squidex.Extensions.Actions.ElasticSearch { ruleDescription = $"Upsert to index: {action.IndexName}"; - ruleJob.Content = ToPayload(contentEvent); + var json = ToJson(contentEvent); + + ruleJob.Content = JObject.Parse(json); ruleJob.Content["objectID"] = contentId; } diff --git a/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs b/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs index e8c722490..40d78e4bf 100644 --- a/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs @@ -9,7 +9,6 @@ using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; @@ -29,12 +28,10 @@ namespace Squidex.Extensions.Actions.Prerender { var url = Format(action.Url, @event); - var request = - new JObject( - new JProperty("prerenderToken", action.Token), - new JProperty("url", url)); + var request = new { prerenderToken = action.Token, url }; + var requestBody = ToJson(request); - return ($"Recache {url}", new PrerenderJob { RequestBody = request.ToString() }); + return ($"Recache {url}", new PrerenderJob { RequestBody = requestBody }); } protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(PrerenderJob job) diff --git a/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs b/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs index aeb40f32c..3d47eaa71 100644 --- a/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs @@ -9,8 +9,6 @@ using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure; @@ -33,14 +31,12 @@ namespace Squidex.Extensions.Actions.Slack protected override (string Description, SlackJob Data) CreateJob(EnrichedEvent @event, SlackAction action) { - var body = - new JObject( - new JProperty("text", Format(action.Text, @event))); + var body = new { text = Format(action.Text, @event) }; var ruleJob = new SlackJob { RequestUrl = action.WebhookUrl.ToString(), - RequestBody = body.ToString(Formatting.Indented) + RequestBody = ToJson(body) }; return (Description, ruleJob); diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs index 6c02810a4..0ca18fdf1 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Runtime.Serialization; using NodaTime; using Squidex.Infrastructure; using Squidex.Shared.Users; @@ -24,8 +25,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents public long Version { get; set; } + [IgnoreDataMember] public abstract Guid AggregateId { get; } + [IgnoreDataMember] public IUser User { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs index e9632578c..7f6c4ed75 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs @@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules this.formatter = formatter; } - protected virtual string ToPayloadJson(T @event) + protected virtual string ToJson(T @event) { return formatter.ToPayload(@event); } diff --git a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 383bb37ae..c6e1c7f42 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs index c3fc170a7..c677386a6 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs @@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Core.ExtractReferenceIds; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Json; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { @@ -22,24 +23,24 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents return data.GetReferencedIds(schema).ToList(); } - public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List deletedIds) + public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List deletedIds, IJsonSerializer serializer) { return result.ConvertId2Name(schema, FieldConverters.ForValues( - ValueConverters.DecodeJson(), + ValueConverters.DecodeJson(serializer), ValueReferencesConverter.CleanReferences(deletedIds)), FieldConverters.ForNestedId2Name( - ValueConverters.DecodeJson(), + ValueConverters.DecodeJson(serializer), ValueReferencesConverter.CleanReferences(deletedIds))); } - public static IdContentData ToMongoModel(this NamedContentData result, Schema schema) + public static IdContentData ToMongoModel(this NamedContentData result, Schema schema, IJsonSerializer serializer) { return result.ConvertName2Id(schema, FieldConverters.ForValues( - ValueConverters.EncodeJson()), + ValueConverters.EncodeJson(serializer)), FieldConverters.ForNestedName2Id( - ValueConverters.EncodeJson())); + ValueConverters.EncodeJson(serializer))); } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs index c9eec5756..799ea5aef 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs @@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Queries; @@ -26,10 +27,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { private readonly string collectionName; - public MongoContentCollection(IMongoDatabase database, string collectionName) + protected IJsonSerializer Serializer { get; } + + public MongoContentCollection(IMongoDatabase database, IJsonSerializer serializer, string collectionName) : base(database) { this.collectionName = collectionName; + + Serializer = serializer; } protected override async Task SetupCollectionAsync(IMongoCollection collection, CancellationToken ct = default(CancellationToken)) @@ -64,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents foreach (var entity in contentItems.Result) { - entity.ParseData(schema.SchemaDef); + entity.ParseData(schema.SchemaDef, Serializer); } return ResultList.Create(contentCount.Result, contentItems.Result); @@ -96,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents foreach (var entity in contentItems.Result) { - entity.ParseData(schema.SchemaDef); + entity.ParseData(schema.SchemaDef, Serializer); } return ResultList.Create(contentCount.Result, contentItems.Result); diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs index 2c80690f7..72af5e013 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs @@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.State; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; @@ -26,8 +27,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { internal sealed class MongoContentDraftCollection : MongoContentCollection { - public MongoContentDraftCollection(IMongoDatabase database) - : base(database, "State_Content_Draft") + public MongoContentDraftCollection(IMongoDatabase database, IJsonSerializer serializer) + : base(database, serializer, "State_Content_Draft") { } @@ -88,7 +89,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id && x.IsDeleted != true).Not(x => x.DataText) .FirstOrDefaultAsync(); - contentEntity?.ParseData(schema.SchemaDef); + contentEntity?.ParseData(schema.SchemaDef, Serializer); return contentEntity; } @@ -103,7 +104,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { var schema = await getSchema(contentEntity.IndexedAppId, contentEntity.IndexedSchemaId); - contentEntity.ParseData(schema.SchemaDef); + contentEntity.ParseData(schema.SchemaDef, Serializer); return (SimpleMapper.Map(contentEntity, new ContentState()), contentEntity.Version); } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs index b758c7919..e19296d9a 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs @@ -14,6 +14,7 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents @@ -124,13 +125,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents get { return dataDraft; } } - public void ParseData(Schema schema) + public void ParseData(Schema schema, IJsonSerializer serializer) { - data = DataByIds.FromMongoModel(schema, ReferencedIdsDeleted); + data = DataByIds.FromMongoModel(schema, ReferencedIdsDeleted, serializer); if (DataDraftByIds != null) { - dataDraft = DataDraftByIds.FromMongoModel(schema, ReferencedIdsDeleted); + dataDraft = DataDraftByIds.FromMongoModel(schema, ReferencedIdsDeleted, serializer); } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs index 97ca68a3d..139869959 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs @@ -13,14 +13,15 @@ using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.MongoDb; namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { internal sealed class MongoContentPublishedCollection : MongoContentCollection { - public MongoContentPublishedCollection(IMongoDatabase database) - : base(database, "State_Content_Published") + public MongoContentPublishedCollection(IMongoDatabase database, IJsonSerializer serializer) + : base(database, serializer, "State_Content_Published") { } @@ -42,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id).Not(x => x.DataText) .FirstOrDefaultAsync(); - contentEntity?.ParseData(schema.SchemaDef); + contentEntity?.ParseData(schema.SchemaDef, Serializer); return contentEntity; } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs index 71c81e3aa..628131df7 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs @@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Queries; @@ -26,17 +27,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents { private readonly IMongoDatabase database; private readonly IAppProvider appProvider; + private readonly IJsonSerializer serializer; private readonly MongoContentDraftCollection contentsDraft; private readonly MongoContentPublishedCollection contentsPublished; - public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider) + public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer) { Guard.NotNull(appProvider, nameof(appProvider)); + Guard.NotNull(serializer, nameof(serializer)); this.appProvider = appProvider; - contentsDraft = new MongoContentDraftCollection(database); - contentsPublished = new MongoContentPublishedCollection(database); + this.serializer = serializer; + + contentsDraft = new MongoContentDraftCollection(database, serializer); + contentsPublished = new MongoContentPublishedCollection(database, serializer); this.database = database; } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs index cd6700635..32f38e982 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs @@ -38,12 +38,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id); - var idData = value.Data.ToMongoModel(schema.SchemaDef); + var idData = value.Data.ToMongoModel(schema.SchemaDef, serializer); var idDraftData = idData; if (!ReferenceEquals(value.Data, value.DataDraft)) { - idDraftData = value.DataDraft?.ToMongoModel(schema.SchemaDef); + idDraftData = value.DataDraft?.ToMongoModel(schema.SchemaDef, serializer); } var content = SimpleMapper.Map(value, new MongoContentEntity diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs index ea1e17408..e19911379 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs @@ -8,8 +8,8 @@ using System; using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; @@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Apps [CollectionName("UISettings")] public sealed class State { - public JObject Settings { get; set; } = new JObject(); + public JsonObject Settings { get; set; } = JsonValue.Object(); } public AppUISettingsGrain(IStore store) @@ -41,19 +41,19 @@ namespace Squidex.Domain.Apps.Entities.Apps return persistence.ReadAsync(); } - public Task> GetAsync() + public Task> GetAsync() { return Task.FromResult(state.Settings.AsJ()); } - public Task SetAsync(J settings) + public Task SetAsync(J settings) { state.Settings = settings; return persistence.WriteSnapshotAsync(state); } - public Task SetAsync(string path, J value) + public Task SetAsync(string path, J value) { var container = GetContainer(path, out var key); @@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Apps throw new InvalidOperationException("Path does not lead to an object."); } - container[key] = value; + container[key] = value.Value; return persistence.WriteSnapshotAsync(state); } @@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Entities.Apps return persistence.WriteSnapshotAsync(state); } - private JObject GetContainer(string path, out string key) + private JsonObject GetContainer(string path, out string key) { Guard.NotNullOrEmpty(path, nameof(path)); @@ -95,12 +95,12 @@ namespace Squidex.Domain.Apps.Entities.Apps { if (!current.TryGetValue(segment, out var temp)) { - temp = new JObject(); + temp = JsonValue.Object(); current[segment] = temp; } - if (temp is JObject next) + if (temp is JsonObject next) { current = next; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs b/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs index 1cc342591..6d7b8e3d6 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Orleans; using Squidex.Domain.Apps.Entities.Apps.Indexes; using Squidex.Domain.Apps.Entities.Backup; @@ -16,6 +15,8 @@ using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; using Squidex.Shared.Users; @@ -27,6 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Apps private const string SettingsFile = "Settings.json"; private readonly IGrainFactory grainFactory; private readonly IUserResolver userResolver; + private readonly IJsonSerializer serializer; private readonly IAppsByNameIndex appsByNameIndex; private readonly HashSet contributors = new HashSet(); private Dictionary usersWithEmail = new Dictionary(); @@ -36,13 +38,14 @@ namespace Squidex.Domain.Apps.Entities.Apps public override string Name { get; } = "Apps"; - public BackupApps(IGrainFactory grainFactory, IUserResolver userResolver) + public BackupApps(IGrainFactory grainFactory, IUserResolver userResolver, IJsonSerializer serializer) { Guard.NotNull(grainFactory, nameof(grainFactory)); + Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(userResolver, nameof(userResolver)); this.grainFactory = grainFactory; - + this.serializer = serializer; this.userResolver = userResolver; appsByNameIndex = grainFactory.GetGrain(SingleGrain.Id); @@ -162,14 +165,14 @@ namespace Squidex.Domain.Apps.Entities.Apps private async Task ReadUsersAsync(BackupReader reader) { - var json = await reader.ReadJsonAttachmentAsync(UsersFile); + var json = await reader.ReadJsonAttachmentAsync>(UsersFile); - usersWithEmail = json.ToObject>(); + usersWithEmail = json; } private async Task WriteUsersAsync(BackupWriter writer) { - var json = JObject.FromObject(usersWithEmail); + var json = usersWithEmail; await writer.WriteJsonAsync(UsersFile, json); } @@ -183,9 +186,9 @@ namespace Squidex.Domain.Apps.Entities.Apps private async Task ReadSettingsAsync(BackupReader reader, Guid appId) { - var json = await reader.ReadJsonAttachmentAsync(SettingsFile); + var json = await reader.ReadJsonAttachmentAsync(SettingsFile); - await grainFactory.GetGrain(appId).SetAsync((JObject)json); + await grainFactory.GetGrain(appId).SetAsync(json); } public override async Task CompleteRestoreAsync(Guid appId, BackupReader reader) diff --git a/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs index 38fde5c74..69583ff25 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs @@ -6,19 +6,19 @@ // ========================================================================== using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Orleans; +using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Apps { public interface IAppUISettingsGrain : IGrainWithGuidKey { - Task> GetAsync(); + Task> GetAsync(); - Task SetAsync(string path, J value); + Task SetAsync(string path, J value); - Task SetAsync(J settings); + Task SetAsync(J settings); Task RemoveAsync(string path); } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs index 345a5b32c..36d8ce960 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; +using System.Runtime.Serialization; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Apps; @@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Apps.State [CollectionName("Apps")] public class AppState : DomainObjectState, IAppEntity { - [JsonProperty] + [DataMember] public string Name { get; set; } - [JsonProperty] + [DataMember] public Roles Roles { get; set; } = Roles.Empty; - [JsonProperty] + [DataMember] public AppPlan Plan { get; set; } - [JsonProperty] + [DataMember] public AppClients Clients { get; set; } = AppClients.Empty; - [JsonProperty] + [DataMember] public AppPatterns Patterns { get; set; } = AppPatterns.Empty; - [JsonProperty] + [DataMember] public AppContributors Contributors { get; set; } = AppContributors.Empty; - [JsonProperty] + [DataMember] public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English; - [JsonProperty] + [DataMember] public bool IsArchived { get; set; } protected void On(AppCreated @event) diff --git a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs index d60e3070c..ebf79a790 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Tags; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.Assets.State; @@ -89,16 +88,16 @@ namespace Squidex.Domain.Apps.Entities.Assets private async Task RestoreTagsAsync(Guid appId, BackupReader reader) { - var tags = await reader.ReadJsonAttachmentAsync(TagsFile); + var tags = await reader.ReadJsonAttachmentAsync(TagsFile); - await tagService.RebuildTagsAsync(appId, TagGroups.Assets, tags.ToObject()); + await tagService.RebuildTagsAsync(appId, TagGroups.Assets, tags); } private async Task BackupTagsAsync(Guid appId, BackupWriter writer) { var tags = await tagService.GetExportableTagsAsync(appId, TagGroups.Assets); - await writer.WriteJsonAsync(TagsFile, JObject.FromObject(tags)); + await writer.WriteJsonAsync(TagsFile, tags); } private Task WriteAssetAsync(Guid assetId, long fileVersion, BackupWriter writer) diff --git a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs index 44693da64..e9ebd33ec 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs @@ -7,7 +7,7 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Runtime.Serialization; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; @@ -20,37 +20,37 @@ namespace Squidex.Domain.Apps.Entities.Assets.State { public class AssetState : DomainObjectState, IAssetEntity { - [JsonProperty] + [DataMember] public NamedId AppId { get; set; } - [JsonProperty] + [DataMember] public string FileName { get; set; } - [JsonProperty] + [DataMember] public string MimeType { get; set; } - [JsonProperty] + [DataMember] public long FileVersion { get; set; } - [JsonProperty] + [DataMember] public long FileSize { get; set; } - [JsonProperty] + [DataMember] public long TotalSize { get; set; } - [JsonProperty] + [DataMember] public bool IsImage { get; set; } - [JsonProperty] + [DataMember] public int? PixelWidth { get; set; } - [JsonProperty] + [DataMember] public int? PixelHeight { get; set; } - [JsonProperty] + [DataMember] public bool IsDeleted { get; set; } - [JsonProperty] + [DataMember] public HashSet Tags { get; set; } Guid IAssetInfo.AssetId diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs index 21067ce66..a8a660bc2 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs @@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; @@ -34,6 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private readonly IBackupArchiveLocation backupArchiveLocation; private readonly IClock clock; private readonly IEnumerable handlers; + private readonly IJsonSerializer serializer; private readonly IEventDataFormatter eventDataFormatter; private readonly IEventStore eventStore; private readonly ISemanticLog log; @@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup IEventStore eventStore, IEventDataFormatter eventDataFormatter, IEnumerable handlers, + IJsonSerializer serializer, ISemanticLog log, IStore store) { @@ -60,6 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Backup Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(handlers, nameof(handlers)); + Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(store, nameof(store)); Guard.NotNull(log, nameof(log)); @@ -69,6 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Backup this.eventStore = eventStore; this.eventDataFormatter = eventDataFormatter; this.handlers = handlers; + this.serializer = serializer; this.store = store; this.log = log; } @@ -139,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) { - using (var writer = new BackupWriter(stream, true)) + using (var writer = new BackupWriter(serializer, stream, true)) { await eventStore.QueryAsync(async storedEvent => { diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs index 32b8faf10..b6cce2231 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs @@ -9,20 +9,19 @@ using System; using System.IO; using System.IO.Compression; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Backup.Helpers; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Backup { public sealed class BackupReader : DisposableObjectBase { - private static readonly JsonSerializer Serializer = new JsonSerializer(); private readonly GuidMapper guidMapper = new GuidMapper(); private readonly ZipArchive archive; + private readonly IJsonSerializer serializer; private int readEvents; private int readAttachments; @@ -36,8 +35,12 @@ namespace Squidex.Domain.Apps.Entities.Backup get { return readAttachments; } } - public BackupReader(Stream stream) + public BackupReader(IJsonSerializer serializer, Stream stream) { + Guard.NotNull(serializer, nameof(serializer)); + + this.serializer = serializer; + archive = new ZipArchive(stream, ZipArchiveMode.Read, false); } @@ -54,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Backup return guidMapper.OldGuid(newId); } - public async Task ReadJsonAttachmentAsync(string name) + public Task ReadJsonAttachmentAsync(string name) { Guard.NotNullOrEmpty(name, nameof(name)); @@ -65,24 +68,16 @@ namespace Squidex.Domain.Apps.Entities.Backup throw new FileNotFoundException("Cannot find attachment.", name); } - JToken result; + T result; using (var stream = attachmentEntry.Open()) { - using (var textReader = new StreamReader(stream)) - { - using (var jsonReader = new JsonTextReader(textReader)) - { - result = await JToken.ReadFromAsync(jsonReader); - - guidMapper.NewGuids(result); - } - } + result = serializer.Deserialize(stream, null, guidMapper.NewGuidOrValue); } readAttachments++; - return result; + return Task.FromResult(result); } public async Task ReadBlobAsync(string name, Func handler) @@ -105,9 +100,10 @@ namespace Squidex.Domain.Apps.Entities.Backup readAttachments++; } - public async Task ReadEventsAsync(IStreamNameResolver streamNameResolver, Func handler) + public async Task ReadEventsAsync(IStreamNameResolver streamNameResolver, IEventDataFormatter formatter, Func<(string Stream, Envelope Event), Task> handler) { Guard.NotNull(handler, nameof(handler)); + Guard.NotNull(formatter, nameof(formatter)); Guard.NotNull(streamNameResolver, nameof(streamNameResolver)); while (true) @@ -121,25 +117,12 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = eventEntry.Open()) { - using (var textReader = new StreamReader(stream)) - { - using (var jsonReader = new JsonTextReader(textReader)) - { - var storedEvent = Serializer.Deserialize(jsonReader); - - storedEvent.Data.Payload = guidMapper.NewGuids(storedEvent.Data.Payload); - storedEvent.Data.Metadata = guidMapper.NewGuids(storedEvent.Data.Metadata); - - var streamName = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidString); + var storedEvent = serializer.Deserialize(stream); - storedEvent = new StoredEvent(streamName, - storedEvent.EventPosition, - storedEvent.EventStreamNumber, - storedEvent.Data); + var eventStream = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidOrNull); + var eventEnvelope = formatter.Parse(storedEvent.Data, true, guidMapper.NewGuidOrValue); - await handler(storedEvent); - } - } + await handler((eventStream, eventEnvelope)); } readEvents++; diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs index fa0e793af..98363cb77 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs @@ -9,18 +9,18 @@ using System; using System.IO; using System.IO.Compression; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Backup.Helpers; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Tasks; namespace Squidex.Domain.Apps.Entities.Backup { public sealed class BackupWriter : DisposableObjectBase { - private static readonly JsonSerializer Serializer = new JsonSerializer(); private readonly ZipArchive archive; + private readonly IJsonSerializer serializer; private int writtenEvents; private int writtenAttachments; @@ -34,8 +34,12 @@ namespace Squidex.Domain.Apps.Entities.Backup get { return writtenAttachments; } } - public BackupWriter(Stream stream, bool keepOpen = false) + public BackupWriter(IJsonSerializer serializer, Stream stream, bool keepOpen = false) { + Guard.NotNull(serializer, nameof(serializer)); + + this.serializer = serializer; + archive = new ZipArchive(stream, ZipArchiveMode.Create, keepOpen); } @@ -47,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Backup } } - public async Task WriteJsonAsync(string name, JToken value) + public Task WriteJsonAsync(string name, object value) { Guard.NotNullOrEmpty(name, nameof(name)); @@ -55,16 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = attachmentEntry.Open()) { - using (var textWriter = new StreamWriter(stream)) - { - using (var jsonWriter = new JsonTextWriter(textWriter)) - { - await value.WriteToAsync(jsonWriter); - } - } + serializer.Serialize(value, stream); } writtenAttachments++; + + return TaskHelper.Done; } public async Task WriteBlobAsync(string name, Func handler) @@ -90,13 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = eventEntry.Open()) { - using (var textWriter = new StreamWriter(stream)) - { - using (var jsonWriter = new JsonTextWriter(textWriter)) - { - Serializer.Serialize(jsonWriter, storedEvent); - } - } + serializer.Serialize(storedEvent, stream); } writtenEvents++; diff --git a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs index c0f7ac827..a2ac8973d 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs @@ -7,112 +7,39 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json.Linq; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Backup { - public sealed class GuidMapper + internal sealed class GuidMapper { private static readonly int GuidLength = Guid.Empty.ToString().Length; - private readonly List<(JObject Source, string NewKey, string OldKey)> mappings = new List<(JObject Source, string NewKey, string OldKey)>(); private readonly Dictionary oldToNewGuid = new Dictionary(); private readonly Dictionary newToOldGuid = new Dictionary(); - public Guid NewGuid(Guid oldGuid) - { - return oldToNewGuid.GetOrDefault(oldGuid); - } - public Guid OldGuid(Guid newGuid) { return newToOldGuid.GetOrDefault(newGuid); } - public string NewGuidString(string key) + public string NewGuidOrNull(string value) { - if (Guid.TryParse(key, out var guid)) + if (TryGenerateNewGuidString(value, out var result) || TryGenerateNewNamedId(value, out result)) { - return GenerateNewGuid(guid).ToString(); + return result; } return null; } - public JToken NewGuids(JToken jToken) - { - var result = NewGuidsCore(jToken); - - if (mappings.Count > 0) - { - foreach (var mapping in mappings) - { - if (mapping.Source.TryGetValue(mapping.OldKey, out var value)) - { - mapping.Source.Remove(mapping.OldKey); - mapping.Source[mapping.NewKey] = value; - } - } - - mappings.Clear(); - } - - return result; - } - - private JToken NewGuidsCore(JToken jToken) - { - switch (jToken.Type) - { - case JTokenType.String: - if (TryConvertString(jToken.ToString(), out var result)) - { - return result; - } - - break; - case JTokenType.Guid: - return GenerateNewGuid((Guid)jToken); - case JTokenType.Object: - NewGuidsCore((JObject)jToken); - break; - case JTokenType.Array: - NewGuidsCore((JArray)jToken); - break; - } - - return jToken; - } - - private void NewGuidsCore(JArray jArray) - { - for (var i = 0; i < jArray.Count; i++) - { - jArray[i] = NewGuidsCore(jArray[i]); - } - } - - private void NewGuidsCore(JObject jObject) + public string NewGuidOrValue(string value) { - foreach (var jProperty in jObject.Properties()) + if (TryGenerateNewGuidString(value, out var result) || TryGenerateNewNamedId(value, out result)) { - var newValue = NewGuidsCore(jProperty.Value); - - if (!ReferenceEquals(newValue, jProperty.Value)) - { - jProperty.Value = newValue; - } - - if (TryConvertString(jProperty.Name, out var newKey)) - { - mappings.Add((jObject, newKey, jProperty.Name)); - } + return result; } - } - private bool TryConvertString(string value, out string result) - { - return TryGenerateNewGuidString(value, out result) || TryGenerateNewNamedId(value, out result); + return value; } private bool TryGenerateNewGuidString(string value, out string result) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs b/src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs index f002efa81..a567eb717 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Net.Http; using System.Threading.Tasks; +using Squidex.Infrastructure.Json; namespace Squidex.Domain.Apps.Entities.Backup.Helpers { @@ -39,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers } } - public static async Task OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, Guid id) + public static async Task OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, Guid id, IJsonSerializer serializer) { Stream stream = null; @@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers { stream = await backupArchiveLocation.OpenStreamAsync(id); - return new BackupReader(stream); + return new BackupReader(serializer, stream); } catch (IOException) { diff --git a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index 0a7536d1b..4a2952e15 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Events.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; @@ -31,6 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private readonly IClock clock; private readonly ICommandBus commandBus; private readonly IEnumerable handlers; + private readonly IJsonSerializer serializer; private readonly IEventStore eventStore; private readonly IEventDataFormatter eventDataFormatter; private readonly ISemanticLog log; @@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup IEventStore eventStore, IEventDataFormatter eventDataFormatter, IEnumerable handlers, + IJsonSerializer serializer, ISemanticLog log, IStreamNameResolver streamNameResolver, IStore store) @@ -61,6 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Backup Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(handlers, nameof(handlers)); + Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(store, nameof(store)); Guard.NotNull(streamNameResolver, nameof(streamNameResolver)); Guard.NotNull(log, nameof(log)); @@ -71,6 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Backup this.eventStore = eventStore; this.eventDataFormatter = eventDataFormatter; this.handlers = handlers; + this.serializer = serializer; this.store = store; this.streamNameResolver = streamNameResolver; this.log = log; @@ -161,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Backup await DownloadAsync(); } - using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id)) + using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id, serializer)) { using (Profiler.Trace("ReadEvents")) { @@ -273,17 +278,15 @@ namespace Squidex.Domain.Apps.Entities.Backup private async Task ReadEventsAsync(BackupReader reader) { - await reader.ReadEventsAsync(streamNameResolver, async storedEvent => + await reader.ReadEventsAsync(streamNameResolver, eventDataFormatter, async storedEvent => { - var @event = eventDataFormatter.Parse(storedEvent.Data); - - await HandleEventAsync(reader, storedEvent, @event); + await HandleEventAsync(reader, storedEvent.Stream, storedEvent.Event); }); Log("Reading events completed."); } - private async Task HandleEventAsync(BackupReader reader, StoredEvent storedEvent, Envelope @event) + private async Task HandleEventAsync(BackupReader reader, string stream, Envelope @event) { if (@event.Payload is SquidexEvent squidexEvent) { @@ -316,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Backup var eventData = eventDataFormatter.ToEventData(@event, @event.Headers.CommitId()); var eventCommit = new List { eventData }; - await eventStore.AppendAsync(Guid.NewGuid(), storedEvent.StreamName, eventCommit); + await eventStore.AppendAsync(Guid.NewGuid(), stream, eventCommit); Log($"Read {reader.ReadEvents} events and {reader.ReadAttachments} attachments.", true); } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs b/src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs index e75eef133..75cf87a13 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs @@ -6,13 +6,13 @@ // ========================================================================== using System.Collections.Generic; -using Newtonsoft.Json; +using System.Runtime.Serialization; namespace Squidex.Domain.Apps.Entities.Backup.State { public sealed class BackupState { - [JsonProperty] + [DataMember] public List Jobs { get; } = new List(); } } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs b/src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs index 6da5e9dfa..209a6f1c7 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs @@ -6,29 +6,29 @@ // ========================================================================== using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using NodaTime; namespace Squidex.Domain.Apps.Entities.Backup.State { public sealed class BackupStateJob : IBackupJob { - [JsonProperty] + [DataMember] public Guid Id { get; set; } - [JsonProperty] + [DataMember] public Instant Started { get; set; } - [JsonProperty] + [DataMember] public Instant? Stopped { get; set; } - [JsonProperty] + [DataMember] public int HandledEvents { get; set; } - [JsonProperty] + [DataMember] public int HandledAssets { get; set; } - [JsonProperty] + [DataMember] public JobStatus Status { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs index d86a658fe..bbfc4db36 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs @@ -5,13 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; +using System.Runtime.Serialization; namespace Squidex.Domain.Apps.Entities.Backup.State { public class RestoreState { - [JsonProperty] + [DataMember] public RestoreStateJob Job { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs index f3fd17042..edefb80dd 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs @@ -7,38 +7,39 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; +using System.Runtime.Serialization; using NodaTime; namespace Squidex.Domain.Apps.Entities.Backup.State { + [DataContract] public sealed class RestoreStateJob : IRestoreJob { - [JsonProperty] + [DataMember] public string AppName { get; set; } - [JsonProperty] + [DataMember] public Guid Id { get; set; } - [JsonProperty] + [DataMember] public Guid AppId { get; set; } - [JsonProperty] + [DataMember] public Uri Url { get; set; } - [JsonProperty] + [DataMember] public string NewAppName { get; set; } - [JsonProperty] + [DataMember] public Instant Started { get; set; } - [JsonProperty] + [DataMember] public Instant? Stopped { get; set; } - [JsonProperty] + [DataMember] public List Log { get; set; } = new List(); - [JsonProperty] + [DataMember] public JobStatus Status { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs index 2c1242877..d5b1316fb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs @@ -8,8 +8,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Infrastructure.Json.Objects; + namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { public sealed class GraphQLExecutionContext : QueryExecutionContext @@ -25,29 +26,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL UrlGenerator = urlGenerator; } - public Task> GetReferencedAssetsAsync(JToken value) + public Task> GetReferencedAssetsAsync(IJsonValue value) { var ids = ParseIds(value); return GetReferencedAssetsAsync(ids); } - public Task> GetReferencedContentsAsync(Guid schemaId, JToken value) + public Task> GetReferencedContentsAsync(Guid schemaId, IJsonValue value) { var ids = ParseIds(value); return GetReferencedContentsAsync(schemaId, ids); } - private static ICollection ParseIds(JToken value) + private static ICollection ParseIds(IJsonValue value) { try { var result = new List(); - if (value is JArray) + if (value is JsonArray array) { - foreach (var id in value) + foreach (var id in array) { result.Add(Guid.Parse(id.ToString())); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs index a450cf448..18b9beff3 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public static readonly IGraphType Guid = new GuidGraphType2(); - public static readonly IGraphType Date = new DateTimeGraphType(); + public static readonly IGraphType Date = new InstantGraphType(); public static readonly IGraphType Json = new JsonGraphType(); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs index 47e32ed04..c23f2c69c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs @@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { Name = "created", ResolvedType = AllTypes.NonNullDate, - Resolver = Resolve(x => x.Created.ToDateTimeUtc()), + Resolver = Resolve(x => x.Created), Description = "The date and time when the asset has been created." }); @@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { Name = "lastModified", ResolvedType = AllTypes.NonNullDate, - Resolver = Resolve(x => x.LastModified.ToDateTimeUtc()), + Resolver = Resolve(x => x.LastModified), Description = "The date and time when the asset has been modified last." }); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs index cb77d343d..20358dd65 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs @@ -9,10 +9,10 @@ using System.Collections.Generic; using System.Linq; using GraphQL.Resolvers; using GraphQL.Types; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { @@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types fieldGraphType.Description = $"The structure of the {fieldName} field of the {schemaName} content type."; - var fieldResolver = new FuncFieldResolver>(c => + var fieldResolver = new FuncFieldResolver>(c => { return c.Source.GetOrDefault(field.Name); }); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs index 40990b9ad..31f68a324 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs @@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { Name = "created", ResolvedType = AllTypes.NonNullDate, - Resolver = Resolve(x => x.Created.ToDateTimeUtc()), + Resolver = Resolve(x => x.Created), Description = $"The date and time when the {schemaName} content has been created." }); @@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { Name = "lastModified", ResolvedType = AllTypes.NonNullDate, - Resolver = Resolve(x => x.LastModified.ToDateTimeUtc()), + Resolver = Resolve(x => x.LastModified), Description = $"The date and time when the {schemaName} content has been modified last." }); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs index b0dc095d2..35c0a1b2e 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs @@ -8,14 +8,14 @@ using System.Linq; using GraphQL.Resolvers; using GraphQL.Types; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { - public sealed class NestedGraphType : ObjectGraphType + public sealed class NestedGraphType : ObjectGraphType { public NestedGraphType(IGraphModel model, ISchemaEntity schema, IArrayField field) { @@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { var resolver = new FuncFieldResolver(c => { - if (((JObject)c.Source).TryGetValue(nestedField.Name, out var value)) + if (((JsonObject)c.Source).TryGetValue(nestedField.Name, out var value)) { return fieldInfo.Resolver(value, c); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs index fa1e34514..2ed69033c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs @@ -7,13 +7,13 @@ using System; using GraphQL.Types; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { - public delegate object ValueResolver(JToken value, ResolveFieldContext context); + public delegate object ValueResolver(IJsonValue value, ResolveFieldContext context); public sealed class QueryGraphTypeVisitor : IFieldVisitor<(IGraphType ResolveType, ValueResolver Resolver)> { diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs new file mode 100644 index 000000000..2f45fe6a9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs @@ -0,0 +1,41 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using GraphQL.Language.AST; +using GraphQL.Types; +using NodaTime.Text; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils +{ + public sealed class InstantGraphType : DateGraphType + { + public override object Serialize(object value) + { + return ParseValue(value); + } + + public override object ParseValue(object value) + { + return InstantPattern.General.Parse(value.ToString()).Value; + } + + public override object ParseLiteral(IValue value) + { + if (value is InstantValue timeValue) + { + return ParseValue(timeValue.Value); + } + + if (value is StringValue stringValue) + { + return ParseValue(stringValue.Value); + } + + return null; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs new file mode 100644 index 000000000..1af6d2e92 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using GraphQL.Language.AST; +using NodaTime; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils +{ + public sealed class InstantValue : ValueNode + { + public InstantValue(Instant value) + { + Value = value; + } + + protected override bool Equals(ValueNode node) + { + return Value.Equals(node.Value); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs index 62bc939fb..a433a5fb3 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs @@ -7,7 +7,7 @@ using GraphQL.Language.AST; using GraphQL.Types; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils { @@ -21,12 +21,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils public IValue Convert(object value, IGraphType type) { - return new JsonValue(value as JObject); + return new JsonValue(value as JsonObject); } public bool Matches(object value, IGraphType type) { - return value is JObject; + return value is JsonObject; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs index 4977e33d2..01449380f 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs @@ -6,18 +6,18 @@ // ========================================================================== using GraphQL.Language.AST; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils { - public sealed class JsonValue : ValueNode + public sealed class JsonValue : ValueNode { - public JsonValue(JObject value) + public JsonValue(IJsonValue value) { Value = value; } - protected override bool Equals(ValueNode node) + protected override bool Equals(ValueNode node) { return false; } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs index 15f2fc357..56936bb0a 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs @@ -6,7 +6,7 @@ // ========================================================================== using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Contents; @@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.State { public class ContentState : DomainObjectState, IContentEntity { - [JsonProperty] + [DataMember] public NamedId AppId { get; set; } - [JsonProperty] + [DataMember] public NamedId SchemaId { get; set; } - [JsonProperty] + [DataMember] public NamedContentData Data { get; set; } - [JsonProperty] + [DataMember] public NamedContentData DataDraft { get; set; } - [JsonProperty] + [DataMember] public ScheduleJob ScheduleJob { get; set; } - [JsonProperty] + [DataMember] public bool IsPending { get; set; } - [JsonProperty] + [DataMember] public bool IsDeleted { get; set; } - [JsonProperty] + [DataMember] public Status Status { get; set; } protected void On(ContentCreated @event) diff --git a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs index 13ce32acc..58b6341b5 100644 --- a/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs +++ b/src/Squidex.Domain.Apps.Entities/DomainObjectState.cs @@ -6,7 +6,7 @@ // ========================================================================== using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using NodaTime; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -24,22 +24,22 @@ namespace Squidex.Domain.Apps.Entities IUpdateableEntityWithLastModifiedBy where T : Cloneable { - [JsonProperty] + [DataMember] public Guid Id { get; set; } - [JsonProperty] + [DataMember] public RefToken CreatedBy { get; set; } - [JsonProperty] + [DataMember] public RefToken LastModifiedBy { get; set; } - [JsonProperty] + [DataMember] public Instant Created { get; set; } - [JsonProperty] + [DataMember] public Instant LastModified { get; set; } - [JsonProperty] + [DataMember] public long Version { get; set; } = EtagVersion.Empty; public T Clone() diff --git a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs index 80da5ed16..a12f9a744 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs @@ -6,7 +6,7 @@ // ========================================================================== using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Rules; @@ -20,13 +20,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.State [CollectionName("Rules")] public class RuleState : DomainObjectState, IRuleEntity { - [JsonProperty] + [DataMember] public NamedId AppId { get; set; } - [JsonProperty] + [DataMember] public Rule RuleDef { get; set; } - [JsonProperty] + [DataMember] public bool IsDeleted { get; set; } protected void On(RuleCreated @event) diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index fdec25995..59aebd11e 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -6,7 +6,7 @@ // ========================================================================== using System; -using Newtonsoft.Json; +using System.Runtime.Serialization; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Events; @@ -22,43 +22,43 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State [CollectionName("Schemas")] public class SchemaState : DomainObjectState, ISchemaEntity { - [JsonProperty] + [DataMember] public NamedId AppId { get; set; } - [JsonProperty] + [DataMember] public string Name { get; set; } - [JsonProperty] + [DataMember] public string Category { get; set; } - [JsonProperty] + [DataMember] public int TotalFields { get; set; } - [JsonProperty] + [DataMember] public bool IsDeleted { get; set; } - [JsonProperty] + [DataMember] public bool IsSingleton { get; set; } - [JsonProperty] + [DataMember] public string ScriptQuery { get; set; } - [JsonProperty] + [DataMember] public string ScriptCreate { get; set; } - [JsonProperty] + [DataMember] public string ScriptUpdate { get; set; } - [JsonProperty] + [DataMember] public string ScriptDelete { get; set; } - [JsonProperty] + [DataMember] public string ScriptChange { get; set; } - [JsonProperty] + [DataMember] public Schema SchemaDef { get; set; } - [JsonIgnore] + [IgnoreDataMember] public bool IsPublished { get { return SchemaDef.IsPublished; } diff --git a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs index 0673d3b5b..007b19de8 100644 --- a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs +++ b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Globalization; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events @@ -15,7 +14,7 @@ namespace Squidex.Domain.Apps.Events { public static Guid AppId(this EnvelopeHeaders headers) { - return headers[SquidexHeaders.AppId].ToGuid(CultureInfo.InvariantCulture); + return headers[SquidexHeaders.AppId].ToGuid(); } public static Envelope SetAppId(this Envelope envelope, Guid value) where T : class diff --git a/src/Squidex.Infrastructure.Redis/RedisPubSub.cs b/src/Squidex.Infrastructure.Redis/RedisPubSub.cs index ce5e9f1f0..48cdb58b0 100644 --- a/src/Squidex.Infrastructure.Redis/RedisPubSub.cs +++ b/src/Squidex.Infrastructure.Redis/RedisPubSub.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Tasks; using StackExchange.Redis; @@ -19,11 +20,13 @@ namespace Squidex.Infrastructure { private readonly ConcurrentDictionary subscriptions = new ConcurrentDictionary(); private readonly Lazy redisClient; + private readonly IJsonSerializer serializer; private readonly Lazy redisSubscriber; private readonly ISemanticLog log; - public RedisPubSub(Lazy redis, ISemanticLog log) + public RedisPubSub(Lazy redis, IJsonSerializer serializer, ISemanticLog log) { + Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(redis, nameof(redis)); Guard.NotNull(log, nameof(log)); @@ -31,6 +34,8 @@ namespace Squidex.Infrastructure redisClient = redis; redisSubscriber = new Lazy(() => redis.Value.GetSubscriber()); + + this.serializer = serializer; } public Task InitializeAsync(CancellationToken ct = default(CancellationToken)) @@ -61,7 +66,7 @@ namespace Squidex.Infrastructure { var typeName = typeof(T).FullName; - return (RedisSubscription)subscriptions.GetOrAdd(typeName, this, (k, c) => new RedisSubscription(c.redisSubscriber.Value, k, c.log)); + return (RedisSubscription)subscriptions.GetOrAdd(typeName, this, (k, c) => new RedisSubscription(c.redisSubscriber.Value, serializer, k, c.log)); } } } diff --git a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs index d2e88a555..dc8c10be2 100644 --- a/src/Squidex.Infrastructure.Redis/RedisSubscription.cs +++ b/src/Squidex.Infrastructure.Redis/RedisSubscription.cs @@ -7,7 +7,7 @@ using System; using System.Reactive.Subjects; -using Newtonsoft.Json; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using StackExchange.Redis; @@ -20,6 +20,7 @@ namespace Squidex.Infrastructure private readonly Guid selfId = Guid.NewGuid(); private readonly Subject subject = new Subject(); private readonly ISubscriber subscriber; + private readonly IJsonSerializer serializer; private readonly ISemanticLog log; private readonly string channelName; @@ -30,10 +31,11 @@ namespace Squidex.Infrastructure public Guid Sender; } - public RedisSubscription(ISubscriber subscriber, string channelName, ISemanticLog log) + public RedisSubscription(ISubscriber subscriber, IJsonSerializer serializer, string channelName, ISemanticLog log) { this.log = log; + this.serializer = serializer; this.subscriber = subscriber; this.subscriber.Subscribe(channelName, (channel, value) => HandleMessage(value)); @@ -46,7 +48,7 @@ namespace Squidex.Infrastructure { var senderId = notifySelf ? Guid.Empty : selfId; - var envelope = JsonConvert.SerializeObject(new Envelope { Sender = senderId, Payload = (T)value }); + var envelope = serializer.Serialize(new Envelope { Sender = senderId, Payload = (T)value }); subscriber.Publish(channelName, envelope); } @@ -68,7 +70,7 @@ namespace Squidex.Infrastructure return; } - var envelope = JsonConvert.DeserializeObject(value); + var envelope = serializer.Deserialize(value); if (envelope.Sender != selfId) { diff --git a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs index 22735b68d..ba6f659a8 100644 --- a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs @@ -25,12 +25,12 @@ namespace Squidex.Infrastructure.EventSourcing this.serializer = serializer; } - public Envelope Parse(EventData eventData, bool migrate = true) + public Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null) { var eventType = typeNameRegistry.GetType(eventData.Type); - var eventHeaders = serializer.Deserialize(eventData.Metadata); - var eventContent = serializer.Deserialize(eventData.Payload, eventType); + var eventHeaders = serializer.Deserialize(eventData.Metadata, null, stringConverter); + var eventContent = serializer.Deserialize(eventData.Payload, eventType, stringConverter); if (migrate && eventContent is IMigratedEvent migratedEvent) { diff --git a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs index a1d60034c..f1dce1331 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Globalization; using NodaTime; namespace Squidex.Infrastructure.EventSourcing @@ -27,7 +26,7 @@ namespace Squidex.Infrastructure.EventSourcing public static long EventStreamNumber(this EnvelopeHeaders headers) { - return headers[CommonHeaders.EventStreamNumber].ToInt64(CultureInfo.InvariantCulture); + return headers[CommonHeaders.EventStreamNumber].ToInt64(); } public static Envelope SetEventStreamNumber(this Envelope envelope, long value) where T : class @@ -39,7 +38,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Guid CommitId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.CommitId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.CommitId].ToGuid(); } public static Envelope SetCommitId(this Envelope envelope, Guid value) where T : class @@ -51,7 +50,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Guid AggregateId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.AggregateId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.AggregateId].ToGuid(); } public static Envelope SetAggregateId(this Envelope envelope, Guid value) where T : class @@ -63,7 +62,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Guid EventId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.EventId].ToGuid(CultureInfo.InvariantCulture); + return headers[CommonHeaders.EventId].ToGuid(); } public static Envelope SetEventId(this Envelope envelope, Guid value) where T : class @@ -75,7 +74,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Instant Timestamp(this EnvelopeHeaders headers) { - return headers[CommonHeaders.Timestamp].ToInstant(CultureInfo.InvariantCulture); + return headers[CommonHeaders.Timestamp].ToInstant(); } public static Envelope SetTimestamp(this Envelope envelope, Instant value) where T : class diff --git a/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs index 6cd586c84..071808fb6 100644 --- a/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs @@ -11,7 +11,7 @@ namespace Squidex.Infrastructure.EventSourcing { public interface IEventDataFormatter { - Envelope Parse(EventData eventData, bool migrate = true); + Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null); EventData ToEventData(Envelope envelope, Guid commitId, bool migrate = true); } diff --git a/src/Squidex.Infrastructure/Json/IJsonSerializer.cs b/src/Squidex.Infrastructure/Json/IJsonSerializer.cs index e42fb9ea3..704ebaa78 100644 --- a/src/Squidex.Infrastructure/Json/IJsonSerializer.cs +++ b/src/Squidex.Infrastructure/Json/IJsonSerializer.cs @@ -12,12 +12,12 @@ namespace Squidex.Infrastructure.Json { public interface IJsonSerializer { - string Serialize(T value); + string Serialize(T value, bool intented = false); void Serialize(T value, Stream stream); - T Deserialize(string value, Type actualType = null); + T Deserialize(string value, Type actualType = null, Func stringConverter = null); - T Deserialize(Stream stream, Type actualType = null); + T Deserialize(Stream stream, Type actualType = null, Func stringConverter = null); } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index 81825b5e1..5b31bb412 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -10,15 +10,12 @@ using System.Globalization; using Newtonsoft.Json; using Squidex.Infrastructure.Json.Objects; +#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator + namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class JsonValueConverter : JsonConverter { - public override bool CanConvert(Type objectType) - { - return typeof(IJsonValue).IsAssignableFrom(objectType); - } - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return ReadJson(reader); @@ -125,36 +122,46 @@ namespace Squidex.Infrastructure.Json.Newtonsoft writer.WriteValue(s.Value); break; case JsonScalar s: - writer.WriteValue(s.Value); - break; - case JsonArray array: + + if (s.Value % 1 == 0) + { + writer.WriteValue((long)s.Value); + } + else { - writer.WriteStartArray(); + writer.WriteValue(s.Value); + } - foreach (var item in array) - { - WriteJson(writer, item); - } + break; + case JsonArray array: + writer.WriteStartArray(); - writer.WriteEndArray(); - break; + foreach (var item in array) + { + WriteJson(writer, item); } - case JsonObject obj: - { - writer.WriteStartObject(); + writer.WriteEndArray(); + break; - foreach (var kvp in obj) - { - writer.WritePropertyName(kvp.Key); + case JsonObject obj: + writer.WriteStartObject(); - WriteJson(writer, kvp.Value); - } + foreach (var kvp in obj) + { + writer.WritePropertyName(kvp.Key); - writer.WriteEndObject(); - break; + WriteJson(writer, kvp.Value); } + + writer.WriteEndObject(); + break; } } + + public override bool CanConvert(Type objectType) + { + return typeof(IJsonValue).IsAssignableFrom(objectType); + } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs index a9a045ed3..3a79cd5d5 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs @@ -16,6 +16,32 @@ namespace Squidex.Infrastructure.Json.Newtonsoft private readonly JsonSerializerSettings settings; private readonly JsonSerializer serializer; + private sealed class CustomReader : JsonTextReader + { + private readonly Func stringConverter; + + public override object Value + { + get + { + var value = base.Value; + + if (value is string s) + { + return stringConverter(s); + } + + return value; + } + } + + public CustomReader(TextReader reader, Func stringConverter) + : base(reader) + { + this.stringConverter = stringConverter; + } + } + public NewtonsoftJsonSerializer(JsonSerializerSettings settings) { Guard.NotNull(settings, nameof(settings)); @@ -25,36 +51,50 @@ namespace Squidex.Infrastructure.Json.Newtonsoft serializer = JsonSerializer.Create(settings); } - public T Deserialize(string value, Type actualType = null) + public string Serialize(T value, bool intented) { - actualType = actualType ?? typeof(T); - - return (T)JsonConvert.DeserializeObject(value, actualType, settings); + return JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings); } - public T Deserialize(Stream stream, Type actualType = null) + public void Serialize(T value, Stream stream) { - using (var streamReader = new StreamReader(stream)) + using (var writer = new StreamWriter(stream)) { - actualType = actualType ?? typeof(T); + serializer.Serialize(writer, value); - return (T)serializer.Deserialize(streamReader, actualType); + writer.Flush(); } } - public string Serialize(T value) + public T Deserialize(string value, Type actualType = null, Func stringConverter = null) { - return JsonConvert.SerializeObject(value, settings); + using (var textReader = new StringReader(value)) + { + actualType = actualType ?? typeof(T); + + using (var reader = GetReader(stringConverter, textReader)) + { + return (T)serializer.Deserialize(reader, actualType); + } + } } - public void Serialize(T value, Stream stream) + public T Deserialize(Stream stream, Type actualType = null, Func stringConverter = null) { - using (var writer = new StreamWriter(stream)) + using (var textReader = new StreamReader(stream)) { - serializer.Serialize(writer, value); + actualType = actualType ?? typeof(T); - writer.Flush(); + using (var reader = GetReader(stringConverter, textReader)) + { + return (T)serializer.Deserialize(reader, actualType); + } } } + + private static JsonTextReader GetReader(Func stringConverter, TextReader textReader) + { + return stringConverter != null ? new CustomReader(textReader, stringConverter) : new JsonTextReader(textReader); + } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs index 27bda90ae..8eb624c1f 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs @@ -22,9 +22,9 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { writer.WritePropertyName(kvp.Key); - if (kvp.Value.RawValue is Instant) + if (kvp.Value.RawValue is Instant instant) { - writer.WriteValue(kvp.Value.ToString()); + writer.WriteValue(instant.ToString()); } else { diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs index 06c3325b8..3fc3ae13e 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using NodaTime; namespace Squidex.Infrastructure.Json.Objects { @@ -59,6 +60,8 @@ namespace Squidex.Infrastructure.Json.Objects return Create(i); case long l: return Create(l); + case Instant i: + return Create(i); } throw new ArgumentException("Invalid json type"); @@ -76,6 +79,16 @@ namespace Squidex.Infrastructure.Json.Objects return new JsonScalar(JsonValueType.Number, value); } + public static IJsonValue Create(Instant? value) + { + if (value == null) + { + return Null; + } + + return Create(value.Value.ToString()); + } + public static IJsonValue Create(double? value) { if (value == null) diff --git a/src/Squidex.Infrastructure/Language.cs b/src/Squidex.Infrastructure/Language.cs index ba5c546b6..981d7ee47 100644 --- a/src/Squidex.Infrastructure/Language.cs +++ b/src/Squidex.Infrastructure/Language.cs @@ -18,7 +18,7 @@ namespace Squidex.Infrastructure private static Language AddLanguage(string iso2Code, string englishName) { - return AllLanguagesField.GetOrAdd(iso2Code, code => new Language(code, englishName)); + return AllLanguagesField.GetOrAdd(iso2Code, englishName, (c, n) => new Language(c, n)); } public static Language GetLanguage(string iso2Code) diff --git a/src/Squidex.Infrastructure/Orleans/J{T}.cs b/src/Squidex.Infrastructure/Orleans/J{T}.cs index bfef10aac..8946ef845 100644 --- a/src/Squidex.Infrastructure/Orleans/J{T}.cs +++ b/src/Squidex.Infrastructure/Orleans/J{T}.cs @@ -82,7 +82,7 @@ namespace Squidex.Infrastructure.Orleans { try { - return context?.ServiceProvider?.GetService() ?? J.DefaultSerializer; + return context?.ServiceProvider?.GetRequiredService() ?? J.DefaultSerializer; } catch { diff --git a/src/Squidex.Infrastructure/PropertyValue.cs b/src/Squidex.Infrastructure/PropertyValue.cs index 8187e0b7a..ddec447c6 100644 --- a/src/Squidex.Infrastructure/PropertyValue.cs +++ b/src/Squidex.Infrastructure/PropertyValue.cs @@ -18,24 +18,24 @@ namespace Squidex.Infrastructure { private readonly object rawValue; - private static readonly Dictionary> Parsers = - new Dictionary> + private static readonly Dictionary> Parsers = + new Dictionary> { - { typeof(string), (p, c) => p.ToString() }, - { typeof(bool), (p, c) => p.ToBoolean(c) }, - { typeof(bool?), (p, c) => p.ToNullableBoolean(c) }, - { typeof(float), (p, c) => p.ToSingle(c) }, - { typeof(float?), (p, c) => p.ToNullableSingle(c) }, - { typeof(double), (p, c) => p.ToDouble(c) }, - { typeof(double?), (p, c) => p.ToNullableDouble(c) }, - { typeof(int), (p, c) => p.ToInt32(c) }, - { typeof(int?), (p, c) => p.ToNullableInt32(c) }, - { typeof(long), (p, c) => p.ToInt64(c) }, - { typeof(long?), (p, c) => p.ToNullableInt64(c) }, - { typeof(Instant), (p, c) => p.ToInstant(c) }, - { typeof(Instant?), (p, c) => p.ToNullableInstant(c) }, - { typeof(Guid), (p, c) => p.ToGuid(c) }, - { typeof(Guid?), (p, c) => p.ToNullableGuid(c) } + { typeof(string), p => p.ToString() }, + { typeof(bool), p => p.ToBoolean() }, + { typeof(bool?), p => p.ToNullableBoolean() }, + { typeof(float), p => p.ToSingle() }, + { typeof(float?), p => p.ToNullableSingle() }, + { typeof(double), p => p.ToDouble() }, + { typeof(double?), p => p.ToNullableDouble() }, + { typeof(int), p => p.ToInt32() }, + { typeof(int?), p => p.ToNullableInt32() }, + { typeof(long), p => p.ToInt64() }, + { typeof(long?), p => p.ToNullableInt64() }, + { typeof(Instant), p => p.ToInstant() }, + { typeof(Instant?), p => p.ToNullableInstant() }, + { typeof(Guid), p => p.ToGuid() }, + { typeof(Guid?), p => p.ToNullableGuid() } }; public object RawValue @@ -62,7 +62,7 @@ namespace Squidex.Infrastructure return false; } - result = parser(this, CultureInfo.InvariantCulture); + result = parser(this); return true; } @@ -72,74 +72,74 @@ namespace Squidex.Infrastructure return rawValue?.ToString(); } - public bool ToBoolean(CultureInfo culture) + public bool ToBoolean() { - return ToOrParseValue(culture, ParseBoolean); + return ToOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); } - public bool? ToNullableBoolean(CultureInfo culture) + public bool? ToNullableBoolean() { - return ToNullableOrParseValue(culture, ParseBoolean); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); } - public float ToSingle(CultureInfo culture) + public float ToSingle() { - return ToOrParseValue(culture, x => float.Parse(x, culture)); + return ToOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); } - public float? ToNullableSingle(CultureInfo culture) + public float? ToNullableSingle() { - return ToNullableOrParseValue(culture, x => float.Parse(x, culture)); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); } - public double ToDouble(CultureInfo culture) + public double ToDouble() { - return ToOrParseValue(culture, x => double.Parse(x, culture)); + return ToOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); } - public double? ToNullableDouble(CultureInfo culture) + public double? ToNullableDouble() { - return ToNullableOrParseValue(culture, x => double.Parse(x, culture)); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); } - public int ToInt32(CultureInfo culture) + public int ToInt32() { - return ToOrParseValue(culture, x => int.Parse(x, culture)); + return ToOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); } - public int? ToNullableInt32(CultureInfo culture) + public int? ToNullableInt32() { - return ToNullableOrParseValue(culture, x => int.Parse(x, culture)); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); } - public long ToInt64(CultureInfo culture) + public long ToInt64() { - return ToOrParseValue(culture, x => long.Parse(x, culture)); + return ToOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); } - public long? ToNullableInt64(CultureInfo culture) + public long? ToNullableInt64() { - return ToNullableOrParseValue(culture, x => long.Parse(x, culture)); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); } - public Instant ToInstant(CultureInfo culture) + public Instant ToInstant() { - return ToOrParseValue(culture, x => InstantPattern.General.Parse(x).Value); + return ToOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); } - public Instant? ToNullableInstant(CultureInfo culture) + public Instant? ToNullableInstant() { - return ToNullableOrParseValue(culture, x => InstantPattern.General.Parse(x).Value); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); } - public Guid ToGuid(CultureInfo culture) + public Guid ToGuid() { - return ToOrParseValue(culture, Guid.Parse); + return ToOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); } - public Guid? ToNullableGuid(CultureInfo culture) + public Guid? ToNullableGuid() { - return ToNullableOrParseValue(culture, Guid.Parse); + return ToNullableOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); } private T? ToNullableOrParseValue(IFormatProvider culture, Func parser) where T : struct diff --git a/src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs b/src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs index 07e0f2c6f..96d827867 100644 --- a/src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs +++ b/src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs @@ -17,7 +17,7 @@ namespace Squidex.Areas.Api.Config.Swagger { public static void UseMySwagger(this IApplicationBuilder app) { - var urlOptions = app.ApplicationServices.GetService>().Value; + var urlOptions = app.ApplicationServices.GetRequiredService>().Value; app.UseSwaggerWithApiExplorer(settings => { diff --git a/src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs b/src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs index 336025ee0..d226cc06f 100644 --- a/src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs +++ b/src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs @@ -28,7 +28,7 @@ namespace Squidex.Areas.Api.Config.Swagger { services.AddSingleton(typeof(SwaggerSettings), s => { - var urlOptions = s.GetService>().Value; + var urlOptions = s.GetRequiredService>().Value; var settings = new SwaggerSettings() .AddAssetODataParams() diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs index 792296462..38e6adae1 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs @@ -8,7 +8,6 @@ using System; using System.ComponentModel.DataAnnotations; using System.Linq; -using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Infrastructure.Commands; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs index f2774e4b4..df40cecd5 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; -using Newtonsoft.Json; using NodaTime; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps.Services; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs index 0efd8885b..ad429ac14 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs @@ -6,7 +6,6 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure.Reflection; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs index 4fde2f7b1..75d6b8da3 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure.Reflection; diff --git a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs index 0bc35ef83..42ea190e5 100644 --- a/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs @@ -6,7 +6,6 @@ // ========================================================================== using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json; namespace Squidex.Areas.Api.Controllers.Apps.Models { diff --git a/src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs b/src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs index 8262de435..b6c3f5f53 100644 --- a/src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs +++ b/src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Areas.Api.Controllers.UI.Models { @@ -14,6 +14,6 @@ namespace Squidex.Areas.Api.Controllers.UI.Models /// /// The value for the setting. /// - public JToken Value { get; set; } + public IJsonValue Value { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/UI/UIController.cs b/src/Squidex/Areas/Api/Controllers/UI/UIController.cs index 385c6e20e..d5c486d93 100644 --- a/src/Squidex/Areas/Api/Controllers/UI/UIController.cs +++ b/src/Squidex/Areas/Api/Controllers/UI/UIController.cs @@ -55,9 +55,10 @@ namespace Squidex.Areas.Api.Controllers.UI { var result = await grainFactory.GetGrain(AppId).GetAsync(); - result.Value["mapType"] = uiOptions.Map?.Type ?? "OSM"; - result.Value["mapKey"] = uiOptions.Map?.GoogleMaps?.Key; - result.Value["supportTwitterAction"] = twitterOptions.IsConfigured(); + result.Value.Add("mapType", uiOptions.Map?.Type ?? "OSM"); + result.Value.Add("mapKey", uiOptions.Map?.GoogleMaps?.Key); + + result.Value.Add("supportTwitterAction", twitterOptions.IsConfigured()); return Ok(result.Value); } diff --git a/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs b/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs index f7bc37b51..7ea0f39dc 100644 --- a/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs +++ b/src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs @@ -31,12 +31,12 @@ namespace Squidex.Areas.IdentityServer.Config public static IServiceProvider UseMyAdmin(this IServiceProvider services) { - var options = services.GetService>().Value; + var options = services.GetRequiredService>().Value; - var userManager = services.GetService>(); - var userFactory = services.GetService(); + var userManager = services.GetRequiredService>(); + var userFactory = services.GetRequiredService(); - var log = services.GetService(); + var log = services.GetRequiredService(); if (options.IsAdminConfigured()) { diff --git a/src/Squidex/Config/Domain/EventPublishersServices.cs b/src/Squidex/Config/Domain/EventPublishersServices.cs index 9116f7594..dad6cd6ec 100644 --- a/src/Squidex/Config/Domain/EventPublishersServices.cs +++ b/src/Squidex/Config/Domain/EventPublishersServices.cs @@ -8,10 +8,10 @@ using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; namespace Squidex.Config.Domain { @@ -54,7 +54,7 @@ namespace Squidex.Config.Domain if (enabled) { - services.AddSingletonAs(c => new RabbitMqEventConsumer(c.GetRequiredService(), name, publisherConfig, exchange, eventsFilter)) + services.AddSingletonAs(c => new RabbitMqEventConsumer(c.GetRequiredService(), name, publisherConfig, exchange, eventsFilter)) .As(); } } diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index be0d316e6..10596cbbe 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -20,7 +20,7 @@ using Squidex.Domain.Apps.Events; using Squidex.Extensions.Actions; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Config.Domain { @@ -49,6 +49,7 @@ namespace Squidex.Config.Domain new AppPatternsConverter(), new ClaimsPrincipalConverter(), new InstantConverter(), + new JsonValueConverter(), new LanguageConverter(), new LanguagesConfigConverter(), new NamedGuidIdConverter(), @@ -86,6 +87,8 @@ namespace Squidex.Config.Domain services.AddSingleton(DefaultJsonSerializer); services.AddSingleton(TypeNameRegistry); + services.AddSingleton(new NewtonsoftJsonSerializer(DefaultJsonSettings)); + return services; } diff --git a/src/Squidex/Config/Domain/StoreServices.cs b/src/Squidex/Config/Domain/StoreServices.cs index eb5c8065f..5731d4744 100644 --- a/src/Squidex/Config/Domain/StoreServices.cs +++ b/src/Squidex/Config/Domain/StoreServices.cs @@ -29,6 +29,7 @@ using Squidex.Domain.Users.MongoDb.Infrastructure; using Squidex.Infrastructure; using Squidex.Infrastructure.Diagnostics; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.States; @@ -88,7 +89,7 @@ namespace Squidex.Config.Domain .As() .As>(); - services.AddSingletonAs(c => new MongoContentRepository(mongoContentDatabase, c.GetService())) + services.AddSingletonAs(c => new MongoContentRepository(mongoContentDatabase, c.GetRequiredService(), c.GetRequiredService())) .As() .As>() .As(); diff --git a/src/Squidex/Config/Domain/SubscriptionServices.cs b/src/Squidex/Config/Domain/SubscriptionServices.cs index a9205dbeb..10437c2b7 100644 --- a/src/Squidex/Config/Domain/SubscriptionServices.cs +++ b/src/Squidex/Config/Domain/SubscriptionServices.cs @@ -19,7 +19,7 @@ namespace Squidex.Config.Domain { public static void AddMySubscriptionServices(this IServiceCollection services, IConfiguration config) { - services.AddSingletonAs(c => c.GetService>()?.Value?.Plans.OrEmpty()); + services.AddSingletonAs(c => c.GetRequiredService>()?.Value?.Plans.OrEmpty()); services.AddSingletonAs() .As(); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs index 70d28c50b..e9f155b52 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs @@ -128,7 +128,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules [MemberData(nameof(Triggers))] public void Should_freeze_triggers(RuleTrigger trigger) { - TestData.TestFreeze(trigger); + TestUtils.TestFreeze(trigger); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs index ce33221b3..439600188 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs @@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas [MemberData(nameof(FieldProperties))] public void Should_freeze_field_properties(FieldProperties action) { - TestData.TestFreeze(action); + TestUtils.TestFreeze(action); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs index 1f8073699..4f426ee04 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs @@ -278,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas [Fact] public void Should_serialize_and_deserialize_schema() { - var schemaSource = TestData.MixedSchema(); + var schemaSource = TestUtils.MixedSchema(); var schemaTarget = schemaSource.SerializeAndDeserialize(); schemaTarget.Should().BeEquivalentTo(schemaSource); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index 0c2d23704..f9b90bce8 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs @@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new ContentFieldData() .AddValue("iv", JsonValue.Object()); - var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestData.DefaultSerializer))(input, jsonField); + var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestUtils.DefaultSerializer))(input, jsonField); var expected = new ContentFieldData() diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs index 09d0c4bdc..f1e3a82f6 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs @@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Object(); - var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField); + var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField); Assert.Equal(JsonValue.Create("e30="), result); } @@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField); + var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField); Assert.Same(source, result); } @@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Create("NO-JSON"); - var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField); + var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField); Assert.Same(source, result); } @@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Create("e30="); - var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField); + var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField); Assert.Equal(JsonValue.Object(), result); } @@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField); + var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField); Assert.Same(source, result); } @@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = JsonValue.Null; - var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField); + var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField); Assert.Same(source, result); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs index 8feb2df02..92ab7b60d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateEdmSchema { var languagesConfig = LanguagesConfig.Build(Language.DE, Language.EN); - var edmModel = TestData.MixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x); + var edmModel = TestUtils.MixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x); Assert.NotNull(edmModel); } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs index 8f873f290..922e4971c 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs @@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateJsonSchema { public class JsonSchemaTests { - private readonly Schema schema = TestData.MixedSchema(); + private readonly Schema schema = TestUtils.MixedSchema(); [Fact] public void Should_build_json_schema() diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index b2b58a49d..06226aa4d 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => user.Claims) .Returns(new List { new Claim(SquidexClaimTypes.DisplayName, "me") }); - sut = new RuleEventFormatter(TestData.DefaultSerializer, urlGenerator); + sut = new RuleEventFormatter(TestUtils.DefaultSerializer, urlGenerator); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index 35226e184..7a36445b2 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules A.CallTo(() => ruleTriggerHandler.TriggerType) .Returns(typeof(ContentChangedTrigger)); - sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestData.DefaultSerializer, clock, typeNameRegistry); + sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestUtils.DefaultSerializer, clock, typeNameRegistry); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs similarity index 94% rename from tests/Squidex.Domain.Apps.Core.Tests/TestData.cs rename to tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs index ca33bbd45..bc22739d1 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs @@ -16,17 +16,18 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; using Xunit; namespace Squidex.Domain.Apps.Core { - public static class TestData + public static class TestUtils { public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); - private static IJsonSerializer CreateSerializer() + public static IJsonSerializer CreateSerializer(TypeNameHandling typeNameHandling = TypeNameHandling.Auto) { var typeNameRegistry = new TypeNameRegistry(); @@ -46,13 +47,15 @@ namespace Squidex.Domain.Apps.Core new NamedGuidIdConverter(), new NamedLongIdConverter(), new NamedStringIdConverter(), + new PropertiesBagConverter(), + new PropertiesBagConverter(), new RefTokenConverter(), new RolesConverter(), new RuleConverter(), new SchemaConverter(), new StringEnumConverter()), - TypeNameHandling = TypeNameHandling.Auto + TypeNameHandling = typeNameHandling }; return new NewtonsoftJsonSerializer(serializerSettings); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs index 1e5a0b3e5..45ae6d1af 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs @@ -8,7 +8,7 @@ using System; using System.Threading.Tasks; using FakeItEasy; -using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.States; using Xunit; @@ -33,13 +33,12 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_set_setting() { - await sut.SetAsync(new JObject(new JProperty("key", 15)).AsJ()); + await sut.SetAsync(JsonValue.Object().Add("key", 15).AsJ()); var actual = await sut.GetAsync(); var expected = - new JObject( - new JProperty("key", 15)); + JsonValue.Object().Add("key", 15); Assert.Equal(expected.ToString(), actual.Value.ToString()); } @@ -47,13 +46,12 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_set_root_value() { - await sut.SetAsync("key", ((JToken)123).AsJ()); + await sut.SetAsync("key", JsonValue.Create(123).AsJ()); var actual = await sut.GetAsync(); var expected = - new JObject( - new JProperty("key", 123)); + JsonValue.Object().Add("key", 123); Assert.Equal(expected.ToString(), actual.Value.ToString()); } @@ -61,12 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_remove_root_value() { - await sut.SetAsync("key", ((JToken)123).AsJ()); + await sut.SetAsync("key", JsonValue.Create(123).AsJ()); await sut.RemoveAsync("key"); var actual = await sut.GetAsync(); - var expected = new JObject(); + var expected = JsonValue.Object(); Assert.Equal(expected.ToString(), actual.Value.ToString()); } @@ -74,15 +72,13 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_set_nested_value() { - await sut.SetAsync("root.nested", ((JToken)123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); var actual = await sut.GetAsync(); var expected = - new JObject( - new JProperty("root", - new JObject( - new JProperty("nested", 123)))); + JsonValue.Object().Add("root", + JsonValue.Object().Add("nested", 123)); Assert.Equal(expected.ToString(), actual.Value.ToString()); } @@ -90,14 +86,14 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_remove_nested_value() { - await sut.SetAsync("root.nested", ((JToken)123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); await sut.RemoveAsync("root.nested"); var actual = await sut.GetAsync(); var expected = - new JObject( - new JProperty("root", new JObject())); + JsonValue.Object().Add("root", + JsonValue.Object()); Assert.Equal(expected.ToString(), actual.Value.ToString()); } @@ -105,9 +101,9 @@ namespace Squidex.Domain.Apps.Entities.Apps [Fact] public async Task Should_throw_exception_if_nested_not_an_object() { - await sut.SetAsync("root.nested", ((JToken)123).AsJ()); + await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ()); - await Assert.ThrowsAsync(() => sut.SetAsync("root.nested.value", ((JToken)123).AsJ())); + await Assert.ThrowsAsync(() => sut.SetAsync("root.nested.value", JsonValue.Create(123).AsJ())); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs index 9e18c3108..6d64aad03 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs @@ -8,11 +8,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using FakeItEasy; -using FluentAssertions; +using Squidex.Domain.Apps.Core; +using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.States; using Squidex.Infrastructure.Tasks; using Xunit; @@ -22,9 +23,26 @@ namespace Squidex.Domain.Apps.Entities.Backup public class BackupReaderWriterTests { private readonly IStreamNameResolver streamNameResolver = A.Fake(); + private readonly IJsonSerializer serializer = TestUtils.DefaultSerializer; + private readonly IEventDataFormatter formatter; + private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry(); + + [TypeName(nameof(MyEvent))] + public sealed class MyEvent : IEvent + { + public Guid GuidRaw { get; set; } + + public NamedId GuidNamed { get; set; } + + public Dictionary Values { get; set; } + } public BackupReaderWriterTests() { + typeNameRegistry.Map(typeof(MyEvent)); + + formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer); + A.CallTo(() => streamNameResolver.WithNewId(A.Ignored, A>.Ignored)) .ReturnsLazily(new Func, string>((stream, idGenerator) => stream + "^2")); } @@ -34,77 +52,112 @@ namespace Squidex.Domain.Apps.Entities.Backup { var stream = new MemoryStream(); - var sourceEvents = new List(); + var random = new Random(); + var randomGuids = new List(); + + for (var i = 0; i < 100; i++) + { + randomGuids.Add(Guid.NewGuid()); + } + + Guid RandomGuid() + { + return randomGuids[random.Next(randomGuids.Count)]; + } + + var sourceEvents = new List<(string Stream, Envelope Event)>(); - using (var writer = new BackupWriter(stream, true)) + for (var i = 0; i < 200; i++) { - for (var i = 0; i < 1000; i++) + var @event = new MyEvent { - var eventData = new EventData { Type = i.ToString(), Metadata = i, Payload = i }; + GuidNamed = new NamedId(RandomGuid(), $"name{i}"), + GuidRaw = RandomGuid(), + Values = new Dictionary + { + [RandomGuid()] = $"name{i}_1", + [RandomGuid()] = $"name{i}_1", + } + }; + + var envelope = Envelope.Create(@event); + + envelope.Headers.Set(RandomGuid().ToString(), i); + envelope.Headers.Set("Id", RandomGuid()); + envelope.Headers.Set("Index", i); + + sourceEvents.Add(($"My-{RandomGuid()}", envelope)); + } + + using (var writer = new BackupWriter(serializer, stream, true)) + { + foreach (var @event in sourceEvents) + { + var eventData = formatter.ToEventData(@event.Event, Guid.NewGuid(), true); var eventStored = new StoredEvent("S", "1", 2, eventData); - if (i % 17 == 0) - { - var localI = i; + var index = @event.Event.Headers["Index"].ToInt32(); - await writer.WriteBlobAsync(eventData.Type, innerStream => + if (index % 17 == 0) + { + await writer.WriteBlobAsync(index.ToString(), innerStream => { - innerStream.WriteByte((byte)localI); + innerStream.WriteByte((byte)index); return TaskHelper.Done; }); } - else if (i % 37 == 0) + else if (index % 37 == 0) { - await writer.WriteJsonAsync(eventData.Type, $"JSON_{i}"); + await writer.WriteJsonAsync(index.ToString(), $"JSON_{index}"); } writer.WriteEvent(eventStored); - - sourceEvents.Add(eventStored); } } stream.Position = 0; - var readEvents = new List(); + var targetEvents = new List<(string Stream, Envelope Event)>(); - using (var reader = new BackupReader(stream)) + using (var reader = new BackupReader(serializer, stream)) { - await reader.ReadEventsAsync(streamNameResolver, async @event => + await reader.ReadEventsAsync(streamNameResolver, formatter, async @event => { - var i = int.Parse(@event.Data.Type); + var index = @event.Event.Headers["Index"].ToInt32(); - if (i % 17 == 0) + if (index % 17 == 0) { - await reader.ReadBlobAsync(@event.Data.Type, innerStream => + await reader.ReadBlobAsync(index.ToString(), innerStream => { - var b = innerStream.ReadByte(); + var byteRead = innerStream.ReadByte(); - Assert.Equal((byte)i, b); + Assert.Equal((byte)index, byteRead); return TaskHelper.Done; }); } - else if (i % 37 == 0) + else if (index % 37 == 0) { - var j = await reader.ReadJsonAttachmentAsync(@event.Data.Type); + var json = await reader.ReadJsonAttachmentAsync(index.ToString()); - Assert.Equal($"JSON_{i}", j.ToString()); + Assert.Equal($"JSON_{index}", json); } - readEvents.Add(@event); + targetEvents.Add(@event); }); - } - var sourceEventsWithNewStreamName = - sourceEvents.Select(x => - new StoredEvent(streamNameResolver.WithNewId(x.StreamName, null), - x.EventPosition, - x.EventStreamNumber, - x.Data)).ToList(); + for (var i = 0; i < targetEvents.Count; i++) + { + var lhs = targetEvents[i].Event.To(); + var rhs = sourceEvents[i].Event.To(); - readEvents.Should().BeEquivalentTo(sourceEventsWithNewStreamName); + Assert.Equal(rhs.Payload.GuidRaw, reader.OldGuid(lhs.Payload.GuidRaw)); + Assert.Equal(rhs.Payload.GuidNamed.Id, reader.OldGuid(lhs.Payload.GuidNamed.Id)); + + Assert.Equal(rhs.Headers["Id"].ToGuid(), reader.OldGuid(lhs.Headers["Id"].ToGuid())); + } + } } } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs deleted file mode 100644 index fdab2731a..000000000 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs +++ /dev/null @@ -1,161 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Newtonsoft.Json.Linq; -using Xunit; - -namespace Squidex.Domain.Apps.Entities.Backup -{ - public class GuidMapperTests - { - private readonly Guid id1 = Guid.NewGuid(); - private readonly Guid id2 = Guid.NewGuid(); - private readonly GuidMapper map = new GuidMapper(); - - [Fact] - public void Should_map_guid_string_if_valid() - { - var result = map.NewGuidString(id1.ToString()); - - Assert.Equal(map.NewGuid(id1).ToString(), result); - } - - [Fact] - public void Should_return_null_if_mapping_invalid_guid_string() - { - var result = map.NewGuidString("invalid"); - - Assert.Null(result); - } - - [Fact] - public void Should_return_null_if_mapping_null_guid_string() - { - var result = map.NewGuidString(null); - - Assert.Null(result); - } - - [Fact] - public void Should_map_guid() - { - var result = map.NewGuids(id1); - - Assert.Equal(map.NewGuid(id1), result.Value()); - } - - [Fact] - public void Should_return_old_guid() - { - var newGuid = map.NewGuids(id1).Value(); - - Assert.Equal(id1, map.OldGuid(newGuid)); - } - - [Fact] - public void Should_map_guid_string() - { - var result = map.NewGuids(id1.ToString()); - - Assert.Equal(map.NewGuid(id1).ToString(), result.Value()); - } - - [Fact] - public void Should_map_named_id() - { - var result = map.NewGuids($"{id1},name"); - - Assert.Equal($"{map.NewGuid(id1)},name", result.Value()); - } - - [Fact] - public void Should_map_array_with_guid() - { - var obj = - new JObject( - new JProperty("k", - new JArray(id1, id1, id2))); - - map.NewGuids(obj); - - Assert.Equal(map.NewGuid(id1), obj["k"][0].Value()); - Assert.Equal(map.NewGuid(id1), obj["k"][1].Value()); - Assert.Equal(map.NewGuid(id2), obj["k"][2].Value()); - } - - [Fact] - public void Should_map_objects_with_guid_keys() - { - var obj = - new JObject( - new JProperty("k", - new JObject( - new JProperty(id1.ToString(), id1), - new JProperty(id2.ToString(), id2)))); - - map.NewGuids(obj); - - Assert.Equal(map.NewGuid(id1), obj["k"].Value(map.NewGuid(id1).ToString())); - Assert.Equal(map.NewGuid(id2), obj["k"].Value(map.NewGuid(id2).ToString())); - } - - [Fact] - public void Should_map_objects_with_guid() - { - var obj = - new JObject( - new JProperty("k", - new JObject( - new JProperty("v1", id1), - new JProperty("v2", id1), - new JProperty("v3", id2)))); - - map.NewGuids(obj); - - Assert.Equal(map.NewGuid(id1), obj["k"].Value("v1")); - Assert.Equal(map.NewGuid(id1), obj["k"].Value("v2")); - Assert.Equal(map.NewGuid(id2), obj["k"].Value("v3")); - } - - [Fact] - public void Should_map_objects_with_guid_string() - { - var obj = - new JObject( - new JProperty("k", - new JObject( - new JProperty("v1", id1.ToString()), - new JProperty("v2", id1.ToString()), - new JProperty("v3", id2.ToString())))); - - map.NewGuids(obj); - - Assert.Equal(map.NewGuid(id1).ToString(), obj["k"].Value("v1")); - Assert.Equal(map.NewGuid(id1).ToString(), obj["k"].Value("v2")); - Assert.Equal(map.NewGuid(id2).ToString(), obj["k"].Value("v3")); - } - - [Fact] - public void Should_map_objects_with_named_id() - { - var obj = - new JObject( - new JProperty("k", - new JObject( - new JProperty("v1", $"{id1},v1"), - new JProperty("v2", $"{id1},v2"), - new JProperty("v3", $"{id2},v3")))); - - map.NewGuids(obj); - - Assert.Equal($"{map.NewGuid(id1).ToString()},v1", obj["k"].Value("v1")); - Assert.Equal($"{map.NewGuid(id1).ToString()},v2", obj["k"].Value("v2")); - Assert.Equal($"{map.NewGuid(id2).ToString()},v3", obj["k"].Value("v3")); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs index ffa580607..56bddfe27 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs @@ -41,10 +41,10 @@ namespace Squidex.Domain.Apps.Entities.Contents new NamedContentData() .AddField("my-field1", new ContentFieldData() - .AddValue(null)) + .AddValue("iv", null)) .AddField("my-field2", new ContentFieldData() - .AddValue(1)); + .AddValue("iv", 1)); private readonly NamedContentData data = new NamedContentData() .AddField("my-field1", diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index f2a90e843..ff0a07365 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; using FakeItEasy; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure; using Xunit; @@ -77,9 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = asset.Id, version = 1, - created = asset.Created.ToDateTimeUtc(), + created = asset.Created, createdBy = "subject:user1", - lastModified = asset.LastModified.ToDateTimeUtc(), + lastModified = asset.LastModified, lastModifiedBy = "subject:user2", url = $"assets/{asset.Id}", thumbnailUrl = $"assets/{asset.Id}?width=100", @@ -147,9 +146,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = asset.Id, version = 1, - created = asset.Created.ToDateTimeUtc(), + created = asset.Created, createdBy = "subject:user1", - lastModified = asset.LastModified.ToDateTimeUtc(), + lastModified = asset.LastModified, lastModifiedBy = "subject:user2", url = $"assets/{asset.Id}", thumbnailUrl = $"assets/{asset.Id}?width=100", @@ -211,9 +210,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = asset.Id, version = 1, - created = asset.Created.ToDateTimeUtc(), + created = asset.Created, createdBy = "subject:user1", - lastModified = asset.LastModified.ToDateTimeUtc(), + lastModified = asset.LastModified, lastModifiedBy = "subject:user2", url = $"assets/{asset.Id}", thumbnailUrl = $"assets/{asset.Id}?width=100", @@ -298,9 +297,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = content.Id, version = 1, - created = content.Created.ToDateTimeUtc(), + created = content.Created, createdBy = "subject:user1", - lastModified = content.LastModified.ToDateTimeUtc(), + lastModified = content.LastModified, lastModifiedBy = "subject:user2", status = "DRAFT", url = $"contents/my-schema/{content.Id}", @@ -320,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }, myDatetime = new { - iv = content.LastModified.ToDateTimeUtc() + iv = content.LastModified }, myJson = new { @@ -440,9 +439,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = content.Id, version = 1, - created = content.Created.ToDateTimeUtc(), + created = content.Created, createdBy = "subject:user1", - lastModified = content.LastModified.ToDateTimeUtc(), + lastModified = content.LastModified, lastModifiedBy = "subject:user2", status = "DRAFT", url = $"contents/my-schema/{content.Id}", @@ -462,7 +461,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }, myDatetime = new { - iv = content.LastModified.ToDateTimeUtc() + iv = content.LastModified }, myJson = new { @@ -560,9 +559,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { id = content.Id, version = 1, - created = content.Created.ToDateTimeUtc(), + created = content.Created, createdBy = "subject:user1", - lastModified = content.LastModified.ToDateTimeUtc(), + lastModified = content.LastModified, lastModifiedBy = "subject:user2", status = "DRAFT", url = $"contents/my-schema/{content.Id}", @@ -582,7 +581,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }, myDatetime = new { - iv = content.LastModified.ToDateTimeUtc() + iv = content.LastModified }, myJson = new { @@ -819,9 +818,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); - var json = JToken.FromObject(result); + var json = serializer.Serialize(result); - Assert.Null(json["data"]); + Assert.Contains("\"data\":null", json); } private QueryContext MatchsAssetContext() diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs index d46676a0e..6988c899d 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs @@ -12,7 +12,6 @@ using FakeItEasy; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using NodaTime.Extensions; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Apps; @@ -23,6 +22,8 @@ using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Contents.TestData; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Xunit; #pragma warning disable SA1311 // Static readonly fields must begin with upper-case letter @@ -39,6 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL protected readonly IContentQueryService contentQuery = A.Fake(); protected readonly IAssetQueryService assetQuery = A.Fake(); protected readonly ISchemaEntity schema = A.Fake(); + protected readonly IJsonSerializer serializer = TestUtils.CreateSerializer(TypeNameHandling.None); protected readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); protected readonly IAppProvider appProvider = A.Fake(); protected readonly IAppEntity app = A.Dummy(); @@ -106,40 +108,40 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL .AddValue("de", "value")) .AddField("my-assets", new ContentFieldData() - .AddValue("iv", JToken.FromObject(new[] { assetId }))) + .AddValue("iv", JsonValue.Array(assetId.ToString()))) .AddField("my-number", new ContentFieldData() - .AddValue("iv", 1)) + .AddValue("iv", 1.0)) .AddField("my-boolean", new ContentFieldData() .AddValue("iv", true)) .AddField("my-datetime", new ContentFieldData() - .AddValue("iv", now.ToDateTimeUtc())) + .AddValue("iv", now)) .AddField("my-tags", new ContentFieldData() - .AddValue("iv", JToken.FromObject(new[] { "tag1", "tag2" }))) + .AddValue("iv", JsonValue.Array("tag1", "tag2"))) .AddField("my-references", new ContentFieldData() - .AddValue("iv", JToken.FromObject(new[] { refId }))) + .AddValue("iv", JsonValue.Array(refId.ToString()))) .AddField("my-geolocation", new ContentFieldData() - .AddValue("iv", JToken.FromObject(new { latitude = 10, longitude = 20 }))) + .AddValue("iv", JsonValue.Object().Add("latitude", 10).Add("longitude", 20))) .AddField("my-json", new ContentFieldData() - .AddValue("iv", JToken.FromObject(new { value = 1 }))) + .AddValue("iv", JsonValue.Object().Add("value", 1))) .AddField("my-localized", new ContentFieldData() .AddValue("de-DE", "de-DE")) .AddField("my-array", new ContentFieldData() - .AddValue("iv", new JArray( - new JObject( - new JProperty("nested-boolean", true), - new JProperty("nested-number", 1)), - new JObject( - new JProperty("nested-boolean", false), - new JProperty("nested-number", 2))))); + .AddValue("iv", JsonValue.Array( + JsonValue.Object() + .Add("nested-boolean", true) + .Add("nested-number", 1), + JsonValue.Object() + .Add("nested-boolean", false) + .Add("nested-number", 2)))); var content = new ContentEntity { @@ -179,22 +181,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL return asset; } - protected static void AssertResult(object expected, (bool HasErrors, object Response) result, bool checkErrors = true) + protected void AssertResult(object expected, (bool HasErrors, object Response) result, bool checkErrors = true) { if (checkErrors && result.HasErrors) { throw new InvalidOperationException(Serialize(result)); } - var resultJson = JsonConvert.SerializeObject(result.Response, Formatting.Indented); - var expectJson = JsonConvert.SerializeObject(expected, Formatting.Indented); + var resultJson = serializer.Serialize(result.Response, true); + var expectJson = serializer.Serialize(expected, true); Assert.Equal(expectJson, resultJson); } - private static string Serialize((bool HasErrors, object Response) result) + private string Serialize((bool HasErrors, object Response) result) { - return JsonConvert.SerializeObject(result); + return serializer.Serialize(result); } } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs index 1705ca24d..68a3a2eb8 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; using FakeItEasy; -using Newtonsoft.Json.Linq; using NodaTime; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules; @@ -49,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Rules [InlineData(4, 0, RuleResult.Failed, RuleJobResult.Failed)] public async Task Should_set_next_attempt_based_on_num_calls(int calls, int minutes, RuleResult result, RuleJobResult jobResult) { - var actionData = new JObject(); + var actionData = "{}"; var actionName = "MyAction"; var @event = CreateEvent(calls, actionName, actionData); @@ -73,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Rules .MustHaveHappened(); } - private IRuleEventEntity CreateEvent(int numCalls, string actionName, JObject actionData) + private IRuleEventEntity CreateEvent(int numCalls, string actionName, string actionData) { var @event = A.Fake(); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index d88751b1e..79ec34354 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -18,6 +18,7 @@ + diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs index 7ad64cdb2..bec3549d1 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Globalization; using NodaTime; using Xunit; @@ -15,7 +14,6 @@ namespace Squidex.Infrastructure.EventSourcing public class EnvelopeExtensionsTests { private readonly Envelope sut = new Envelope(string.Empty); - private readonly CultureInfo culture = CultureInfo.InvariantCulture; [Fact] public void Should_set_and_get_timestamp() @@ -25,7 +23,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetTimestamp(timestamp); Assert.Equal(timestamp, sut.Headers.Timestamp()); - Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant(culture)); + Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant()); } [Fact] @@ -36,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetCommitId(commitId); Assert.Equal(commitId, sut.Headers.CommitId()); - Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid(culture)); + Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid()); } [Fact] @@ -47,7 +45,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetEventId(commitId); Assert.Equal(commitId, sut.Headers.EventId()); - Assert.Equal(commitId, sut.Headers["EventId"].ToGuid(culture)); + Assert.Equal(commitId, sut.Headers["EventId"].ToGuid()); } [Fact] @@ -58,7 +56,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetAggregateId(commitId); Assert.Equal(commitId, sut.Headers.AggregateId()); - Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid(culture)); + Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid()); } [Fact] @@ -80,7 +78,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetEventStreamNumber(eventStreamNumber); Assert.Equal(eventStreamNumber, sut.Headers.EventStreamNumber()); - Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64(culture)); + Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64()); } } } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index da62e3e23..7f5bd174c 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -79,7 +79,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => persistence.WriteSnapshotAsync(A.Ignored)) .Invokes(new Action(s => state = s)); - A.CallTo(() => formatter.Parse(eventData, true)).Returns(envelope); + A.CallTo(() => formatter.Parse(eventData, true, null)).Returns(envelope); sut = new MyEventConsumerGrain( x => eventConsumer, @@ -192,7 +192,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_ignore_old_events() { - A.CallTo(() => formatter.Parse(eventData, true)) + A.CallTo(() => formatter.Parse(eventData, true, null)) .Throws(new TypeNameNotFoundException()); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); @@ -326,7 +326,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var ex = new InvalidOperationException(); - A.CallTo(() => formatter.Parse(eventData, true)) + A.CallTo(() => formatter.Parse(eventData, true, null)) .Throws(ex); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); diff --git a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs b/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs index defe9c236..e60eec701 100644 --- a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs +++ b/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs @@ -1,12 +1,11 @@ // ========================================================================== // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) +// Copyright () Squidex UG (haftungsbeschränkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; -using System.Globalization; using System.Linq; using Microsoft.CSharp.RuntimeBinder; using NodaTime; @@ -17,7 +16,6 @@ namespace Squidex.Infrastructure { public class PropertiesBagTests { - private readonly CultureInfo c = CultureInfo.InvariantCulture; private readonly PropertiesBag bag = new PropertiesBag(); private readonly dynamic dynamicBag; @@ -52,7 +50,7 @@ namespace Squidex.Infrastructure Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue); } - Assert.Equal(bag["Key5"].ToGuid(c), output["Key5"].ToGuid(c)); + Assert.Equal(bag["Key5"].ToGuid(), output["Key5"].ToGuid()); } [Fact] @@ -84,7 +82,7 @@ namespace Squidex.Infrastructure Assert.True(bag.Contains("NewKey")); Assert.Equal(1, bag.Count); - Assert.Equal(123, bag["NewKey"].ToInt64(c)); + Assert.Equal(123, bag["NewKey"].ToInt64()); Assert.False(bag.Contains("OldKey")); } @@ -172,7 +170,7 @@ namespace Squidex.Infrastructure { bag.Set("Key", "abc"); - Assert.Throws(() => bag["Key"].ToInt64(CultureInfo.InvariantCulture)); + Assert.Throws(() => bag["Key"].ToInt64()); } [Fact] @@ -212,7 +210,7 @@ namespace Squidex.Infrastructure { bag.Set("Key", long.MaxValue); - Assert.Throws(() => bag["Key"].ToInt32(c)); + Assert.Throws(() => bag["Key"].ToInt32()); } [Fact] @@ -236,7 +234,7 @@ namespace Squidex.Infrastructure { bag.Set("Key", double.MaxValue); - Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle(c)); + Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle()); } [Fact] @@ -340,7 +338,7 @@ namespace Squidex.Infrastructure { bag.Set("Key", SystemClock.Instance.GetCurrentInstant()); - Assert.Throws(() => bag["Key"].ToGuid(CultureInfo.InvariantCulture)); + Assert.Throws(() => bag["Key"].ToGuid()); } private void AssertNumber() @@ -366,8 +364,8 @@ namespace Squidex.Infrastructure private void AssertBoolean(bool expected) { - Assert.Equal(expected, bag["Key"].ToBoolean(c)); - Assert.Equal(expected, bag["Key"].ToNullableBoolean(c)); + Assert.Equal(expected, bag["Key"].ToBoolean()); + Assert.Equal(expected, bag["Key"].ToNullableBoolean()); Assert.Equal(expected, (bool)dynamicBag.Key); Assert.Equal(expected, (bool?)dynamicBag.Key); @@ -375,8 +373,8 @@ namespace Squidex.Infrastructure private void AssertInstant(Instant expected) { - Assert.Equal(expected, bag["Key"].ToInstant(c)); - Assert.Equal(expected, bag["Key"].ToNullableInstant(c).Value); + Assert.Equal(expected, bag["Key"].ToInstant()); + Assert.Equal(expected, bag["Key"].ToNullableInstant().Value); Assert.Equal(expected, (Instant)dynamicBag.Key); Assert.Equal(expected, (Instant?)dynamicBag.Key); @@ -384,8 +382,8 @@ namespace Squidex.Infrastructure private void AssertGuid(Guid expected) { - Assert.Equal(expected, bag["Key"].ToGuid(c)); - Assert.Equal(expected, bag["Key"].ToNullableGuid(c)); + Assert.Equal(expected, bag["Key"].ToGuid()); + Assert.Equal(expected, bag["Key"].ToNullableGuid()); Assert.Equal(expected, (Guid)dynamicBag.Key); Assert.Equal(expected, (Guid?)dynamicBag.Key); @@ -393,8 +391,8 @@ namespace Squidex.Infrastructure private void AssertDouble(double expected) { - Assert.Equal(expected, bag["Key"].ToDouble(c)); - Assert.Equal(expected, bag["Key"].ToNullableDouble(c)); + Assert.Equal(expected, bag["Key"].ToDouble()); + Assert.Equal(expected, bag["Key"].ToNullableDouble()); Assert.Equal(expected, (double)dynamicBag.Key); Assert.Equal(expected, (double?)dynamicBag.Key); @@ -402,8 +400,8 @@ namespace Squidex.Infrastructure private void AssertSingle(float expected) { - Assert.Equal(expected, bag["Key"].ToSingle(c)); - Assert.Equal(expected, bag["Key"].ToNullableSingle(c)); + Assert.Equal(expected, bag["Key"].ToSingle()); + Assert.Equal(expected, bag["Key"].ToNullableSingle()); Assert.Equal(expected, (float)dynamicBag.Key); Assert.Equal(expected, (float?)dynamicBag.Key); @@ -411,8 +409,8 @@ namespace Squidex.Infrastructure private void AssertInt32(long expected) { - Assert.Equal(expected, bag["Key"].ToInt64(c)); - Assert.Equal(expected, bag["Key"].ToNullableInt64(c)); + Assert.Equal(expected, bag["Key"].ToInt64()); + Assert.Equal(expected, bag["Key"].ToNullableInt64()); Assert.Equal(expected, (long)dynamicBag.Key); Assert.Equal(expected, (long?)dynamicBag.Key); @@ -420,8 +418,8 @@ namespace Squidex.Infrastructure private void AssertInt64(int expected) { - Assert.Equal(expected, bag["Key"].ToInt64(c)); - Assert.Equal(expected, bag["Key"].ToNullableInt64(c)); + Assert.Equal(expected, bag["Key"].ToInt64()); + Assert.Equal(expected, bag["Key"].ToNullableInt64()); Assert.Equal(expected, (int)dynamicBag.Key); Assert.Equal(expected, (int?)dynamicBag.Key); diff --git a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index 5da0ea0c2..e7223fca2 100644 --- a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -61,7 +61,7 @@ namespace Squidex.Infrastructure.States A.CallTo(() => eventStore.QueryAsync(key, 0)) .Returns(new List { storedEvent }); - A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, true)) + A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, true, null)) .Throws(new TypeNameNotFoundException()); var persistedEvents = new List(); @@ -256,7 +256,7 @@ namespace Squidex.Infrastructure.States eventsStored.Add(eventStored); - A.CallTo(() => eventDataFormatter.Parse(eventData, true)) + A.CallTo(() => eventDataFormatter.Parse(eventData, true, null)) .Returns(new Envelope(@event)); i++; diff --git a/tools/Migrate_01/Rebuilder.cs b/tools/Migrate_01/Rebuilder.cs index be45577c0..6214e37b4 100644 --- a/tools/Migrate_01/Rebuilder.cs +++ b/tools/Migrate_01/Rebuilder.cs @@ -25,6 +25,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.States; namespace Migrate_01 @@ -33,18 +34,21 @@ namespace Migrate_01 { private readonly FieldRegistry fieldRegistry; private readonly ILocalCache localCache; + private readonly IJsonSerializer serializer; private readonly IStore store; private readonly IEventStore eventStore; public Rebuilder( FieldRegistry fieldRegistry, ILocalCache localCache, + IJsonSerializer serializer, IStore store, IEventStore eventStore) { this.fieldRegistry = fieldRegistry; this.eventStore = eventStore; this.localCache = localCache; + this.serializer = serializer; this.store = store; } @@ -104,7 +108,9 @@ namespace Migrate_01 await eventStore.QueryAsync(async storedEvent => { - var id = Guid.Parse(storedEvent.Data.Metadata.Value(CommonHeaders.AggregateId)); + var headers = serializer.Deserialize(storedEvent.Data.Metadata); + + var id = headers.AggregateId(); if (handledIds.Add(id)) { From 30a8025389fcb8bcb7164562f383a02756a6b89a Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 26 Nov 2018 16:11:20 +0100 Subject: [PATCH 04/15] Build errors fixed. --- .../Commands/DomainObjectGrainBase.cs | 2 +- .../Models/Fields/BooleanFieldPropertiesDto.cs | 3 --- .../Models/Fields/DateTimeFieldPropertiesDto.cs | 4 ---- .../Models/Fields/GeolocationFieldPropertiesDto.cs | 3 --- .../Models/Fields/NumberFieldPropertiesDto.cs | 3 --- .../Models/Fields/StringFieldPropertiesDto.cs | 3 --- .../Models/Fields/TagsFieldPropertiesDto.cs | 3 --- .../Areas/Api/Controllers/UI/UIController.cs | 3 ++- .../Pipeline/Diagnostics/HealthCheckMiddleware.cs | 14 ++++++++------ 9 files changed, 11 insertions(+), 27 deletions(-) diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs b/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs index 1f6044d3d..548a82c33 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectGrainBase.cs @@ -221,7 +221,7 @@ namespace Squidex.Infrastructure.Commands { var result = await ExecuteAsync(command.Value); - return result.AsJ(); + return result; } protected abstract Task ExecuteAsync(IAggregateCommand command); diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/BooleanFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/BooleanFieldPropertiesDto.cs index 45964540e..7b92fccdb 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/BooleanFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/BooleanFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Reflection; @@ -29,7 +27,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public BooleanFieldEditor Editor { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/DateTimeFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/DateTimeFieldPropertiesDto.cs index f5f7ba515..0340f7313 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/DateTimeFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/DateTimeFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using NodaTime; using Squidex.Domain.Apps.Core.Schemas; @@ -35,13 +33,11 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public DateTimeFieldEditor Editor { get; set; } /// /// The calculated default value for the field value. /// - [JsonConverter(typeof(StringEnumConverter))] public DateTimeCalculatedDefaultValue? CalculatedDefaultValue { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/GeolocationFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/GeolocationFieldPropertiesDto.cs index 898b2df3e..4ba929093 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/GeolocationFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/GeolocationFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Reflection; @@ -24,7 +22,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public GeolocationFieldEditor Editor { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs index 4a7861b04..9e3670ff0 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; @@ -45,7 +43,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public NumberFieldEditor Editor { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs index 0722fec99..483f93bc8 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; @@ -55,7 +53,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public StringFieldEditor Editor { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs index 3a7727107..777103f7c 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using NJsonSchema.Annotations; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.Collections; @@ -35,7 +33,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The editor that is used to manage this field. /// - [JsonConverter(typeof(StringEnumConverter))] public TagsFieldEditor Editor { get; set; } public override FieldProperties ToProperties() diff --git a/src/Squidex/Areas/Api/Controllers/UI/UIController.cs b/src/Squidex/Areas/Api/Controllers/UI/UIController.cs index d5c486d93..6d34ef4c3 100644 --- a/src/Squidex/Areas/Api/Controllers/UI/UIController.cs +++ b/src/Squidex/Areas/Api/Controllers/UI/UIController.cs @@ -14,6 +14,7 @@ using Squidex.Config; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Extensions.Actions.Twitter; using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.Orleans; using Squidex.Pipeline; namespace Squidex.Areas.Api.Controllers.UI @@ -78,7 +79,7 @@ namespace Squidex.Areas.Api.Controllers.UI [ApiPermission] public async Task PutSetting(string app, string key, [FromBody] UpdateSettingDto request) { - await grainFactory.GetGrain(AppId).SetAsync(key, request.Value); + await grainFactory.GetGrain(AppId).SetAsync(key, request.Value.AsJ()); return NoContent(); } diff --git a/src/Squidex/Pipeline/Diagnostics/HealthCheckMiddleware.cs b/src/Squidex/Pipeline/Diagnostics/HealthCheckMiddleware.cs index 2025dbbfb..00725377d 100644 --- a/src/Squidex/Pipeline/Diagnostics/HealthCheckMiddleware.cs +++ b/src/Squidex/Pipeline/Diagnostics/HealthCheckMiddleware.cs @@ -11,9 +11,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.Diagnostics; +using Squidex.Infrastructure.Json; namespace Squidex.Pipeline.Diagnostics { @@ -23,19 +23,19 @@ namespace Squidex.Pipeline.Diagnostics private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2); private readonly Dictionary healthChecks; - private readonly JsonSerializerSettings serializerSettings; + private readonly IJsonSerializer serializer; private readonly RequestDelegate next; private readonly List scopes; - public HealthCheckMiddleware(IEnumerable healthChecks, JsonSerializerSettings serializerSettings, RequestDelegate next, string scopes) + public HealthCheckMiddleware(IEnumerable healthChecks, IJsonSerializer serializer, RequestDelegate next, string scopes) { Guard.NotNull(healthChecks, nameof(healthChecks)); - Guard.NotNull(serializerSettings, nameof(serializerSettings)); + Guard.NotNull(serializer, nameof(serializer)); this.healthChecks = healthChecks.ToDictionary(GetName); this.next = next; + this.serializer = serializer; this.scopes = SplitScopes(scopes); - this.serializerSettings = serializerSettings; } public async Task Invoke(HttpContext context) @@ -58,7 +58,9 @@ namespace Squidex.Pipeline.Diagnostics var response = results.ToDictionary(x => x.Name, x => x.Result); - await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = response }, Formatting.Indented, serializerSettings)); + var json = serializer.Serialize(new { status = response }); + + await context.Response.WriteAsync(json); } } else From e35da8de8b05c7de70c0e6871854899b9e9ea366 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 26 Nov 2018 17:53:03 +0100 Subject: [PATCH 05/15] Fix registration. --- src/Squidex/Config/Domain/SerializationServices.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 10596cbbe..2f68fccc3 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -20,6 +20,7 @@ using Squidex.Domain.Apps.Events; using Squidex.Extensions.Actions; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; namespace Squidex.Config.Domain @@ -87,7 +88,7 @@ namespace Squidex.Config.Domain services.AddSingleton(DefaultJsonSerializer); services.AddSingleton(TypeNameRegistry); - services.AddSingleton(new NewtonsoftJsonSerializer(DefaultJsonSettings)); + services.AddSingleton(new NewtonsoftJsonSerializer(DefaultJsonSettings)); return services; } From 5378880556170a631b52c79e14f326ddc3f629c4 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 26 Nov 2018 19:47:25 +0100 Subject: [PATCH 06/15] Removed properties bag. --- .../HandleRules/RuleService.cs | 2 +- .../Backup/BackupReader.cs | 42 +- .../SquidexHeaderExtensions.cs | 4 +- .../EventSourcing/Formatter.cs | 21 +- .../EventSourcing/GetEventStore.cs | 14 +- .../GetEventStoreSubscription.cs | 9 +- .../EventSourcing/MongoEvent.cs | 9 +- .../MongoDb/BsonJsonConverter.cs | 155 ------- .../DefaultEventDataFormatter.cs | 20 +- .../EventSourcing/EnvelopeExtensions.cs | 46 +- .../EventSourcing/EnvelopeHeaders.cs | 25 +- .../EventSourcing/EventData.cs | 19 +- .../Json/Newtonsoft/PropertiesBagConverter.cs | 78 ---- .../Json/Objects/JsonObject.cs | 16 +- src/Squidex.Infrastructure/PropertiesBag.cs | 112 ----- src/Squidex.Infrastructure/PropertyValue.cs | 235 ---------- .../Config/Domain/EventStoreServices.cs | 3 +- .../Config/Domain/SerializationServices.cs | 2 - .../TestUtils.cs | 2 - .../Backup/BackupReaderWriterTests.cs | 12 +- .../DefaultEventDataFormatterTests.cs | 4 +- .../EventSourcing/EnvelopeExtensionsTests.cs | 10 +- .../EventSourcing/EnvelopeHeaderTests.cs | 19 +- .../Grains/EventConsumerGrainTests.cs | 2 +- .../EventSourcing/RetrySubscriptionTests.cs | 4 +- .../MongoDb/BsonConverterTests.cs | 38 -- .../PropertiesBagTests.cs | 428 ------------------ .../States/PersistenceEventSourcingTests.cs | 4 +- .../TestHelpers/JsonHelper.cs | 2 - tools/Migrate_01/Rebuilder.cs | 2 +- 30 files changed, 182 insertions(+), 1157 deletions(-) delete mode 100644 src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConverter.cs delete mode 100644 src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs delete mode 100644 src/Squidex.Infrastructure/PropertiesBag.cs delete mode 100644 src/Squidex.Infrastructure/PropertyValue.cs delete mode 100644 tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index 8a5dbac53..d6f4fa5a3 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -93,7 +93,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules var now = clock.GetCurrentInstant(); var eventTime = - @event.Headers.Contains(CommonHeaders.Timestamp) ? + @event.Headers.ContainsKey(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now; diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs index b6cce2231..2c3560334 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs @@ -9,12 +9,16 @@ using System; using System.IO; using System.IO.Compression; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Backup.Helpers; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.States; +#pragma warning disable SA1401 // Fields must be private + namespace Squidex.Domain.Apps.Entities.Backup { public sealed class BackupReader : DisposableObjectBase @@ -25,6 +29,36 @@ namespace Squidex.Domain.Apps.Entities.Backup private int readEvents; private int readAttachments; + private sealed class ComaptibleStoredEvent + { + [JsonProperty] + public string StreamName; + + [JsonProperty] + public string EventPosition; + + [JsonProperty] + public long EventStreamNumber; + + [JsonProperty] + public CompatibleEventData Data; + } + + private sealed class CompatibleEventData + { + [JsonProperty] + public string Type; + + [JsonProperty] + public JRaw Payload; + + [JsonProperty] + public EnvelopeHeaders Headers; + + [JsonProperty] + public EnvelopeHeaders Metadata; + } + public int ReadEvents { get { return readEvents; } @@ -117,10 +151,14 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = eventEntry.Open()) { - var storedEvent = serializer.Deserialize(stream); + var storedEvent = serializer.Deserialize(stream); + + var src = storedEvent.Data; + + var data = new EventData(src.Type, src.Headers ?? src.Metadata, src.Payload.ToString()); var eventStream = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidOrNull); - var eventEnvelope = formatter.Parse(storedEvent.Data, true, guidMapper.NewGuidOrValue); + var eventEnvelope = formatter.Parse(data, true, guidMapper.NewGuidOrValue); await handler((eventStream, eventEnvelope)); } diff --git a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs index 007b19de8..d65fcd3ff 100644 --- a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs +++ b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs @@ -14,12 +14,12 @@ namespace Squidex.Domain.Apps.Events { public static Guid AppId(this EnvelopeHeaders headers) { - return headers[SquidexHeaders.AppId].ToGuid(); + return headers.GetGuid(SquidexHeaders.AppId); } public static Envelope SetAppId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Set(SquidexHeaders.AppId, value); + envelope.Headers.Add(SquidexHeaders.AppId, value); return envelope; } diff --git a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs index 6fdfaa288..8a2dd111f 100644 --- a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs +++ b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/Formatter.cs @@ -8,20 +8,23 @@ using System; using System.Text; using EventStore.ClientAPI; +using Squidex.Infrastructure.Json; using EventStoreData = EventStore.ClientAPI.EventData; namespace Squidex.Infrastructure.EventSourcing { public static class Formatter { - public static StoredEvent Read(ResolvedEvent resolvedEvent) + public static StoredEvent Read(ResolvedEvent resolvedEvent, IJsonSerializer serializer) { var @event = resolvedEvent.Event; - var body = Encoding.UTF8.GetString(@event.Data); - var meta = Encoding.UTF8.GetString(@event.Metadata); + var metadata = Encoding.UTF8.GetString(@event.Data); - var eventData = new EventData { Type = @event.EventType, Payload = body, Metadata = meta }; + var headersJson = Encoding.UTF8.GetString(@event.Metadata); + var headers = serializer.Deserialize(headersJson); + + var eventData = new EventData(@event.EventType, headers, metadata); return new StoredEvent( @event.EventStreamId, @@ -30,12 +33,14 @@ namespace Squidex.Infrastructure.EventSourcing eventData); } - public static EventStoreData Write(EventData eventData) + public static EventStoreData Write(EventData eventData, IJsonSerializer serializer) { - var body = Encoding.UTF8.GetBytes(eventData.Payload); - var meta = Encoding.UTF8.GetBytes(eventData.Metadata); + var payload = Encoding.UTF8.GetBytes(eventData.Payload); + + var headersJson = serializer.Serialize(eventData.Headers); + var headersBytes = Encoding.UTF8.GetBytes(headersJson); - return new EventStoreData(Guid.NewGuid(), eventData.Type, true, body, meta); + return new EventStoreData(Guid.NewGuid(), eventData.Type, true, payload, headersBytes); } } } diff --git a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs index 9ea5bc45c..68ceef876 100644 --- a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs +++ b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using EventStore.ClientAPI; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; namespace Squidex.Infrastructure.EventSourcing @@ -20,14 +21,17 @@ namespace Squidex.Infrastructure.EventSourcing private const int WritePageSize = 500; private const int ReadPageSize = 500; private readonly IEventStoreConnection connection; + private readonly IJsonSerializer serializer; private readonly string prefix; private readonly ProjectionClient projectionClient; - public GetEventStore(IEventStoreConnection connection, string prefix, string projectionHost) + public GetEventStore(IEventStoreConnection connection, IJsonSerializer serializer, string prefix, string projectionHost) { Guard.NotNull(connection, nameof(connection)); + Guard.NotNull(serializer, nameof(serializer)); this.connection = connection; + this.serializer = serializer; this.prefix = prefix?.Trim(' ', '-').WithFallback("squidex"); @@ -50,7 +54,7 @@ namespace Squidex.Infrastructure.EventSourcing public IEventSubscription CreateSubscription(IEventSubscriber subscriber, string streamFilter, string position = null) { - return new GetEventStoreSubscription(connection, subscriber, projectionClient, position, streamFilter); + return new GetEventStoreSubscription(connection, subscriber, serializer, projectionClient, position, streamFilter); } public Task CreateIndexAsync(string property) @@ -95,7 +99,7 @@ namespace Squidex.Infrastructure.EventSourcing foreach (var resolved in currentSlice.Events) { - var storedEvent = Formatter.Read(resolved); + var storedEvent = Formatter.Read(resolved, serializer); await callback(storedEvent); } @@ -123,7 +127,7 @@ namespace Squidex.Infrastructure.EventSourcing foreach (var resolved in currentSlice.Events) { - var storedEvent = Formatter.Read(resolved); + var storedEvent = Formatter.Read(resolved, serializer); result.Add(storedEvent); } @@ -164,7 +168,7 @@ namespace Squidex.Infrastructure.EventSourcing return; } - var eventsToSave = events.Select(Formatter.Write).ToList(); + var eventsToSave = events.Select(x => Formatter.Write(x, serializer)).ToList(); if (eventsToSave.Count < WritePageSize) { diff --git a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStoreSubscription.cs b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStoreSubscription.cs index e77d4a204..a1d3faf01 100644 --- a/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStoreSubscription.cs +++ b/src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStoreSubscription.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using EventStore.ClientAPI; using EventStore.ClientAPI.Exceptions; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Tasks; namespace Squidex.Infrastructure.EventSourcing @@ -16,12 +17,14 @@ namespace Squidex.Infrastructure.EventSourcing { private readonly IEventStoreConnection connection; private readonly IEventSubscriber subscriber; + private readonly IJsonSerializer serializer; private readonly EventStoreCatchUpSubscription subscription; private readonly long? position; public GetEventStoreSubscription( IEventStoreConnection connection, IEventSubscriber subscriber, + IJsonSerializer serializer, ProjectionClient projectionClient, string position, string streamFilter) @@ -34,8 +37,10 @@ namespace Squidex.Infrastructure.EventSourcing var streamName = projectionClient.CreateProjectionAsync(streamFilter).Result; + this.serializer = serializer; this.subscriber = subscriber; - this.subscription = SubscribeToStream(streamName); + + subscription = SubscribeToStream(streamName); } public Task StopAsync() @@ -56,7 +61,7 @@ namespace Squidex.Infrastructure.EventSourcing return connection.SubscribeToStreamFrom(streamName, position, settings, (s, e) => { - var storedEvent = Formatter.Read(e); + var storedEvent = Formatter.Read(e, serializer); subscriber.OnEventAsync(this, storedEvent).Wait(); }, null, diff --git a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs index 8748ba8b1..70e4d3397 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventSourcing/MongoEvent.cs @@ -21,18 +21,19 @@ namespace Squidex.Infrastructure.EventSourcing [BsonRequired] public string Payload { get; set; } - [BsonElement] + [BsonElement("Metadata")] [BsonRequired] - public BsonDocument Metadata { get; set; } + [BsonJson] + public EnvelopeHeaders Headers { get; set; } public static MongoEvent FromEventData(EventData data) { - return new MongoEvent { Type = data.Type, Metadata = BsonDocument.Parse(data.Payload), Payload = data.Payload }; + return new MongoEvent { Type = data.Type, Headers = data.Headers, Payload = data.Payload }; } public EventData ToEventData() { - return new EventData { Type = Type, Metadata = Metadata.ToJson().ToString(), Payload = Payload }; + return new EventData(Type, Headers, Payload); } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConverter.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConverter.cs deleted file mode 100644 index e9aa9d8cc..000000000 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonConverter.cs +++ /dev/null @@ -1,155 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Globalization; -using MongoDB.Bson; -using Newtonsoft.Json.Linq; - -namespace Squidex.Infrastructure.MongoDb -{ - public static class BsonJsonConverter - { - public static BsonDocument ToBson(this JObject source) - { - var result = new BsonDocument(); - - foreach (var property in source) - { - result.Add(property.Key.EscapeJson(), 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.UnescapeBson(), 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.Type) - { - case JTokenType.Object: - return ((JObject)source).ToBson(); - case JTokenType.Array: - return ((JArray)source).ToBson(); - case JTokenType.Integer: - return BsonValue.Create(((JValue)source).Value); - case JTokenType.Float: - return BsonValue.Create(((JValue)source).Value); - case JTokenType.String: - return BsonValue.Create(((JValue)source).Value); - case JTokenType.Boolean: - return BsonValue.Create(((JValue)source).Value); - case JTokenType.Null: - return BsonNull.Value; - case JTokenType.Undefined: - return BsonUndefined.Value; - case JTokenType.Bytes: - return BsonValue.Create(((JValue)source).Value); - case JTokenType.Guid: - return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); - case JTokenType.Uri: - return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); - case JTokenType.TimeSpan: - return BsonValue.Create(((JValue)source).ToString(CultureInfo.InvariantCulture)); - case JTokenType.Date: - { - var value = ((JValue)source).Value; - - if (value is DateTime dateTime) - { - return dateTime.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); - } - else if (value is DateTimeOffset dateTimeOffset) - { - if (dateTimeOffset.Offset == TimeSpan.Zero) - { - return dateTimeOffset.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); - } - else - { - return dateTimeOffset.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture); - } - } - else - { - return value.ToString(); - } - } - } - - 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.Binary: - return new JValue(source.AsBsonBinaryData.Bytes); - case BsonType.Null: - return JValue.CreateNull(); - case BsonType.Undefined: - return JValue.CreateUndefined(); - } - - throw new NotSupportedException($"Cannot convert {source.GetType()} to Json."); - } - } -} \ No newline at end of file diff --git a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs index ba6f659a8..247f6a918 100644 --- a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs @@ -27,17 +27,15 @@ namespace Squidex.Infrastructure.EventSourcing public Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null) { - var eventType = typeNameRegistry.GetType(eventData.Type); + var payloadType = typeNameRegistry.GetType(eventData.Type); + var payload = serializer.Deserialize(eventData.Payload, payloadType, stringConverter); - var eventHeaders = serializer.Deserialize(eventData.Metadata, null, stringConverter); - var eventContent = serializer.Deserialize(eventData.Payload, eventType, stringConverter); - - if (migrate && eventContent is IMigratedEvent migratedEvent) + if (migrate && payload is IMigratedEvent migratedEvent) { - eventContent = migratedEvent.Migrate(); + payload = migratedEvent.Migrate(); } - var envelope = new Envelope(eventContent, eventHeaders); + var envelope = new Envelope(payload, eventData.Headers); return envelope; } @@ -51,14 +49,12 @@ namespace Squidex.Infrastructure.EventSourcing eventPayload = migratedEvent.Migrate(); } - var eventType = typeNameRegistry.GetName(eventPayload.GetType()); + var payloadType = typeNameRegistry.GetName(eventPayload.GetType()); + var payload = serializer.Serialize(envelope.Payload); envelope.SetCommitId(commitId); - var eventHeaders = serializer.Serialize(envelope.Headers); - var eventContent = serializer.Serialize(envelope.Payload); - - return new EventData { Type = eventType, Payload = eventContent, Metadata = eventHeaders }; + return new EventData(payloadType, envelope.Headers, payload); } } } diff --git a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs index f1dce1331..20176f576 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs @@ -6,7 +6,10 @@ // ========================================================================== using System; +using System.Globalization; using NodaTime; +using NodaTime.Text; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Infrastructure.EventSourcing { @@ -19,69 +22,90 @@ namespace Squidex.Infrastructure.EventSourcing public static Envelope SetEventPosition(this Envelope envelope, string value) where T : class { - envelope.Headers.Set(CommonHeaders.EventNumber, value); + envelope.Headers.Add(CommonHeaders.EventNumber, value); return envelope; } public static long EventStreamNumber(this EnvelopeHeaders headers) { - return headers[CommonHeaders.EventStreamNumber].ToInt64(); + return headers.GetInt64(CommonHeaders.EventStreamNumber); } public static Envelope SetEventStreamNumber(this Envelope envelope, long value) where T : class { - envelope.Headers.Set(CommonHeaders.EventStreamNumber, value); + envelope.Headers.Add(CommonHeaders.EventStreamNumber, value); return envelope; } public static Guid CommitId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.CommitId].ToGuid(); + return headers.GetGuid(CommonHeaders.CommitId); } public static Envelope SetCommitId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Set(CommonHeaders.CommitId, value); + envelope.Headers.Add(CommonHeaders.CommitId, value); return envelope; } public static Guid AggregateId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.AggregateId].ToGuid(); + return headers.GetGuid(CommonHeaders.AggregateId); } public static Envelope SetAggregateId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Set(CommonHeaders.AggregateId, value); + envelope.Headers.Add(CommonHeaders.AggregateId, value); return envelope; } public static Guid EventId(this EnvelopeHeaders headers) { - return headers[CommonHeaders.EventId].ToGuid(); + return headers.GetGuid(CommonHeaders.EventId); } public static Envelope SetEventId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Set(CommonHeaders.EventId, value); + envelope.Headers.Add(CommonHeaders.EventId, value); return envelope; } public static Instant Timestamp(this EnvelopeHeaders headers) { - return headers[CommonHeaders.Timestamp].ToInstant(); + return headers.GetInstant(CommonHeaders.Timestamp); } public static Envelope SetTimestamp(this Envelope envelope, Instant value) where T : class { - envelope.Headers.Set(CommonHeaders.Timestamp, value); + envelope.Headers.Add(CommonHeaders.Timestamp, value); return envelope; } + + public static long GetInt64(this JsonObject obj, string key) + { + var value = obj[key]; + + return value is JsonScalar s ? (long)s.Value : long.Parse(value.ToString(), CultureInfo.InvariantCulture); + } + + public static Guid GetGuid(this JsonObject obj, string key) + { + var value = obj[key]; + + return Guid.Parse(value.ToString()); + } + + public static Instant GetInstant(this JsonObject obj, string key) + { + var value = obj[key]; + + return InstantPattern.General.Parse(value.ToString()).Value; + } } } diff --git a/src/Squidex.Infrastructure/EventSourcing/EnvelopeHeaders.cs b/src/Squidex.Infrastructure/EventSourcing/EnvelopeHeaders.cs index 3b53fcd30..021ec109f 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EnvelopeHeaders.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EnvelopeHeaders.cs @@ -5,37 +5,24 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Infrastructure.Json.Objects; + namespace Squidex.Infrastructure.EventSourcing { - public sealed class EnvelopeHeaders : PropertiesBag + public sealed class EnvelopeHeaders : JsonObject { public EnvelopeHeaders() { } - public EnvelopeHeaders(PropertiesBag bag) + public EnvelopeHeaders(JsonObject headers) + : base(headers) { - if (bag == null) - { - return; - } - - foreach (var property in bag.Properties) - { - Set(property.Key, property.Value.RawValue); - } } public EnvelopeHeaders Clone() { - var clone = new EnvelopeHeaders(); - - foreach (var property in Properties) - { - clone.Set(property.Key, property.Value.RawValue); - } - - return clone; + return new EnvelopeHeaders(this); } } } diff --git a/src/Squidex.Infrastructure/EventSourcing/EventData.cs b/src/Squidex.Infrastructure/EventSourcing/EventData.cs index a3d82188f..016043919 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EventData.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EventData.cs @@ -7,12 +7,25 @@ namespace Squidex.Infrastructure.EventSourcing { - public class EventData + public sealed class EventData { - public string Payload { get; set; } + public EnvelopeHeaders Headers { get; } - public string Metadata { get; set; } + public string Payload { get; } public string Type { get; set; } + + public EventData(string type, EnvelopeHeaders headers, string payload) + { + Guard.NotNull(type, nameof(type)); + Guard.NotNull(headers, nameof(headers)); + Guard.NotNull(payload, nameof(payload)); + + Headers = headers; + + Payload = payload; + + Type = type; + } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs deleted file mode 100644 index 8eb624c1f..000000000 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs +++ /dev/null @@ -1,78 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Newtonsoft.Json; -using NodaTime; -using NodaTime.Extensions; - -namespace Squidex.Infrastructure.Json.Newtonsoft -{ - public sealed class PropertiesBagConverter : JsonClassConverter where T : PropertiesBag, new() - { - protected override void WriteValue(JsonWriter writer, T value, JsonSerializer serializer) - { - writer.WriteStartObject(); - - foreach (var kvp in value.Properties) - { - writer.WritePropertyName(kvp.Key); - - if (kvp.Value.RawValue is Instant instant) - { - writer.WriteValue(instant.ToString()); - } - else - { - writer.WriteValue(kvp.Value.RawValue); - } - } - - writer.WriteEndObject(); - } - - protected override T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) - { - if (reader.TokenType != JsonToken.StartObject) - { - throw new JsonException($"Expected Object, but got {reader.TokenType}."); - } - - var properties = new T(); - - while (reader.Read()) - { - if (reader.TokenType != JsonToken.PropertyName) - { - break; - } - - var key = reader.Value.ToString(); - - reader.Read(); - - var value = reader.Value; - - if (value is DateTime dateTime) - { - properties.Set(key, dateTime.ToInstant()); - } - else - { - properties.Set(key, value); - } - } - - return properties; - } - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(T); - } - } -} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs index a2cacce74..0c29a39ca 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs @@ -12,9 +12,9 @@ using System.Linq; namespace Squidex.Infrastructure.Json.Objects { - public sealed class JsonObject : IReadOnlyDictionary, IJsonValue, IEquatable + public class JsonObject : IReadOnlyDictionary, IJsonValue, IEquatable { - private readonly Dictionary inner = new Dictionary(); + private readonly Dictionary inner; public IJsonValue this[string key] { @@ -51,6 +51,16 @@ namespace Squidex.Infrastructure.Json.Objects get { return JsonValueType.Array; } } + public JsonObject() + { + inner = new Dictionary(); + } + + public JsonObject(JsonObject obj) + { + inner = new Dictionary(obj.inner); + } + public JsonObject Add(string key, object value) { return Add(key, JsonValue.Create(value)); @@ -61,7 +71,7 @@ namespace Squidex.Infrastructure.Json.Objects Guard.NotNullOrEmpty(key, nameof(key)); Guard.NotNull(value, nameof(value)); - inner.Add(key, value); + inner[key] = value; return this; } diff --git a/src/Squidex.Infrastructure/PropertiesBag.cs b/src/Squidex.Infrastructure/PropertiesBag.cs deleted file mode 100644 index 511c76368..000000000 --- a/src/Squidex.Infrastructure/PropertiesBag.cs +++ /dev/null @@ -1,112 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Dynamic; - -namespace Squidex.Infrastructure -{ - public class PropertiesBag : DynamicObject - { - private static readonly PropertyValue FallbackValue = new PropertyValue(null); - private readonly Dictionary internalDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public int Count - { - get { return internalDictionary.Count; } - } - - public IReadOnlyDictionary Properties - { - get { return internalDictionary; } - } - - public IEnumerable PropertyNames - { - get { return internalDictionary.Keys; } - } - - public PropertyValue this[string propertyName] - { - get - { - Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - - return internalDictionary.GetOrDefault(propertyName) ?? FallbackValue; - } - } - - public override IEnumerable GetDynamicMemberNames() - { - return internalDictionary.Keys; - } - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - result = this[binder.Name]; - - return true; - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - internalDictionary[binder.Name] = new PropertyValue(value); - - return true; - } - - public bool Contains(string propertyName) - { - Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - - return internalDictionary.ContainsKey(propertyName); - } - - public bool Remove(string propertyName) - { - Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - - return internalDictionary.Remove(propertyName); - } - - public PropertiesBag Set(string propertyName, object value) - { - Guard.NotNullOrEmpty(propertyName, nameof(propertyName)); - - internalDictionary[propertyName] = new PropertyValue(value); - - return this; - } - - public bool Rename(string oldPropertyName, string newPropertyName) - { - Guard.NotNullOrEmpty(oldPropertyName, nameof(oldPropertyName)); - Guard.NotNullOrEmpty(newPropertyName, nameof(newPropertyName)); - - if (internalDictionary.ContainsKey(newPropertyName)) - { - throw new ArgumentException($"An property with the key '{newPropertyName}' already exists.", newPropertyName); - } - - if (string.Equals(oldPropertyName, newPropertyName, StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException($"The property names '{newPropertyName}' are equal.", newPropertyName); - } - - if (!internalDictionary.TryGetValue(oldPropertyName, out var property)) - { - return false; - } - - internalDictionary[newPropertyName] = property; - internalDictionary.Remove(oldPropertyName); - - return true; - } - } -} diff --git a/src/Squidex.Infrastructure/PropertyValue.cs b/src/Squidex.Infrastructure/PropertyValue.cs deleted file mode 100644 index ddec447c6..000000000 --- a/src/Squidex.Infrastructure/PropertyValue.cs +++ /dev/null @@ -1,235 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using NodaTime; -using NodaTime.Text; - -namespace Squidex.Infrastructure -{ - public sealed class PropertyValue : DynamicObject - { - private readonly object rawValue; - - private static readonly Dictionary> Parsers = - new Dictionary> - { - { typeof(string), p => p.ToString() }, - { typeof(bool), p => p.ToBoolean() }, - { typeof(bool?), p => p.ToNullableBoolean() }, - { typeof(float), p => p.ToSingle() }, - { typeof(float?), p => p.ToNullableSingle() }, - { typeof(double), p => p.ToDouble() }, - { typeof(double?), p => p.ToNullableDouble() }, - { typeof(int), p => p.ToInt32() }, - { typeof(int?), p => p.ToNullableInt32() }, - { typeof(long), p => p.ToInt64() }, - { typeof(long?), p => p.ToNullableInt64() }, - { typeof(Instant), p => p.ToInstant() }, - { typeof(Instant?), p => p.ToNullableInstant() }, - { typeof(Guid), p => p.ToGuid() }, - { typeof(Guid?), p => p.ToNullableGuid() } - }; - - public object RawValue - { - get { return rawValue; } - } - - internal PropertyValue(object rawValue) - { - if (rawValue != null && !Parsers.ContainsKey(rawValue.GetType())) - { - throw new InvalidOperationException($"The type '{rawValue.GetType()}' is not supported."); - } - - this.rawValue = rawValue; - } - - public override bool TryConvert(ConvertBinder binder, out object result) - { - result = null; - - if (!Parsers.TryGetValue(binder.Type, out var parser)) - { - return false; - } - - result = parser(this); - - return true; - } - - public override string ToString() - { - return rawValue?.ToString(); - } - - public bool ToBoolean() - { - return ToOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); - } - - public bool? ToNullableBoolean() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, ParseBoolean); - } - - public float ToSingle() - { - return ToOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); - } - - public float? ToNullableSingle() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture)); - } - - public double ToDouble() - { - return ToOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); - } - - public double? ToNullableDouble() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture)); - } - - public int ToInt32() - { - return ToOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); - } - - public int? ToNullableInt32() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture)); - } - - public long ToInt64() - { - return ToOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); - } - - public long? ToNullableInt64() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture)); - } - - public Instant ToInstant() - { - return ToOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); - } - - public Instant? ToNullableInstant() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value); - } - - public Guid ToGuid() - { - return ToOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); - } - - public Guid? ToNullableGuid() - { - return ToNullableOrParseValue(CultureInfo.InvariantCulture, Guid.Parse); - } - - private T? ToNullableOrParseValue(IFormatProvider culture, Func parser) where T : struct - { - return TryParse(culture, parser, out var result) ? result : (T?)null; - } - - private T ToOrParseValue(IFormatProvider culture, Func parser) - { - return TryParse(culture, parser, out var result) ? result : default(T); - } - - private bool TryParse(IFormatProvider culture, Func parser, out T result) - { - var value = rawValue; - - if (value != null) - { - var valueType = value.GetType(); - - if (valueType == typeof(T)) - { - result = (T)value; - } - else if (valueType == typeof(string)) - { - result = Parse(parser, valueType, value); - } - else - { - result = Convert(culture, value, valueType); - } - - return true; - } - - result = default(T); - - return false; - } - - private static T Convert(IFormatProvider culture, object value, Type valueType) - { - var requestedType = typeof(T); - - try - { - return (T)System.Convert.ChangeType(value, requestedType, culture); - } - catch (OverflowException) - { - var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}' because it is either too small or large."; - - throw new InvalidCastException(message); - } - catch (InvalidCastException) - { - var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; - - throw new InvalidCastException(message); - } - } - - private static T Parse(Func parser, Type valueType, object value) - { - var requestedType = typeof(T); - - try - { - return parser(value.ToString()); - } - catch (Exception ex) - { - var message = $"The property has type '{valueType}' and cannot be casted to '{requestedType}'."; - - throw new InvalidCastException(message, ex); - } - } - - private static bool ParseBoolean(string value) - { - switch (value) - { - case "1": - return true; - case "0": - return false; - default: - return bool.Parse(value); - } - } - } -} diff --git a/src/Squidex/Config/Domain/EventStoreServices.cs b/src/Squidex/Config/Domain/EventStoreServices.cs index 85066ae53..a960b1488 100644 --- a/src/Squidex/Config/Domain/EventStoreServices.cs +++ b/src/Squidex/Config/Domain/EventStoreServices.cs @@ -14,6 +14,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Diagnostics; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing.Grains; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.States; namespace Squidex.Config.Domain @@ -50,7 +51,7 @@ namespace Squidex.Config.Domain services.AddSingletonAs(c => new GetEventStoreHealthCheck(connection)) .As(); - services.AddSingletonAs(c => new GetEventStore(connection, eventStorePrefix, eventStoreProjectionHost)) + services.AddSingletonAs(c => new GetEventStore(connection, c.GetRequiredService(), eventStorePrefix, eventStoreProjectionHost)) .As(); } }); diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 2f68fccc3..96c8e1dd1 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -56,8 +56,6 @@ namespace Squidex.Config.Domain new NamedGuidIdConverter(), new NamedLongIdConverter(), new NamedStringIdConverter(), - new PropertiesBagConverter(), - new PropertiesBagConverter(), new RefTokenConverter(), new RolesConverter(), new RuleConverter(), diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs index bc22739d1..bafb2c6f8 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs @@ -47,8 +47,6 @@ namespace Squidex.Domain.Apps.Core new NamedGuidIdConverter(), new NamedLongIdConverter(), new NamedStringIdConverter(), - new PropertiesBagConverter(), - new PropertiesBagConverter(), new RefTokenConverter(), new RolesConverter(), new RuleConverter(), diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs index 6d64aad03..1179039aa 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs @@ -82,9 +82,9 @@ namespace Squidex.Domain.Apps.Entities.Backup var envelope = Envelope.Create(@event); - envelope.Headers.Set(RandomGuid().ToString(), i); - envelope.Headers.Set("Id", RandomGuid()); - envelope.Headers.Set("Index", i); + envelope.Headers.Add(RandomGuid().ToString(), i); + envelope.Headers.Add("Id", RandomGuid()); + envelope.Headers.Add("Index", i); sourceEvents.Add(($"My-{RandomGuid()}", envelope)); } @@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Backup var eventData = formatter.ToEventData(@event.Event, Guid.NewGuid(), true); var eventStored = new StoredEvent("S", "1", 2, eventData); - var index = @event.Event.Headers["Index"].ToInt32(); + var index = int.Parse(@event.Event.Headers["Index"].ToString()); if (index % 17 == 0) { @@ -124,7 +124,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { await reader.ReadEventsAsync(streamNameResolver, formatter, async @event => { - var index = @event.Event.Headers["Index"].ToInt32(); + var index = int.Parse(@event.Event.Headers["Index"].ToString()); if (index % 17 == 0) { @@ -155,7 +155,7 @@ namespace Squidex.Domain.Apps.Entities.Backup Assert.Equal(rhs.Payload.GuidRaw, reader.OldGuid(lhs.Payload.GuidRaw)); Assert.Equal(rhs.Payload.GuidNamed.Id, reader.OldGuid(lhs.Payload.GuidNamed.Id)); - Assert.Equal(rhs.Headers["Id"].ToGuid(), reader.OldGuid(lhs.Headers["Id"].ToGuid())); + Assert.Equal(rhs.Headers.GetGuid("Id"), reader.OldGuid(lhs.Headers.GetGuid("Id"))); } } } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs index 212352d04..60e2b769f 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/DefaultEventDataFormatterTests.cs @@ -88,9 +88,9 @@ namespace Squidex.Infrastructure.EventSourcing Assert.Equal(inputEvent.Payload.MyProperty, outputEvent.Payload.MyProperty); } - private static void AssertHeaders(PropertiesBag lhs, PropertiesBag rhs) + private static void AssertHeaders(EnvelopeHeaders lhs, EnvelopeHeaders rhs) { - foreach (var key in lhs.PropertyNames.Concat(rhs.PropertyNames).Distinct()) + foreach (var key in lhs.Keys.Concat(rhs.Keys).Distinct()) { Assert.Equal(lhs[key].ToString(), rhs[key].ToString()); } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs index bec3549d1..9d377646b 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs @@ -23,7 +23,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetTimestamp(timestamp); Assert.Equal(timestamp, sut.Headers.Timestamp()); - Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant()); + Assert.Equal(timestamp, sut.Headers.GetInstant("Timestamp")); } [Fact] @@ -34,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetCommitId(commitId); Assert.Equal(commitId, sut.Headers.CommitId()); - Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid()); + Assert.Equal(commitId, sut.Headers.GetGuid("CommitId")); } [Fact] @@ -45,7 +45,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetEventId(commitId); Assert.Equal(commitId, sut.Headers.EventId()); - Assert.Equal(commitId, sut.Headers["EventId"].ToGuid()); + Assert.Equal(commitId, sut.Headers.GetGuid("EventId")); } [Fact] @@ -56,7 +56,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetAggregateId(commitId); Assert.Equal(commitId, sut.Headers.AggregateId()); - Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid()); + Assert.Equal(commitId, sut.Headers.GetGuid("AggregateId")); } [Fact] @@ -78,7 +78,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetEventStreamNumber(eventStreamNumber); Assert.Equal(eventStreamNumber, sut.Headers.EventStreamNumber()); - Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64()); + Assert.Equal(eventStreamNumber, sut.Headers.GetInt64("EventStreamNumber")); } } } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs index c3f4b091c..21b1c7363 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using System.Linq; +using Squidex.Infrastructure.Json.Objects; using Xunit; namespace Squidex.Infrastructure.EventSourcing @@ -17,21 +18,13 @@ namespace Squidex.Infrastructure.EventSourcing { var headers = new EnvelopeHeaders(); - Assert.Equal(0, headers.Count); - } - - [Fact] - public void Should_create_headers_with_null_properties() - { - var headers = new EnvelopeHeaders(null); - - Assert.Equal(0, headers.Count); + Assert.Empty(headers); } [Fact] public void Should_create_headers_as_copy() { - var source = new PropertiesBag().Set("Key1", 123); + var source = new JsonObject().Add("Key1", 123); var headers = new EnvelopeHeaders(source); CompareHeaders(headers, source); @@ -40,7 +33,7 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public void Should_clone_headers() { - var source = new PropertiesBag().Set("Key1", 123); + var source = new JsonObject().Add("Key1", 123); var headers = new EnvelopeHeaders(source); var clone = headers.Clone(); @@ -48,9 +41,9 @@ namespace Squidex.Infrastructure.EventSourcing CompareHeaders(headers, clone); } - private static void CompareHeaders(PropertiesBag lhs, PropertiesBag rhs) + private static void CompareHeaders(JsonObject lhs, JsonObject rhs) { - foreach (var key in lhs.PropertyNames.Concat(rhs.PropertyNames).Distinct()) + foreach (var key in lhs.Keys.Concat(rhs.Keys).Distinct()) { Assert.Equal(lhs[key].ToString(), rhs[key].ToString()); } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index 7f5bd174c..b7842bec0 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -49,7 +49,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains private readonly ISemanticLog log = A.Fake(); private readonly IStore store = A.Fake>(); private readonly IEventDataFormatter formatter = A.Fake(); - private readonly EventData eventData = new EventData(); + private readonly EventData eventData = new EventData("Type", new EnvelopeHeaders(), "Payload"); private readonly Envelope envelope = new Envelope(new MyEvent()); private readonly EventConsumerGrain sut; private readonly string consumerName; diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/RetrySubscriptionTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/RetrySubscriptionTests.cs index 85aee50d2..10ad267e9 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/RetrySubscriptionTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/RetrySubscriptionTests.cs @@ -90,7 +90,7 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public async Task Should_forward_event_from_inner_subscription() { - var ev = new StoredEvent("Stream", "1", 2, new EventData()); + var ev = new StoredEvent("Stream", "1", 2, new EventData("Type", new EnvelopeHeaders(), "Payload")); await OnEventAsync(eventSubscription, ev); await sut.StopAsync(); @@ -102,7 +102,7 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public async Task Should_not_forward_event_when_message_is_from_another_subscription() { - var ev = new StoredEvent("Stream", "1", 2, new EventData()); + var ev = new StoredEvent("Stream", "1", 2, new EventData("Type", new EnvelopeHeaders(), "Payload")); await OnEventAsync(A.Fake(), ev); await sut.StopAsync(); diff --git a/tests/Squidex.Infrastructure.Tests/MongoDb/BsonConverterTests.cs b/tests/Squidex.Infrastructure.Tests/MongoDb/BsonConverterTests.cs index 543d74468..c8ae76adf 100644 --- a/tests/Squidex.Infrastructure.Tests/MongoDb/BsonConverterTests.cs +++ b/tests/Squidex.Infrastructure.Tests/MongoDb/BsonConverterTests.cs @@ -117,44 +117,6 @@ namespace Squidex.Infrastructure.MongoDb private readonly TestObject source = TestObject.CreateWithValues(); private readonly JsonSerializer serializer = JsonSerializer.CreateDefault(); - [Fact] - public void Should_serialize_and_deserialize_to_bson_with_json() - { - var target = JObject.FromObject(source).ToBson().ToJson().ToObject(); - - target.Should().BeEquivalentTo(source); - } - - [Fact] - public void Should_serialize_datetime_to_iso() - { - source.DateTime = new DateTime(2012, 12, 12, 12, 12, 12, DateTimeKind.Utc); - - var target = JObject.FromObject(source).ToBson(); - - Assert.Equal("2012-12-12T12:12:12Z", target["DateTime"].ToString()); - } - - [Fact] - public void Should_serialize_datetimeoffset_to_iso_utc() - { - source.DateTimeOffset = new DateTime(2012, 12, 12, 12, 12, 12, DateTimeKind.Utc); - - var target = JObject.FromObject(source).ToBson(); - - Assert.Equal("2012-12-12T12:12:12Z", target["DateTimeOffset"].ToString()); - } - - [Fact] - public void Should_serialize_datetimeoffset_to_iso_utc_with_offset() - { - source.DateTimeOffset = new DateTimeOffset(2012, 12, 12, 12, 12, 12, TimeSpan.FromHours(2)); - - var target = JObject.FromObject(source).ToBson(); - - Assert.Equal("2012-12-12T12:12:12+02:00", target["DateTimeOffset"].ToString()); - } - [Fact] public void Should_write_problematic_object() { diff --git a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs b/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs deleted file mode 100644 index e60eec701..000000000 --- a/tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs +++ /dev/null @@ -1,428 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright () Squidex UG (haftungsbeschränkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Linq; -using Microsoft.CSharp.RuntimeBinder; -using NodaTime; -using Squidex.Infrastructure.TestHelpers; -using Xunit; - -namespace Squidex.Infrastructure -{ - public class PropertiesBagTests - { - private readonly PropertiesBag bag = new PropertiesBag(); - private readonly dynamic dynamicBag; - - public PropertiesBagTests() - { - dynamicBag = bag; - } - - [Fact] - public void Should_serialize_and_deserialize_empty_bag() - { - var output = bag.SerializeAndDeserialize(); - - Assert.Equal(bag.Count, output.Count); - } - - [Fact] - public void Should_serialize_and_deserialize() - { - var time = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); - - bag.Set("Key1", time); - bag.Set("Key2", "MyString"); - bag.Set("Key3", 123L); - bag.Set("Key4", true); - bag.Set("Key5", Guid.NewGuid()); - - var output = bag.SerializeAndDeserialize(); - - foreach (var kvp in output.Properties.Take(4)) - { - Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue); - } - - Assert.Equal(bag["Key5"].ToGuid(), output["Key5"].ToGuid()); - } - - [Fact] - public void Should_return_false_when_renaming_unknown_property() - { - Assert.False(bag.Rename("OldKey", "NewKey")); - } - - [Fact] - public void Should_throw_when_renaming_to_existing_property() - { - bag.Set("NewKey", 1); - - Assert.Throws(() => bag.Rename("OldKey", "NewKey")); - } - - [Fact] - public void Should_throw_when_renaming_to_same_key() - { - Assert.Throws(() => bag.Rename("SameKey", "SameKey")); - } - - [Fact] - public void Should_provide_property_with_new_name_after_rename() - { - bag.Set("OldKey", 123); - - Assert.True(bag.Rename("OldKey", "NewKey")); - Assert.True(bag.Contains("NewKey")); - - Assert.Equal(1, bag.Count); - Assert.Equal(123, bag["NewKey"].ToInt64()); - - Assert.False(bag.Contains("OldKey")); - } - - [Fact] - public void Should_calculate_count_correctly() - { - bag.Set("Key1", 1); - bag.Set("Key2", 1); - - Assert.Equal(2, bag.Count); - } - - [Fact] - public void Should_calculate_keys_correctly() - { - bag.Set("Key1", 1); - bag.Set("Key2", 1); - - Assert.Equal(new[] { "Key1", "Key2" }, bag.PropertyNames.ToArray()); - Assert.Equal(new[] { "Key1", "Key2" }, bag.Properties.Keys.ToArray()); - Assert.Equal(new[] { "Key1", "Key2" }, bag.GetDynamicMemberNames().ToArray()); - } - - [Fact] - public void Should_return_correct_value_when_contains_check() - { - Assert.False(bag.Contains("Key")); - - bag.Set("Key", 1); - - Assert.True(bag.Contains("Key")); - Assert.True(bag.Contains("KEY")); - } - - [Fact] - public void Should_returne_false_when_property_to_rename_does_not_exist() - { - Assert.False(bag.Remove("NOTFOUND")); - } - - [Fact] - public void Should_ignore_casing_when_returning() - { - bag.Set("Key", 1); - - Assert.True(bag.Remove("KEY")); - Assert.False(bag.Contains("KEY")); - } - - [Fact] - public void Should_set_value_as_dynamic() - { - dynamicBag.Key = 456; - - Assert.Equal(456, (int)dynamicBag.Key); - } - - [Fact] - public void Should_throw_when_setting_value_with_invalid_type_dynamically() - { - Assert.Throws(() => dynamicBag.Key = (byte)123); - } - - [Fact] - public void Should_throw_when_setting_value_with_invalid_type() - { - Assert.Throws(() => bag.Set("Key", (byte)1)); - } - - [Fact] - public void Should_return_false_when_making_contains_check() - { - Assert.False(dynamicBag.Contains("Key")); - } - - [Fact] - public void Should_provide_default_value_if_not_exists() - { - Assert.Equal(0, (int)dynamicBag.Key); - } - - [Fact] - public void Should_throw_when_parsing_failed() - { - bag.Set("Key", "abc"); - - Assert.Throws(() => bag["Key"].ToInt64()); - } - - [Fact] - public void Should_return_false_when_converter_does_not_exist() - { - bag.Set("Key", "abc"); - - Assert.Throws(() => (TimeSpan)dynamicBag.Key); - } - - [Fact] - public void Should_convert_string_to_numbers() - { - bag.Set("Key", 123); - - AssertNumber(); - } - - [Fact] - public void Should_convert_int_to_numbers() - { - bag.Set("Key", 123); - - AssertNumber(); - } - - [Fact] - public void Should_convert_long_to_numbers() - { - bag.Set("Key", 123L); - - AssertNumber(); - } - - [Fact] - public void Should_throw_when_casting_from_large_long() - { - bag.Set("Key", long.MaxValue); - - Assert.Throws(() => bag["Key"].ToInt32()); - } - - [Fact] - public void Should_convert_float_to_number() - { - bag.Set("Key", 123f); - - AssertNumber(); - } - - [Fact] - public void Should_convert_double_to_number() - { - bag.Set("Key", 123d); - - AssertNumber(); - } - - [Fact] - public void Should_throw_when_casting_from_large_doule() - { - bag.Set("Key", double.MaxValue); - - Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle()); - } - - [Fact] - public void Should_convert_from_instant_value() - { - var time = SystemClock.Instance.GetCurrentInstant(); - - bag.Set("Key", time); - - AssertInstant(time); - } - - [Fact] - public void Should_convert_from_instant_string() - { - var time = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); - - bag.Set("Key", time.ToString()); - - AssertInstant(time); - } - - [Fact] - public void Should_convert_from_guid_value() - { - var id = Guid.NewGuid(); - - bag.Set("Key", id); - - AssertGuid(id); - } - - [Fact] - public void Should_convert_from_guid_string() - { - var id = Guid.NewGuid(); - - bag.Set("Key", id.ToString()); - - AssertGuid(id); - } - - [Fact] - public void Should_convert_from_boolean_value() - { - bag.Set("Key", true); - - AssertBoolean(true); - } - - [Fact] - public void Should_convert_from_boolean_string() - { - bag.Set("Key", "true"); - - AssertBoolean(true); - } - - [Fact] - public void Should_convert_boolean_from_number() - { - bag.Set("Key", 1); - - AssertBoolean(true); - } - - [Fact] - public void Should_convert_boolean_to_truthy_number_string() - { - bag.Set("Key", "1"); - - AssertBoolean(true); - } - - [Fact] - public void Should_convert_boolean_to_falsy_number_string() - { - bag.Set("Key", "0"); - - AssertBoolean(false); - } - - [Fact] - public void Should_provide_value_as_string() - { - bag.Set("Key", "Foo"); - - AssertString("Foo"); - } - - [Fact] - public void Should_provide_null() - { - bag.Set("Key", null); - - AssertNull(); - } - - [Fact] - public void Should_throw_when_converting_instant_to_number() - { - bag.Set("Key", SystemClock.Instance.GetCurrentInstant()); - - Assert.Throws(() => bag["Key"].ToGuid()); - } - - private void AssertNumber() - { - AssertInt32(123); - AssertInt64(123); - AssertSingle(123); - AssertDouble(123); - } - - private void AssertString(string expected) - { - Assert.Equal(expected, bag["Key"].ToString()); - - Assert.Equal(expected, (string)dynamicBag.Key); - } - - private void AssertNull() - { - Assert.Null(bag["Key"].ToString()); - Assert.Null(bag["Key"].RawValue); - } - - private void AssertBoolean(bool expected) - { - Assert.Equal(expected, bag["Key"].ToBoolean()); - Assert.Equal(expected, bag["Key"].ToNullableBoolean()); - - Assert.Equal(expected, (bool)dynamicBag.Key); - Assert.Equal(expected, (bool?)dynamicBag.Key); - } - - private void AssertInstant(Instant expected) - { - Assert.Equal(expected, bag["Key"].ToInstant()); - Assert.Equal(expected, bag["Key"].ToNullableInstant().Value); - - Assert.Equal(expected, (Instant)dynamicBag.Key); - Assert.Equal(expected, (Instant?)dynamicBag.Key); - } - - private void AssertGuid(Guid expected) - { - Assert.Equal(expected, bag["Key"].ToGuid()); - Assert.Equal(expected, bag["Key"].ToNullableGuid()); - - Assert.Equal(expected, (Guid)dynamicBag.Key); - Assert.Equal(expected, (Guid?)dynamicBag.Key); - } - - private void AssertDouble(double expected) - { - Assert.Equal(expected, bag["Key"].ToDouble()); - Assert.Equal(expected, bag["Key"].ToNullableDouble()); - - Assert.Equal(expected, (double)dynamicBag.Key); - Assert.Equal(expected, (double?)dynamicBag.Key); - } - - private void AssertSingle(float expected) - { - Assert.Equal(expected, bag["Key"].ToSingle()); - Assert.Equal(expected, bag["Key"].ToNullableSingle()); - - Assert.Equal(expected, (float)dynamicBag.Key); - Assert.Equal(expected, (float?)dynamicBag.Key); - } - - private void AssertInt32(long expected) - { - Assert.Equal(expected, bag["Key"].ToInt64()); - Assert.Equal(expected, bag["Key"].ToNullableInt64()); - - Assert.Equal(expected, (long)dynamicBag.Key); - Assert.Equal(expected, (long?)dynamicBag.Key); - } - - private void AssertInt64(int expected) - { - Assert.Equal(expected, bag["Key"].ToInt64()); - Assert.Equal(expected, bag["Key"].ToNullableInt64()); - - Assert.Equal(expected, (int)dynamicBag.Key); - Assert.Equal(expected, (int?)dynamicBag.Key); - } - } -} \ No newline at end of file diff --git a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index e7223fca2..a061b8a61 100644 --- a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -56,7 +56,7 @@ namespace Squidex.Infrastructure.States [Fact] public async Task Should_ignore_old_events() { - var storedEvent = new StoredEvent("1", "1", 0, new EventData()); + var storedEvent = new StoredEvent("1", "1", 0, new EventData("Type", new EnvelopeHeaders(), "Payload")); A.CallTo(() => eventStore.QueryAsync(key, 0)) .Returns(new List { storedEvent }); @@ -251,7 +251,7 @@ namespace Squidex.Infrastructure.States foreach (var @event in events) { - var eventData = new EventData(); + var eventData = new EventData("Type", new EnvelopeHeaders(), "Payload"); var eventStored = new StoredEvent(i.ToString(), i.ToString(), i, eventData); eventsStored.Add(eventStored); diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs index 9d913bdda..0eee70a27 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs @@ -32,8 +32,6 @@ namespace Squidex.Infrastructure.TestHelpers new NamedGuidIdConverter(), new NamedLongIdConverter(), new NamedStringIdConverter(), - new PropertiesBagConverter(), - new PropertiesBagConverter(), new RefTokenConverter(), new StringEnumConverter()), diff --git a/tools/Migrate_01/Rebuilder.cs b/tools/Migrate_01/Rebuilder.cs index 6214e37b4..d3bac5427 100644 --- a/tools/Migrate_01/Rebuilder.cs +++ b/tools/Migrate_01/Rebuilder.cs @@ -108,7 +108,7 @@ namespace Migrate_01 await eventStore.QueryAsync(async storedEvent => { - var headers = serializer.Deserialize(storedEvent.Data.Metadata); + var headers = storedEvent.Data.Headers; var id = headers.AggregateId(); From e62ad7d46223c5e3a743483a07df36110b9b4c6e Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 26 Nov 2018 21:12:26 +0100 Subject: [PATCH 07/15] Fixes for json. --- .../SquidexHeaderExtensions.cs | 2 +- .../EventSourcing/EnvelopeExtensions.cs | 62 +++- .../Newtonsoft/EnvelopeHeadersConverter.cs | 34 ++ .../Json/Newtonsoft/JsonValueConverter.cs | 5 +- .../Json/Objects/JsonArray.cs | 20 +- .../Json/Objects/JsonObject.cs | 8 +- .../Json/Objects/JsonScalar.cs | 18 +- .../Json/Objects/JsonValue.cs | 17 +- .../Config/Domain/SerializationServices.cs | 1 + tests/RunCoverage.ps1 | 2 +- .../Model/PartitioningTests.cs | 37 +- .../TestUtils.cs | 1 + .../EventSourcing/EnvelopeExtensionsTests.cs | 4 +- .../Grains/EventConsumerGrainTests.cs | 8 +- .../Json/Objects/JsonObjectTests.cs | 321 ++++++++++++++++++ .../NamedIdTests.cs | 52 +-- .../RefTokenTests.cs | 42 +-- .../TestHelpers/JsonHelper.cs | 1 + 18 files changed, 487 insertions(+), 148 deletions(-) create mode 100644 src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs create mode 100644 tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs diff --git a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs index d65fcd3ff..dc2e05ad2 100644 --- a/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs +++ b/src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs @@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Events public static Envelope SetAppId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Add(SquidexHeaders.AppId, value); + envelope.Headers.Add(SquidexHeaders.AppId, value.ToString()); return envelope; } diff --git a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs index 20176f576..968eadf3c 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs @@ -17,7 +17,7 @@ namespace Squidex.Infrastructure.EventSourcing { public static string EventPosition(this EnvelopeHeaders headers) { - return headers[CommonHeaders.EventNumber].ToString(); + return headers.GetString(CommonHeaders.EventNumber); } public static Envelope SetEventPosition(this Envelope envelope, string value) where T : class @@ -29,7 +29,7 @@ namespace Squidex.Infrastructure.EventSourcing public static long EventStreamNumber(this EnvelopeHeaders headers) { - return headers.GetInt64(CommonHeaders.EventStreamNumber); + return headers.GetLong(CommonHeaders.EventStreamNumber); } public static Envelope SetEventStreamNumber(this Envelope envelope, long value) where T : class @@ -46,7 +46,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Envelope SetCommitId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Add(CommonHeaders.CommitId, value); + envelope.Headers.Add(CommonHeaders.CommitId, value.ToString()); return envelope; } @@ -58,7 +58,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Envelope SetAggregateId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Add(CommonHeaders.AggregateId, value); + envelope.Headers.Add(CommonHeaders.AggregateId, value.ToString()); return envelope; } @@ -70,7 +70,7 @@ namespace Squidex.Infrastructure.EventSourcing public static Envelope SetEventId(this Envelope envelope, Guid value) where T : class { - envelope.Headers.Add(CommonHeaders.EventId, value); + envelope.Headers.Add(CommonHeaders.EventId, value.ToString()); return envelope; } @@ -82,30 +82,62 @@ namespace Squidex.Infrastructure.EventSourcing public static Envelope SetTimestamp(this Envelope envelope, Instant value) where T : class { - envelope.Headers.Add(CommonHeaders.Timestamp, value); + envelope.Headers.Add(CommonHeaders.Timestamp, value.ToString()); return envelope; } - public static long GetInt64(this JsonObject obj, string key) + public static long GetLong(this JsonObject obj, string key) { - var value = obj[key]; - - return value is JsonScalar s ? (long)s.Value : long.Parse(value.ToString(), CultureInfo.InvariantCulture); + if (obj.TryGetValue(key, out var v)) + { + if (v is JsonScalar number) + { + return (long)number.Value; + } + else if (v.Type == JsonValueType.String && double.TryParse(v.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + { + return (long)result; + } + } + + return 0; } public static Guid GetGuid(this JsonObject obj, string key) { - var value = obj[key]; - - return Guid.Parse(value.ToString()); + if (obj.TryGetValue(key, out var v)) + { + if (v.Type == JsonValueType.String && Guid.TryParse(v.ToString(), out var guid)) + { + return guid; + } + } + + return default(Guid); } public static Instant GetInstant(this JsonObject obj, string key) { - var value = obj[key]; + if (obj.TryGetValue(key, out var v)) + { + if (v.Type == JsonValueType.String && InstantPattern.General.Parse(v.ToString()).TryGetValue(default(Instant), out var instant)) + { + return instant; + } + } + + return default(Instant); + } + + public static string GetString(this JsonObject obj, string key) + { + if (obj.TryGetValue(key, out var v)) + { + return v.ToString(); + } - return InstantPattern.General.Parse(value.ToString()).Value; + return string.Empty; } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs new file mode 100644 index 000000000..a90b97950 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs @@ -0,0 +1,34 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Newtonsoft.Json; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json.Objects; + +namespace Squidex.Infrastructure.Json.Newtonsoft +{ + public sealed class EnvelopeHeadersConverter : JsonValueConverter + { + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var result = base.ReadJson(reader, objectType, existingValue, serializer); + + if (result is JsonObject obj) + { + return new EnvelopeHeaders(obj); + } + + return result; + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(EnvelopeHeaders); + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index 5b31bb412..fdbf6f97b 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -8,13 +8,14 @@ using System; using System.Globalization; using Newtonsoft.Json; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json.Objects; #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator namespace Squidex.Infrastructure.Json.Newtonsoft { - public sealed class JsonValueConverter : JsonConverter + public class JsonValueConverter : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { @@ -161,7 +162,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft public override bool CanConvert(Type objectType) { - return typeof(IJsonValue).IsAssignableFrom(objectType); + return objectType != typeof(EnvelopeHeaders) && typeof(IJsonValue).IsAssignableFrom(objectType); } } } diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs b/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs index 00880e34a..e3d58f511 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -23,28 +22,19 @@ namespace Squidex.Infrastructure.Json.Objects { } - public JsonArray(IList values) - : base(values) - { - } - - public JsonArray(params object[] values) + internal JsonArray(params object[] values) : base(values?.Select(JsonValue.Create).ToList()) { } protected override void InsertItem(int index, IJsonValue item) { - Guard.NotNull(item, nameof(item)); - - base.InsertItem(index, item); + base.InsertItem(index, item ?? JsonValue.Null); } protected override void SetItem(int index, IJsonValue item) { - Guard.NotNull(item, nameof(item)); - - base.SetItem(index, item); + base.SetItem(index, item ?? JsonValue.Null); } public override bool Equals(object obj) @@ -77,7 +67,7 @@ namespace Squidex.Infrastructure.Json.Objects public override int GetHashCode() { - var hashCode = 0; + var hashCode = 17; for (var i = 0; i < Count; i++) { @@ -94,7 +84,7 @@ namespace Squidex.Infrastructure.Json.Objects public override string ToString() { - return $"[{string.Join(", ", this)}]"; + return $"[{string.Join(", ", this.Select(x => x.ToJsonString()))}]"; } } } diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs index 0c29a39ca..7f1f6ceda 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs @@ -25,9 +25,8 @@ namespace Squidex.Infrastructure.Json.Objects set { Guard.NotNullOrEmpty(key, nameof(key)); - Guard.NotNull(value, nameof(value)); - inner[key] = value; + inner[key] = value ?? JsonValue.Null; } } @@ -68,10 +67,7 @@ namespace Squidex.Infrastructure.Json.Objects public JsonObject Add(string key, IJsonValue value) { - Guard.NotNullOrEmpty(key, nameof(key)); - Guard.NotNull(value, nameof(value)); - - inner[key] = value; + inner[key] = value ?? JsonValue.Null; return this; } diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs index 0689f7ecd..bea068163 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs @@ -47,14 +47,24 @@ namespace Squidex.Infrastructure.Json.Objects return value.GetHashCode(); } - public string ToJsonString() + public override string ToString() { - return Type == JsonValueType.String ? $"\"{value}\"" : ToString(); + if (Type == JsonValueType.Boolean) + { + return Equals(Value, true) ? "true" : "false"; + } + + return Value.ToString(); } - public override string ToString() + public string ToJsonString() { - return value.ToString(); + if (Type == JsonValueType.String) + { + return $"\"{value}\""; + } + + return ToString(); } } } diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs index 3fc3ae13e..5022aadab 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs @@ -8,16 +8,20 @@ using System; using NodaTime; +#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator + namespace Squidex.Infrastructure.Json.Objects { public static class JsonValue { - public static readonly JsonScalar Empty = new JsonScalar(JsonValueType.String, string.Empty); + public static readonly IJsonValue Empty = new JsonScalar(JsonValueType.String, string.Empty); + + public static readonly IJsonValue True = new JsonScalar(JsonValueType.Boolean, true); + public static readonly IJsonValue False = new JsonScalar(JsonValueType.Boolean, false); - public static readonly JsonScalar True = new JsonScalar(JsonValueType.Boolean, true); - public static readonly JsonScalar False = new JsonScalar(JsonValueType.Boolean, false); + public static readonly IJsonValue Null = JsonNull.Null; - public static readonly JsonNull Null = JsonNull.Null; + public static readonly IJsonValue Zero = new JsonScalar(JsonValueType.Number, 0); public static JsonArray Array() { @@ -76,6 +80,11 @@ namespace Squidex.Infrastructure.Json.Objects { Guard.ValidNumber(value, nameof(value)); + if (value == 0) + { + return Zero; + } + return new JsonScalar(JsonValueType.Number, value); } diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 96c8e1dd1..0baee6338 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -49,6 +49,7 @@ namespace Squidex.Config.Domain new AppContributorsConverter(), new AppPatternsConverter(), new ClaimsPrincipalConverter(), + new EnvelopeHeadersConverter(), new InstantConverter(), new JsonValueConverter(), new LanguageConverter(), diff --git a/tests/RunCoverage.ps1 b/tests/RunCoverage.ps1 index a30fd78cb..ea27808f8 100644 --- a/tests/RunCoverage.ps1 +++ b/tests/RunCoverage.ps1 @@ -76,6 +76,6 @@ if ($all -Or $web) { -oldStyle } -&"$folderHome\.nuget\packages\ReportGenerator\4.0.4\tools\ReportGenerator.exe" ` +&"$folderHome\.nuget\packages\ReportGenerator\4.0.4\tools\net47\ReportGenerator.exe" ` -reports:"$folderWorking\$folderReports\*.xml" ` -targetdir:"$folderWorking\$folderReports\Output" \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs index b82d85bcc..f16cdf930 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Model/PartitioningTests.cs @@ -68,37 +68,18 @@ namespace Squidex.Domain.Apps.Core.Model [Fact] public void Should_make_correct_equal_comparisons() { - var partitioning1a = new Partitioning("partitioning1"); - var partitioning1b = new Partitioning("partitioning1"); - var partitioning2a = new Partitioning("partitioning2"); + var partitioning1_a = new Partitioning("partitioning1"); + var partitioning1_b = new Partitioning("partitioning1"); - Assert.True(partitioning1a.Equals(partitioning1b)); + var partitioning2 = new Partitioning("partitioning2"); - Assert.False(partitioning1a.Equals(partitioning2a)); - } - - [Fact] - public void Should_make_correct_object_equal_comparisons() - { - object partitioning1a = new Partitioning("partitioning1"); - object partitioning1b = new Partitioning("partitioning1"); - object partitioning2a = new Partitioning("partitioning2"); - - Assert.True(partitioning1a.Equals(partitioning1b)); - - Assert.False(partitioning1a.Equals(partitioning2a)); - } - - [Fact] - public void Should_provide_correct_hash_codes() - { - var partitioning1a = new Partitioning("partitioning1"); - var partitioning1b = new Partitioning("partitioning1"); - var partitioning2a = new Partitioning("partitioning2"); - - Assert.Equal(partitioning1a.GetHashCode(), partitioning1b.GetHashCode()); + Assert.Equal(partitioning1_a, partitioning1_b); + Assert.Equal(partitioning1_a.GetHashCode(), partitioning1_b.GetHashCode()); + Assert.True(partitioning1_a.Equals((object)partitioning1_b)); - Assert.NotEqual(partitioning1a.GetHashCode(), partitioning2a.GetHashCode()); + Assert.NotEqual(partitioning1_a, partitioning2); + Assert.NotEqual(partitioning1_a.GetHashCode(), partitioning2.GetHashCode()); + Assert.False(partitioning1_a.Equals((object)partitioning2)); } } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs index bafb2c6f8..a73b14f3c 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs @@ -40,6 +40,7 @@ namespace Squidex.Domain.Apps.Core new AppContributorsConverter(), new AppPatternsConverter(), new ClaimsPrincipalConverter(), + new EnvelopeHeadersConverter(), new InstantConverter(), new JsonValueConverter(), new LanguageConverter(), diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs index 9d377646b..464585379 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs @@ -18,7 +18,7 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public void Should_set_and_get_timestamp() { - var timestamp = SystemClock.Instance.GetCurrentInstant(); + var timestamp = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); sut.SetTimestamp(timestamp); @@ -78,7 +78,7 @@ namespace Squidex.Infrastructure.EventSourcing sut.SetEventStreamNumber(eventStreamNumber); Assert.Equal(eventStreamNumber, sut.Headers.EventStreamNumber()); - Assert.Equal(eventStreamNumber, sut.Headers.GetInt64("EventStreamNumber")); + Assert.Equal(eventStreamNumber, sut.Headers.GetLong("EventStreamNumber")); } } } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index b7842bec0..a65750d97 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -64,7 +64,10 @@ namespace Squidex.Infrastructure.EventSourcing.Grains consumerName = eventConsumer.GetType().Name; A.CallTo(() => store.WithSnapshots(A.Ignored, consumerName, A>.Ignored)) - .Invokes(new Action>((t, key, a) => apply = a)) + .Invokes(new Action>((t, key, a) => + { + apply = a; + })) .Returns(persistence); A.CallTo(() => eventStore.CreateSubscription(A.Ignored, A.Ignored, A.Ignored)) @@ -79,7 +82,8 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => persistence.WriteSnapshotAsync(A.Ignored)) .Invokes(new Action(s => state = s)); - A.CallTo(() => formatter.Parse(eventData, true, null)).Returns(envelope); + A.CallTo(() => formatter.Parse(eventData, true, null)) + .Returns(envelope); sut = new MyEventConsumerGrain( x => eventConsumer, diff --git a/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs new file mode 100644 index 000000000..a5c00aae3 --- /dev/null +++ b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs @@ -0,0 +1,321 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Squidex.Infrastructure.Json.Objects +{ + public class JsonObjectTests + { + [Fact] + public void Should_make_correct_object_equal_comparisons() + { + var obj_count1_key1_val1_a = JsonValue.Object().Add("key1", 1); + var obj_count1_key1_val1_b = JsonValue.Object().Add("key1", 1); + + var obj_count1_key1_val2 = JsonValue.Object().Add("key1", 2); + var obj_count1_key2_val1 = JsonValue.Object().Add("key2", 1); + var obj_count2_key1_val1 = JsonValue.Object().Add("key1", 1).Add("key2", 2); + + var number = JsonValue.Create(1); + + Assert.Equal(obj_count1_key1_val1_a, obj_count1_key1_val1_b); + Assert.Equal(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key1_val1_b.GetHashCode()); + Assert.True(obj_count1_key1_val1_a.Equals((object)obj_count1_key1_val1_b)); + + Assert.NotEqual(obj_count1_key1_val1_a, obj_count1_key1_val2); + Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key1_val2.GetHashCode()); + Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count1_key1_val2)); + + Assert.NotEqual(obj_count1_key1_val1_a, obj_count1_key2_val1); + Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key2_val1.GetHashCode()); + Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count1_key2_val1)); + + Assert.NotEqual(obj_count1_key1_val1_a, obj_count2_key1_val1); + Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count2_key1_val1.GetHashCode()); + Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count2_key1_val1)); + + Assert.NotEqual(obj_count1_key1_val1_a, number); + Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), number.GetHashCode()); + Assert.False(obj_count1_key1_val1_a.Equals((object)number)); + } + + [Fact] + public void Should_make_correct_array_equal_comparisons() + { + var array_count1_val1_a = JsonValue.Array(1); + var array_count1_val1_b = JsonValue.Array(1); + + var array_count1_val2 = JsonValue.Array(2); + var array_count2_val1 = JsonValue.Array(1, 2); + + var number = JsonValue.Create(1); + + Assert.Equal(array_count1_val1_a, array_count1_val1_b); + Assert.Equal(array_count1_val1_a.GetHashCode(), array_count1_val1_b.GetHashCode()); + Assert.True(array_count1_val1_a.Equals((object)array_count1_val1_b)); + + Assert.NotEqual(array_count1_val1_a, array_count1_val2); + Assert.NotEqual(array_count1_val1_a.GetHashCode(), array_count1_val2.GetHashCode()); + Assert.False(array_count1_val1_a.Equals((object)array_count1_val2)); + + Assert.NotEqual(array_count1_val1_a, array_count2_val1); + Assert.NotEqual(array_count1_val1_a.GetHashCode(), array_count2_val1.GetHashCode()); + Assert.False(array_count1_val1_a.Equals((object)array_count2_val1)); + + Assert.NotEqual(array_count1_val1_a, number); + Assert.NotEqual(array_count1_val1_a.GetHashCode(), number.GetHashCode()); + Assert.False(array_count1_val1_a.Equals((object)number)); + } + + [Fact] + public void Should_make_correct_array_scalar_comparisons() + { + var number_val1_a = JsonValue.Create(1); + var number_val1_b = JsonValue.Create(1); + + var number_val2 = JsonValue.Create(2); + + var boolean = JsonValue.True; + + Assert.Equal(number_val1_a, number_val1_b); + Assert.Equal(number_val1_a.GetHashCode(), number_val1_b.GetHashCode()); + Assert.True(number_val1_a.Equals((object)number_val1_b)); + + Assert.NotEqual(number_val1_a, number_val2); + Assert.NotEqual(number_val1_a.GetHashCode(), number_val2.GetHashCode()); + Assert.False(number_val1_a.Equals((object)number_val2)); + + Assert.NotEqual(number_val1_a, boolean); + Assert.NotEqual(number_val1_a.GetHashCode(), boolean.GetHashCode()); + Assert.False(number_val1_a.Equals((object)boolean)); + } + + [Fact] + public void Should_make_correct_null_comparisons() + { + var null_a = JsonValue.Null; + var null_b = JsonValue.Null; + + var boolean = JsonValue.True; + + Assert.Equal(null_a, null_b); + Assert.Equal(null_a.GetHashCode(), null_b.GetHashCode()); + Assert.True(null_a.Equals((object)null_b)); + + Assert.NotEqual(null_a, boolean); + Assert.NotEqual(null_a.GetHashCode(), boolean.GetHashCode()); + Assert.False(null_a.Equals((object)boolean)); + } + + [Fact] + public void Should_cache_null() + { + Assert.Same(JsonValue.Null, JsonValue.Create((string)null)); + } + + [Fact] + public void Should_cache_true() + { + Assert.Same(JsonValue.True, JsonValue.Create(true)); + } + + [Fact] + public void Should_cache_false() + { + Assert.Same(JsonValue.False, JsonValue.Create(false)); + } + + [Fact] + public void Should_cache_empty() + { + Assert.Same(JsonValue.Empty, JsonValue.Create(string.Empty)); + } + + [Fact] + public void Should_cache_zero() + { + Assert.Same(JsonValue.Zero, JsonValue.Create(0)); + } + + [Fact] + public void Should_create_array() + { + var json = JsonValue.Array(1, "2"); + + Assert.Equal("[1, \"2\"]", json.ToJsonString()); + Assert.Equal("[1, \"2\"]", json.ToString()); + } + + [Fact] + public void Should_create_object() + { + var json = JsonValue.Object().Add("key1", 1).Add("key2", "2"); + + Assert.Equal("{\"key1\":1, \"key2\":\"2\"}", json.ToJsonString()); + Assert.Equal("{\"key1\":1, \"key2\":\"2\"}", json.ToString()); + } + + [Fact] + public void Should_create_number() + { + var json = JsonValue.Create(123); + + Assert.Equal("123", json.ToJsonString()); + Assert.Equal("123", json.ToString()); + } + + [Fact] + public void Should_create_boolean_true() + { + var json = JsonValue.Create(true); + + Assert.Equal("true", json.ToJsonString()); + Assert.Equal("true", json.ToString()); + } + + [Fact] + public void Should_create_boolean_false() + { + var json = JsonValue.Create(false); + + Assert.Equal("false", json.ToJsonString()); + Assert.Equal("false", json.ToString()); + } + + [Fact] + public void Should_create_string() + { + var json = JsonValue.Create("hi"); + + Assert.Equal("\"hi\"", json.ToJsonString()); + Assert.Equal("hi", json.ToString()); + } + + [Fact] + public void Should_create_null() + { + var json = JsonValue.Create((object)null); + + Assert.Equal("null", json.ToJsonString()); + Assert.Equal("null", json.ToString()); + } + + [Fact] + public void Should_create_arrays_in_different_ways() + { + var numbers = new[] + { + JsonValue.Array(1.0f, 2.0f), + JsonValue.Array(JsonValue.Create(1.0f), JsonValue.Create(2.0f)) + }; + + Assert.Single(numbers.Distinct()); + Assert.Single(numbers.Select(x => x.GetHashCode()).Distinct()); + } + + [Fact] + public void Should_create_number_from_types() + { + var numbers = new[] + { + JsonValue.Create(1.0f), + JsonValue.Create(1.0), + JsonValue.Create(1L), + JsonValue.Create(1) + }; + + Assert.Single(numbers.Distinct()); + Assert.Single(numbers.Select(x => x.GetHashCode()).Distinct()); + } + + [Fact] + public void Should_create_null_when_adding_null_to_array() + { + var array = JsonValue.Array(); + + array.Add(null); + + Assert.Same(JsonValue.Null, array[0]); + } + + [Fact] + public void Should_create_null_when_replacing_to_null_in_array() + { + var array = JsonValue.Array(1); + + array[0] = null; + + Assert.Same(JsonValue.Null, array[0]); + } + + [Fact] + public void Should_create_null_when_adding_null_to_object() + { + var obj = JsonValue.Object(); + + obj.Add("key", null); + + Assert.Same(JsonValue.Null, obj["key"]); + } + + [Fact] + public void Should_create_null_when_replacing_to_null_object() + { + var obj = JsonValue.Object(); + + obj["key"] = null; + + Assert.Same(JsonValue.Null, obj["key"]); + } + + [Fact] + public void Should_remove_value_from_object() + { + var obj = JsonValue.Object().Add("key", 1); + + obj.Remove("key"); + + Assert.False(obj.TryGetValue("key", out _)); + Assert.False(obj.ContainsKey("key")); + } + + [Fact] + public void Should_clear_values_from_object() + { + var obj = JsonValue.Object().Add("key", 1); + + obj.Clear(); + + Assert.False(obj.TryGetValue("key", out _)); + Assert.False(obj.ContainsKey("key")); + } + + [Fact] + public void Should_provide_collection_values_from_object() + { + var obj = JsonValue.Object().Add("11", "44").Add("22", "88"); + + var kvps = new[] + { + new KeyValuePair("11", JsonValue.Create("44")), + new KeyValuePair("22", JsonValue.Create("88")) + }; + + Assert.Equal(2, obj.Count); + + Assert.Equal(new[] { "11", "22" }, obj.Keys); + Assert.Equal(new[] { "44", "88" }, obj.Values.Select(x => x.ToString())); + + Assert.Equal(kvps, obj.ToArray()); + Assert.Equal(kvps, ((IEnumerable)obj).OfType>().ToArray()); + } + } +} diff --git a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs index bdf62cae4..44a09138a 100644 --- a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs +++ b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs @@ -40,49 +40,23 @@ namespace Squidex.Infrastructure var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); - var token1a = NamedId.Of(id1, "my-name1"); - var token1b = NamedId.Of(id1, "my-name1"); - var token1c = NamedId.Of(id1, "my-name2"); - var token2a = NamedId.Of(id2, "my-name1"); + var named_id1_name1_a = NamedId.Of(id1, "name1"); + var named_id1_name1_b = NamedId.Of(id1, "name1"); - Assert.True(token1a.Equals(token1b)); + var named_id2_name1 = NamedId.Of(id2, "name1"); + var named_id1_name2 = NamedId.Of(id1, "name2"); - Assert.False(token1a.Equals(token2a)); - Assert.False(token1a.Equals(token1c)); - } - - [Fact] - public void Should_make_correct_object_equal_comparisons() - { - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - - object token1a = NamedId.Of(id1, "my-name1"); - object token1b = NamedId.Of(id1, "my-name1"); - object token1c = NamedId.Of(id1, "my-name2"); - object token2a = NamedId.Of(id2, "my-name1"); - - Assert.True(token1a.Equals(token1b)); - - Assert.False(token1a.Equals(token2a)); - Assert.False(token1a.Equals(token1c)); - } - - [Fact] - public void Should_provide_correct_hash_codes() - { - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - - object token1a = NamedId.Of(id1, "my-name1"); - object token1b = NamedId.Of(id1, "my-name1"); - object token1c = NamedId.Of(id1, "my-name2"); - object token2a = NamedId.Of(id2, "my-name1"); + Assert.Equal(named_id1_name1_a, named_id1_name1_b); + Assert.Equal(named_id1_name1_a.GetHashCode(), named_id1_name1_b.GetHashCode()); + Assert.True(named_id1_name1_a.Equals((object)named_id1_name1_b)); - Assert.Equal(token1a.GetHashCode(), token1b.GetHashCode()); + Assert.NotEqual(named_id1_name1_a, named_id2_name1); + Assert.NotEqual(named_id1_name1_a.GetHashCode(), named_id2_name1.GetHashCode()); + Assert.False(named_id1_name1_a.Equals((object)named_id2_name1)); - Assert.NotEqual(token1a.GetHashCode(), token2a.GetHashCode()); - Assert.NotEqual(token1a.GetHashCode(), token1c.GetHashCode()); + Assert.NotEqual(named_id1_name1_a, named_id1_name2); + Assert.NotEqual(named_id1_name1_a.GetHashCode(), named_id1_name2.GetHashCode()); + Assert.False(named_id1_name1_a.Equals((object)named_id1_name2)); } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs b/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs index 09620ee0f..b7ddf5d99 100644 --- a/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs +++ b/tests/Squidex.Infrastructure.Tests/RefTokenTests.cs @@ -70,39 +70,23 @@ namespace Squidex.Infrastructure [Fact] public void Should_make_correct_equal_comparisons() { - var token1a = RefToken.Parse("client:client1"); - var token1b = RefToken.Parse("client:client1"); - var token2a = RefToken.Parse("client:client2"); + var token_type1_id1_a = RefToken.Parse("type1:client1"); + var token_type1_id1_b = RefToken.Parse("type1:client1"); - Assert.True(token1a.Equals(token1b)); + var token_type2_id1 = RefToken.Parse("type2:client1"); + var token_type1_id2 = RefToken.Parse("type1:client2"); - Assert.False(token1a.Equals(token2a)); - } - - [Fact] - public void Should_make_correct_object_equal_comparisons() - { - object token1a = RefToken.Parse("client:client1"); - object token1b = RefToken.Parse("client:client1"); - object token2a = RefToken.Parse("client:client2"); - - Assert.True(token1a.Equals(token1b)); - - Assert.False(token1a.Equals(token2a)); - Assert.False(token1b.Equals(token2a)); - } - - [Fact] - public void Should_provide_correct_hash_codes() - { - var token1a = RefToken.Parse("client:client1"); - var token1b = RefToken.Parse("client:client1"); - var token2a = RefToken.Parse("client:client2"); + Assert.Equal(token_type1_id1_a, token_type1_id1_b); + Assert.Equal(token_type1_id1_a.GetHashCode(), token_type1_id1_b.GetHashCode()); + Assert.True(token_type1_id1_a.Equals((object)token_type1_id1_b)); - Assert.Equal(token1a.GetHashCode(), token1b.GetHashCode()); + Assert.NotEqual(token_type1_id1_a, token_type2_id1); + Assert.NotEqual(token_type1_id1_a.GetHashCode(), token_type2_id1.GetHashCode()); + Assert.False(token_type1_id1_a.Equals((object)token_type2_id1)); - Assert.NotEqual(token1a.GetHashCode(), token2a.GetHashCode()); - Assert.NotEqual(token1b.GetHashCode(), token2a.GetHashCode()); + Assert.NotEqual(token_type1_id1_a, token_type1_id2); + Assert.NotEqual(token_type1_id1_a.GetHashCode(), token_type1_id2.GetHashCode()); + Assert.False(token_type1_id1_a.Equals((object)token_type1_id2)); } [Fact] diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs index 0eee70a27..5bb1b818a 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs @@ -28,6 +28,7 @@ namespace Squidex.Infrastructure.TestHelpers new ClaimsPrincipalConverter(), new InstantConverter(), new JsonValueConverter(), + new EnvelopeHeadersConverter(), new LanguageConverter(), new NamedGuidIdConverter(), new NamedLongIdConverter(), From dbc4a932dad9401caa0b9adeaada02c1a32134c2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 27 Nov 2018 15:08:18 +0100 Subject: [PATCH 08/15] Backup fixes. --- .../Backup/BackupReader.cs | 61 ++++----- .../Backup/BackupVersion.cs | 15 +++ .../Backup/BackupWriter.cs | 13 +- .../Backup/GuidMapper.cs | 2 +- .../Backup/Model/CompatibleStoredEvent.cs | 116 ++++++++++++++++++ .../DefaultEventDataFormatter.cs | 14 +-- .../Queries/FilterValue.cs | 20 ++- src/Squidex/appsettings.json | 2 +- .../ConvertContent/ContentConversionTests.cs | 4 +- .../ConvertContent/FieldConvertersTests.cs | 32 ++--- .../HandleRules/RuleServiceTests.cs | 2 +- .../Scripting/ContentDataObjectTests.cs | 6 +- .../Backup/BackupReaderWriterTests.cs | 25 ++-- .../Squidex.Domain.Apps.Entities.Tests.csproj | 5 - 14 files changed, 221 insertions(+), 96 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs index 2c3560334..660ca8627 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs @@ -8,13 +8,14 @@ using System; using System.IO; using System.IO.Compression; +using System.Linq; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Backup.Helpers; +using Squidex.Domain.Apps.Entities.Backup.Model; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.States; #pragma warning disable SA1401 // Fields must be private @@ -29,36 +30,6 @@ namespace Squidex.Domain.Apps.Entities.Backup private int readEvents; private int readAttachments; - private sealed class ComaptibleStoredEvent - { - [JsonProperty] - public string StreamName; - - [JsonProperty] - public string EventPosition; - - [JsonProperty] - public long EventStreamNumber; - - [JsonProperty] - public CompatibleEventData Data; - } - - private sealed class CompatibleEventData - { - [JsonProperty] - public string Type; - - [JsonProperty] - public JRaw Payload; - - [JsonProperty] - public EnvelopeHeaders Headers; - - [JsonProperty] - public EnvelopeHeaders Metadata; - } - public int ReadEvents { get { return readEvents; } @@ -151,20 +122,34 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = eventEntry.Open()) { - var storedEvent = serializer.Deserialize(stream); - - var src = storedEvent.Data; + var (streamName, data) = serializer.Deserialize(stream).ToEvent(); - var data = new EventData(src.Type, src.Headers ?? src.Metadata, src.Payload.ToString()); + MapHeaders(data); - var eventStream = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidOrNull); + var eventStream = streamNameResolver.WithNewId(streamName, guidMapper.NewGuidOrNull); var eventEnvelope = formatter.Parse(data, true, guidMapper.NewGuidOrValue); - await handler((eventStream, eventEnvelope)); + await handler((streamName, eventEnvelope)); } readEvents++; } } + + private void MapHeaders(EventData data) + { + foreach (var kvp in data.Headers.ToList()) + { + if (kvp.Value.Type == JsonValueType.String) + { + var newGuid = guidMapper.NewGuidOrNull(kvp.Value.ToString()); + + if (newGuid != null) + { + data.Headers.Add(kvp.Key, newGuid); + } + } + } + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs new file mode 100644 index 000000000..e6cddcd7d --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs @@ -0,0 +1,15 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Entities.Backup +{ + public enum BackupVersion + { + V2, + V1 + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs index 98363cb77..0f3033bcb 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs @@ -10,6 +10,7 @@ using System.IO; using System.IO.Compression; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Backup.Helpers; +using Squidex.Domain.Apps.Entities.Backup.Model; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; @@ -21,6 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { private readonly ZipArchive archive; private readonly IJsonSerializer serializer; + private readonly Func converter; private int writtenEvents; private int writtenAttachments; @@ -34,12 +36,17 @@ namespace Squidex.Domain.Apps.Entities.Backup get { return writtenAttachments; } } - public BackupWriter(IJsonSerializer serializer, Stream stream, bool keepOpen = false) + public BackupWriter(IJsonSerializer serializer, Stream stream, bool keepOpen = false, BackupVersion version = BackupVersion.V2) { Guard.NotNull(serializer, nameof(serializer)); this.serializer = serializer; + converter = + version == BackupVersion.V1 ? + new Func(CompatibleStoredEvent.V1) : + new Func(CompatibleStoredEvent.V2); + archive = new ZipArchive(stream, ZipArchiveMode.Create, keepOpen); } @@ -90,7 +97,9 @@ namespace Squidex.Domain.Apps.Entities.Backup using (var stream = eventEntry.Open()) { - serializer.Serialize(storedEvent, stream); + var @event = converter(storedEvent); + + serializer.Serialize(@event, stream); } writtenEvents++; diff --git a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs index a2ac8973d..e4b5aceb1 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs @@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Entities.Backup public Guid OldGuid(Guid newGuid) { - return newToOldGuid.GetOrDefault(newGuid); + return newToOldGuid.GetOrCreate(newGuid, x => x); } public string NewGuidOrNull(string value) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs b/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs new file mode 100644 index 000000000..ac84f1bb7 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs @@ -0,0 +1,116 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Squidex.Infrastructure.EventSourcing; + +#pragma warning disable SA1401 // Fields must be private + +namespace Squidex.Domain.Apps.Entities.Backup.Model +{ + public sealed class CompatibleStoredEvent + { + [JsonProperty("n")] + public NewEvent NewEvent; + + [JsonProperty] + public string StreamName; + + [JsonProperty] + public string EventPosition; + + [JsonProperty] + public long EventStreamNumber; + + [JsonProperty] + public CompatibleEventData Data; + + public static CompatibleStoredEvent V1(StoredEvent stored) + { + return new CompatibleStoredEvent + { + Data = CompatibleEventData.V1(stored.Data), + EventPosition = stored.EventPosition, + EventStreamNumber = stored.EventStreamNumber, + StreamName = stored.StreamName + }; + } + + public static CompatibleStoredEvent V2(StoredEvent stored) + { + return new CompatibleStoredEvent { NewEvent = NewEvent.V2(stored) }; + } + + public (string Stream, EventData Data) ToEvent() + { + if (NewEvent != null) + { + return NewEvent.ToEvent(); + } + else + { + return (StreamName, Data.ToData()); + } + } + } + + public sealed class CompatibleEventData + { + [JsonProperty] + public string Type; + + [JsonProperty] + public JRaw Payload; + + [JsonProperty] + public EnvelopeHeaders Metadata; + + public static CompatibleEventData V1(EventData data) + { + var payload = new JRaw(data.Payload); + + return new CompatibleEventData { Type = data.Type, Payload = payload, Metadata = data.Headers }; + } + + public EventData ToData() + { + return new EventData(Type, Metadata, Payload.ToString()); + } + } + + public sealed class NewEvent + { + [JsonProperty("t")] + public string EventType; + + [JsonProperty("s")] + public string StreamName; + + [JsonProperty("p")] + public string EventPayload; + + [JsonProperty("h")] + public EnvelopeHeaders EventHeaders; + + public static NewEvent V2(StoredEvent stored) + { + return new NewEvent + { + EventType = stored.Data.Type, + EventHeaders = stored.Data.Headers, + EventPayload = stored.Data.Payload, + StreamName = stored.StreamName + }; + } + + public (string Stream, EventData Data) ToEvent() + { + return (StreamName, new EventData(EventType, EventHeaders, EventPayload)); + } + } +} diff --git a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs index 247f6a918..d8e27f511 100644 --- a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Json; namespace Squidex.Infrastructure.EventSourcing { - public class DefaultEventDataFormatter : IEventDataFormatter + public sealed class DefaultEventDataFormatter : IEventDataFormatter { private readonly IJsonSerializer serializer; private readonly TypeNameRegistry typeNameRegistry; @@ -28,14 +28,14 @@ namespace Squidex.Infrastructure.EventSourcing public Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null) { var payloadType = typeNameRegistry.GetType(eventData.Type); - var payload = serializer.Deserialize(eventData.Payload, payloadType, stringConverter); + var payloadObj = serializer.Deserialize(eventData.Payload, payloadType, stringConverter); - if (migrate && payload is IMigratedEvent migratedEvent) + if (migrate && payloadObj is IMigratedEvent migratedEvent) { - payload = migratedEvent.Migrate(); + payloadObj = migratedEvent.Migrate(); } - var envelope = new Envelope(payload, eventData.Headers); + var envelope = new Envelope(payloadObj, eventData.Headers); return envelope; } @@ -50,11 +50,11 @@ namespace Squidex.Infrastructure.EventSourcing } var payloadType = typeNameRegistry.GetName(eventPayload.GetType()); - var payload = serializer.Serialize(envelope.Payload); + var payloadJson = serializer.Serialize(envelope.Payload); envelope.SetCommitId(commitId); - return new EventData(payloadType, envelope.Headers, payload); + return new EventData(payloadType, envelope.Headers, payloadJson); } } } diff --git a/src/Squidex.Infrastructure/Queries/FilterValue.cs b/src/Squidex.Infrastructure/Queries/FilterValue.cs index d340a4c88..7c07827f8 100644 --- a/src/Squidex.Infrastructure/Queries/FilterValue.cs +++ b/src/Squidex.Infrastructure/Queries/FilterValue.cs @@ -8,6 +8,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using NodaTime; @@ -126,26 +127,23 @@ namespace Squidex.Infrastructure.Queries { return $"[{string.Join(", ", list.OfType().Select(ToString).ToArray())}]"; } - else - { - return ToString(Value); - } + + return ToString(Value); } private string ToString(object value) { - if (ValueType == FilterValueType.String) - { - return $"'{value.ToString().Replace("'", "\\'")}'"; - } - else if (value == null) + if (value == null) { return "null"; } - else + + if (value is string s) { - return value.ToString(); + return $"'{s.Replace("'", "\\'")}'"; } + + return string.Format(CultureInfo.InvariantCulture, "{0}", value); } } } diff --git a/src/Squidex/appsettings.json b/src/Squidex/appsettings.json index 2907858cd..0b1d8de1a 100644 --- a/src/Squidex/appsettings.json +++ b/src/Squidex/appsettings.json @@ -253,7 +253,7 @@ "identity": { /* - * Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options. + * Enable password auth. Set this to false if you want to disable local login, leaving only 3rd party login options. */ "allowPasswordAuth": true, /* diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs index 43700a84c..a19acee27 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs @@ -174,7 +174,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new NamedContentData() .AddField("field1", new ContentFieldData() - .AddValue("en", new JsonArray("hello", "loved"))) + .AddValue("en", JsonValue.Array("hello", "loved"))) .AddField("field2", new ContentFieldData() .AddValue("iv", "world")); @@ -191,7 +191,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent new NamedContentData() .AddField("field1", new ContentFieldData() - .AddValue("en", new JsonArray(JsonValue.Object().Add("p1", "hello")))) + .AddValue("en", JsonValue.Array(JsonValue.Object().Add("p1", "hello")))) .AddField("field2", new ContentFieldData() .AddValue("iv", "world")); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index f9b90bce8..278e2bb3b 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs @@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("field1", 100) .Add("field2", 200) @@ -84,7 +84,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var expected = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("1", 100))); @@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("field1", 100) .Add("field2", 200) @@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var expected = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("field1", 100))); @@ -121,7 +121,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("1", 100) .Add("2", 200) @@ -132,7 +132,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var expected = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("1", 100))); @@ -145,7 +145,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var input = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("1", 100) .Add("2", 200) @@ -156,7 +156,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent var expected = new ContentFieldData() .AddValue("iv", - new JsonArray( + JsonValue.Array( JsonValue.Object() .Add("field1", 100))); @@ -422,11 +422,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JsonArray("url/to/1", "url/to/2")); + .AddValue("iv", JsonValue.Array("url/to/1", "url/to/2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "1" }), assetUrlGenerator)(source, assetsField); @@ -438,11 +438,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JsonArray("url/to/1", "url/to/2")); + .AddValue("iv", JsonValue.Array("url/to/1", "url/to/2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "*" }), assetUrlGenerator)(source, assetsField); @@ -454,11 +454,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var rtesult = FieldConverters.ResolveAssetUrls(new HashSet(new[] { "2" }), assetUrlGenerator)(source, assetsField); @@ -470,11 +470,11 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent { var source = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var expected = new ContentFieldData() - .AddValue("iv", new JsonArray("1", "2")); + .AddValue("iv", JsonValue.Array("1", "2")); var rtesult = FieldConverters.ResolveAssetUrls(null, assetUrlGenerator)(source, assetsField); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index 7a36445b2..90ce03bb3 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -171,7 +171,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { var @event = new ContentCreated { SchemaId = NamedId.Of(Guid.NewGuid(), "my-schema"), AppId = NamedId.Of(Guid.NewGuid(), "my-event") }; - var now = SystemClock.Instance.GetCurrentInstant(); + var now = Instant.FromUnixTimeSeconds(SystemClock.Instance.GetCurrentInstant().ToUnixTimeSeconds()); var ruleConfig = ValidRule(); var ruleEnvelope = Envelope.Create(@event); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs index 488c32b9f..271953f3c 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ContentDataObjectTests.cs @@ -155,13 +155,13 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JsonArray(1.0, 2.0))); + .AddValue("iv", JsonValue.Array(1.0, 2.0))); var expected = new NamedContentData() .AddField("number", new ContentFieldData() - .AddValue("iv", new JsonArray(1.0, 4.0, 5.0))); + .AddValue("iv", JsonValue.Array(1.0, 4.0, 5.0))); var result = ExecuteScript(original, @"data.number.iv = [data.number.iv[0], data.number.iv[1] + 2, 5]"); @@ -278,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Operations.Scripting new NamedContentData() .AddField("obj", new ContentFieldData() - .AddValue("iv", new JsonArray())); + .AddValue("iv", JsonValue.Array())); ExecuteScript(original, "data.obj.iv[0] = 1"); } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs index 1179039aa..7f31050f4 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs @@ -47,8 +47,10 @@ namespace Squidex.Domain.Apps.Entities.Backup .ReturnsLazily(new Func, string>((stream, idGenerator) => stream + "^2")); } - [Fact] - public async Task Should_write_and_read_events() + [Theory] + [InlineData(BackupVersion.V1)] + [InlineData(BackupVersion.V2)] + public async Task Should_write_and_read_events_to_backup(BackupVersion version) { var stream = new MemoryStream(); @@ -83,13 +85,13 @@ namespace Squidex.Domain.Apps.Entities.Backup var envelope = Envelope.Create(@event); envelope.Headers.Add(RandomGuid().ToString(), i); - envelope.Headers.Add("Id", RandomGuid()); + envelope.Headers.Add("Id", RandomGuid().ToString()); envelope.Headers.Add("Index", i); sourceEvents.Add(($"My-{RandomGuid()}", envelope)); } - using (var writer = new BackupWriter(serializer, stream, true)) + using (var writer = new BackupWriter(serializer, stream, true, version)) { foreach (var @event in sourceEvents) { @@ -149,13 +151,18 @@ namespace Squidex.Domain.Apps.Entities.Backup for (var i = 0; i < targetEvents.Count; i++) { - var lhs = targetEvents[i].Event.To(); - var rhs = sourceEvents[i].Event.To(); + var tgt = targetEvents[i].Event.To(); + var src = sourceEvents[i].Event.To(); - Assert.Equal(rhs.Payload.GuidRaw, reader.OldGuid(lhs.Payload.GuidRaw)); - Assert.Equal(rhs.Payload.GuidNamed.Id, reader.OldGuid(lhs.Payload.GuidNamed.Id)); + Assert.Equal(src.Payload.GuidRaw, reader.OldGuid(tgt.Payload.GuidRaw)); + Assert.Equal(src.Payload.GuidNamed.Id, reader.OldGuid(tgt.Payload.GuidNamed.Id)); - Assert.Equal(rhs.Headers.GetGuid("Id"), reader.OldGuid(lhs.Headers.GetGuid("Id"))); + Assert.NotEqual(src.Payload.GuidRaw, tgt.Payload.GuidRaw); + Assert.NotEqual(src.Payload.GuidNamed.Id, tgt.Payload.GuidNamed.Id); + + Assert.Equal(src.Headers.GetGuid("Id"), reader.OldGuid(tgt.Headers.GetGuid("Id"))); + + Assert.NotEqual(src.Headers.GetGuid("Id"), tgt.Headers.GetGuid("Id")); } } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index 79ec34354..a557808d2 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -5,11 +5,6 @@ 2.1.1 Squidex.Domain.Apps.Entities - - - - - From 265fa0aadb3531762392c8303c99d513258472ba Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 29 Nov 2018 19:17:09 +0100 Subject: [PATCH 09/15] Performance optimizations. --- .../Backup/GuidMapper.cs | 29 ++++++---- .../Json/Newtonsoft/JsonValueConverter.cs | 3 +- .../Json/Newtonsoft/NamedGuidIdConverter.cs | 12 ++--- .../Json/Newtonsoft/NamedLongIdConverter.cs | 12 ++--- .../Json/Newtonsoft/NamedStringIdConverter.cs | 18 ++++--- .../Json/Newtonsoft/RefTokenConverter.cs | 7 ++- .../Json/Objects/JsonObject.cs | 2 +- src/Squidex.Infrastructure/NamedId{T}.cs | 53 +++++++++++++++---- src/Squidex.Infrastructure/RefToken.cs | 44 +++++++++------ .../States/DefaultStreamNameResolver.cs | 2 +- .../StringExtensions.cs | 6 +-- .../Config/Domain/SerializationServices.cs | 1 - .../TestUtils.cs | 1 - .../Backup/BackupReaderWriterTests.cs | 26 ++++----- ...HeaderTests.cs => EnvelopeHeadersTests.cs} | 19 +++++-- .../Json/Objects/JsonObjectTests.cs | 44 +++++++++++++-- .../TestHelpers/JsonHelper.cs | 3 +- .../Migrations/ConvertEventStore.cs | 1 - 18 files changed, 194 insertions(+), 89 deletions(-) rename tests/Squidex.Infrastructure.Tests/EventSourcing/{EnvelopeHeaderTests.cs => EnvelopeHeadersTests.cs} (71%) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs index e4b5aceb1..4b1916655 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs @@ -16,6 +16,7 @@ namespace Squidex.Domain.Apps.Entities.Backup private static readonly int GuidLength = Guid.Empty.ToString().Length; private readonly Dictionary oldToNewGuid = new Dictionary(); private readonly Dictionary newToOldGuid = new Dictionary(); + private readonly Dictionary strings = new Dictionary(); public Guid OldGuid(Guid newGuid) { @@ -44,39 +45,49 @@ namespace Squidex.Domain.Apps.Entities.Backup private bool TryGenerateNewGuidString(string value, out string result) { - result = null; - if (value.Length == GuidLength) { + if (strings.TryGetValue(value, out result)) + { + return true; + } + if (Guid.TryParse(value, out var guid)) { var newGuid = GenerateNewGuid(guid); - result = newGuid.ToString(); + strings[value] = result = newGuid.ToString(); return true; } } + result = null; + return false; } private bool TryGenerateNewNamedId(string value, out string result) { - result = null; - - if (value.Length > GuidLength && value[GuidLength] == ',') + if (value.Length > GuidLength) { - if (Guid.TryParse(value.Substring(0, GuidLength), out var guid)) + if (strings.TryGetValue(value, out result)) { - var newGuid = GenerateNewGuid(guid); + return true; + } - result = newGuid + value.Substring(GuidLength); + if (NamedId.TryParse(value, Guid.TryParse, out var namedId)) + { + var newGuid = GenerateNewGuid(namedId.Id); + + strings[value] = result = new NamedId(newGuid, namedId.Name).ToString(); return true; } } + result = null; + return false; } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index fdbf6f97b..749cc9586 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -8,7 +8,6 @@ using System; using System.Globalization; using Newtonsoft.Json; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json.Objects; #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator @@ -162,7 +161,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft public override bool CanConvert(Type objectType) { - return objectType != typeof(EnvelopeHeaders) && typeof(IJsonValue).IsAssignableFrom(objectType); + return typeof(IJsonValue).IsAssignableFrom(objectType); } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs index f40e50481..f1b141060 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedGuidIdConverter.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { protected override void WriteValue(JsonWriter writer, NamedId value, JsonSerializer serializer) { - writer.WriteValue($"{value.Id},{value.Name}"); + writer.WriteValue(value.ToString()); } protected override NamedId ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) @@ -24,14 +24,12 @@ namespace Squidex.Infrastructure.Json.Newtonsoft throw new JsonException($"Expected String, but got {reader.TokenType}."); } - try + if (!NamedId.TryParse(reader.Value.ToString(), Guid.TryParse, out var result)) { - return NamedId.Parse(reader.Value.ToString(), Guid.TryParse); - } - catch (ArgumentException ex) - { - throw new JsonException(ex.Message); + throw new JsonException("Named id must have more than 2 parts divided by commata."); } + + return result; } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs index b90415b71..d984e572b 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedLongIdConverter.cs @@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { protected override void WriteValue(JsonWriter writer, NamedId value, JsonSerializer serializer) { - writer.WriteValue($"{value.Id},{value.Name}"); + writer.WriteValue(value.ToString()); } protected override NamedId ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) @@ -24,14 +24,12 @@ namespace Squidex.Infrastructure.Json.Newtonsoft throw new JsonException($"Expected String, but got {reader.TokenType}."); } - try + if (!NamedId.TryParse(reader.Value.ToString(), long.TryParse, out var result)) { - return NamedId.Parse(reader.Value.ToString(), long.TryParse); - } - catch (ArgumentException ex) - { - throw new JsonException(ex.Message); + throw new JsonException("Named id must have at least 2 parts divided by commata."); } + + return result; } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs index 78f39fb1f..573cbaa57 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/NamedStringIdConverter.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Linq; using Newtonsoft.Json; namespace Squidex.Infrastructure.Json.Newtonsoft @@ -15,7 +14,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { protected override void WriteValue(JsonWriter writer, NamedId value, JsonSerializer serializer) { - writer.WriteValue($"{value.Id},{value.Name}"); + writer.WriteValue(value.ToString()); } protected override NamedId ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer) @@ -25,14 +24,19 @@ namespace Squidex.Infrastructure.Json.Newtonsoft throw new JsonException($"Expected String, but got {reader.TokenType}."); } - var parts = reader.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length < 2) + if (!NamedId.TryParse(reader.Value.ToString(), ParseString, out var result)) { - throw new JsonException("Named id must have more than 2 parts divided by colon."); + throw new JsonException("Named id must have at least 2 parts divided by commata."); } - return NamedId.Of(parts[0], string.Join(",", parts.Skip(1))); + return result; + } + + private static bool ParseString(string value, out string result) + { + result = value; + + return true; } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs index 9f12baacc..988f3ff95 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/RefTokenConverter.cs @@ -24,7 +24,12 @@ namespace Squidex.Infrastructure.Json.Newtonsoft throw new JsonException($"Expected String, but got {reader.TokenType}."); } - return RefToken.Parse(reader.Value.ToString()); + if (!RefToken.TryParse(reader.Value.ToString(), out var result)) + { + throw new JsonException("Named id must have at least 2 parts divided by colon."); + } + + return result; } } } \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs index 7f1f6ceda..c4974ce47 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonObject.cs @@ -50,7 +50,7 @@ namespace Squidex.Infrastructure.Json.Objects get { return JsonValueType.Array; } } - public JsonObject() + internal JsonObject() { inner = new Dictionary(); } diff --git a/src/Squidex.Infrastructure/NamedId{T}.cs b/src/Squidex.Infrastructure/NamedId{T}.cs index e8f99f6d4..eeeab8ab9 100644 --- a/src/Squidex.Infrastructure/NamedId{T}.cs +++ b/src/Squidex.Infrastructure/NamedId{T}.cs @@ -6,7 +6,8 @@ // ========================================================================== using System; -using System.Linq; + +#pragma warning disable RECS0108 // Warns about static fields in generic types namespace Squidex.Infrastructure { @@ -14,6 +15,8 @@ namespace Squidex.Infrastructure public sealed class NamedId : IEquatable> { + private static readonly int GuidLength = Guid.Empty.ToString().Length; + public T Id { get; } public string Name { get; } @@ -48,23 +51,51 @@ namespace Squidex.Infrastructure return (Id.GetHashCode() * 397) ^ Name.GetHashCode(); } - public static NamedId Parse(string value, Parser parser) + public static bool TryParse(string value, Parser parser, out NamedId result) { - Guard.NotNull(value, nameof(value)); - - var parts = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length < 2) + if (value != null) { - throw new ArgumentException("Named id must have more than 2 parts divided by commata."); + if (typeof(T) == typeof(Guid)) + { + if (value.Length > GuidLength + 1 && value[GuidLength] == ',') + { + if (parser(value.Substring(0, GuidLength), out var id)) + { + result = new NamedId(id, value.Substring(GuidLength + 1)); + + return true; + } + } + } + else + { + var index = value.IndexOf(','); + + if (index > 0 && index < value.Length - 1) + { + if (parser(value.Substring(0, index), out var id)) + { + result = new NamedId(id, value.Substring(index + 1)); + + return true; + } + } + } } - if (!parser(parts[0], out var id)) + result = null; + + return false; + } + + public static NamedId Parse(string value, Parser parser) + { + if (!TryParse(value, parser, out var result)) { - throw new ArgumentException("Named id must be a valid guid."); + throw new ArgumentException("Named id must have at least 2 parts divided by commata.", nameof(value)); } - return new NamedId(id, string.Join(",", parts.Skip(1))); + return result; } } } diff --git a/src/Squidex.Infrastructure/RefToken.cs b/src/Squidex.Infrastructure/RefToken.cs index d9298b6aa..5d0c1d215 100644 --- a/src/Squidex.Infrastructure/RefToken.cs +++ b/src/Squidex.Infrastructure/RefToken.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Linq; namespace Squidex.Infrastructure { @@ -26,20 +25,6 @@ namespace Squidex.Infrastructure Identifier = identifier; } - public static RefToken Parse(string input) - { - Guard.NotNullOrEmpty(input, nameof(input)); - - var parts = input.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length < 2) - { - throw new ArgumentException("Input must have more than 2 parts divided by colon.", nameof(input)); - } - - return new RefToken(parts[0], string.Join(":", parts.Skip(1))); - } - public override string ToString() { return $"{Type}:{Identifier}"; @@ -59,5 +44,34 @@ namespace Squidex.Infrastructure { return (Type.GetHashCode() * 397) ^ Identifier.GetHashCode(); } + + public static bool TryParse(string value, out RefToken result) + { + if (value != null) + { + var idx = value.IndexOf(':'); + + if (idx > 0 && idx < value.Length - 1) + { + result = new RefToken(value.Substring(0, idx), value.Substring(idx + 1)); + + return true; + } + } + + result = null; + + return false; + } + + public static RefToken Parse(string value) + { + if (!TryParse(value, out var result)) + { + throw new ArgumentException("Ref token must have more than 2 parts divided by colon.", nameof(value)); + } + + return result; + } } } diff --git a/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs b/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs index 582011a20..e7b30bca6 100644 --- a/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs +++ b/src/Squidex.Infrastructure/States/DefaultStreamNameResolver.cs @@ -1,4 +1,4 @@ -// ========================================================================== + // ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschränkt) diff --git a/src/Squidex.Infrastructure/StringExtensions.cs b/src/Squidex.Infrastructure/StringExtensions.cs index 41b23ac9e..bfa46b900 100644 --- a/src/Squidex.Infrastructure/StringExtensions.cs +++ b/src/Squidex.Infrastructure/StringExtensions.cs @@ -331,7 +331,7 @@ namespace Squidex.Infrastructure return string.Empty; } - var sb = new StringBuilder(); + var sb = new StringBuilder(value.Length); var last = NullChar; var length = 0; @@ -387,7 +387,7 @@ namespace Squidex.Infrastructure return string.Empty; } - var sb = new StringBuilder(); + var sb = new StringBuilder(value.Length); var length = 0; @@ -429,7 +429,7 @@ namespace Squidex.Infrastructure return string.Empty; } - var sb = new StringBuilder(); + var sb = new StringBuilder(value.Length); var last = NullChar; var length = 0; diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 0baee6338..500a1aad3 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -19,7 +19,6 @@ using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Events; using Squidex.Extensions.Actions; using Squidex.Infrastructure; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs index a73b14f3c..dec0ade55 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs @@ -16,7 +16,6 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; using Xunit; diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs index 7f31050f4..01cf6bc52 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using FakeItEasy; using Squidex.Domain.Apps.Core; @@ -77,8 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Backup GuidRaw = RandomGuid(), Values = new Dictionary { - [RandomGuid()] = $"name{i}_1", - [RandomGuid()] = $"name{i}_1", + [RandomGuid()] = "Key" } }; @@ -149,20 +149,22 @@ namespace Squidex.Domain.Apps.Entities.Backup targetEvents.Add(@event); }); - for (var i = 0; i < targetEvents.Count; i++) + void CompareGuid(Guid source, Guid target) { - var tgt = targetEvents[i].Event.To(); - var src = sourceEvents[i].Event.To(); - - Assert.Equal(src.Payload.GuidRaw, reader.OldGuid(tgt.Payload.GuidRaw)); - Assert.Equal(src.Payload.GuidNamed.Id, reader.OldGuid(tgt.Payload.GuidNamed.Id)); + Assert.Equal(source, reader.OldGuid(target)); + Assert.NotEqual(source, target); + } - Assert.NotEqual(src.Payload.GuidRaw, tgt.Payload.GuidRaw); - Assert.NotEqual(src.Payload.GuidNamed.Id, tgt.Payload.GuidNamed.Id); + for (var i = 0; i < targetEvents.Count; i++) + { + var source = targetEvents[i].Event.To(); - Assert.Equal(src.Headers.GetGuid("Id"), reader.OldGuid(tgt.Headers.GetGuid("Id"))); + var target = sourceEvents[i].Event.To(); - Assert.NotEqual(src.Headers.GetGuid("Id"), tgt.Headers.GetGuid("Id")); + CompareGuid(target.Payload.Values.First().Key, source.Payload.Values.First().Key); + CompareGuid(target.Payload.GuidRaw, source.Payload.GuidRaw); + CompareGuid(target.Payload.GuidNamed.Id, source.Payload.GuidNamed.Id); + CompareGuid(target.Headers.GetGuid("Id"), source.Headers.GetGuid("Id")); } } } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeadersTests.cs similarity index 71% rename from tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs rename to tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeadersTests.cs index 21b1c7363..4a2a0286e 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeaderTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeHeadersTests.cs @@ -7,11 +7,12 @@ using System.Linq; using Squidex.Infrastructure.Json.Objects; +using Squidex.Infrastructure.TestHelpers; using Xunit; namespace Squidex.Infrastructure.EventSourcing { - public class EnvelopeHeaderTests + public class EnvelopeHeadersTests { [Fact] public void Should_create_headers() @@ -24,7 +25,8 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public void Should_create_headers_as_copy() { - var source = new JsonObject().Add("Key1", 123); + var source = JsonValue.Object().Add("Key1", 123); + var headers = new EnvelopeHeaders(source); CompareHeaders(headers, source); @@ -33,14 +35,23 @@ namespace Squidex.Infrastructure.EventSourcing [Fact] public void Should_clone_headers() { - var source = new JsonObject().Add("Key1", 123); - var headers = new EnvelopeHeaders(source); + var headers = new EnvelopeHeaders(JsonValue.Object().Add("Key1", 123)); var clone = headers.Clone(); CompareHeaders(headers, clone); } + [Fact] + public void Should_serialize_and_deserialize() + { + var value = new EnvelopeHeaders(JsonValue.Object().Add("Key1", 123)); + + var deserialized = value.SerializeAndDeserialize(); + + CompareHeaders(deserialized, value); + } + private static void CompareHeaders(JsonObject lhs, JsonObject rhs) { foreach (var key in lhs.Keys.Concat(rhs.Keys).Distinct()) diff --git a/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs index a5c00aae3..5ad034901 100644 --- a/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs @@ -5,9 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections; using System.Collections.Generic; using System.Linq; +using NodaTime; using Xunit; namespace Squidex.Infrastructure.Json.Objects @@ -119,6 +121,10 @@ namespace Squidex.Infrastructure.Json.Objects public void Should_cache_null() { Assert.Same(JsonValue.Null, JsonValue.Create((string)null)); + Assert.Same(JsonValue.Null, JsonValue.Create((bool?)null)); + Assert.Same(JsonValue.Null, JsonValue.Create((double?)null)); + Assert.Same(JsonValue.Null, JsonValue.Create((object)null)); + Assert.Same(JsonValue.Null, JsonValue.Create((Instant?)null)); } [Fact] @@ -145,6 +151,28 @@ namespace Squidex.Infrastructure.Json.Objects Assert.Same(JsonValue.Zero, JsonValue.Create(0)); } + [Fact] + public void Should_boolean_from_object() + { + Assert.Equal(JsonValue.True, JsonValue.Create((object)true)); + } + + [Fact] + public void Should_create_value_from_instant() + { + var instant = Instant.FromUnixTimeSeconds(4123125455); + + Assert.Equal(instant.ToString(), JsonValue.Create(instant).ToString()); + } + + [Fact] + public void Should_create_value_from_instant_object() + { + var instant = Instant.FromUnixTimeSeconds(4123125455); + + Assert.Equal(instant.ToString(), JsonValue.Create((object)instant).ToString()); + } + [Fact] public void Should_create_array() { @@ -226,10 +254,12 @@ namespace Squidex.Infrastructure.Json.Objects { var numbers = new[] { - JsonValue.Create(1.0f), - JsonValue.Create(1.0), - JsonValue.Create(1L), - JsonValue.Create(1) + JsonValue.Create(12.0f), + JsonValue.Create(12.0), + JsonValue.Create(12L), + JsonValue.Create(12), + JsonValue.Create((object)12.0d), + JsonValue.Create((double?)12.0d) }; Assert.Single(numbers.Distinct()); @@ -317,5 +347,11 @@ namespace Squidex.Infrastructure.Json.Objects Assert.Equal(kvps, obj.ToArray()); Assert.Equal(kvps, ((IEnumerable)obj).OfType>().ToArray()); } + + [Fact] + public void Should_throw_exception_when_creation_value_from_invalid_type() + { + Assert.Throws(() => JsonValue.Create(Guid.Empty)); + } } } diff --git a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs index 5bb1b818a..c3b088571 100644 --- a/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs +++ b/tests/Squidex.Infrastructure.Tests/TestHelpers/JsonHelper.cs @@ -8,7 +8,6 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; @@ -27,8 +26,8 @@ namespace Squidex.Infrastructure.TestHelpers ContractResolver = new ConverterContractResolver( new ClaimsPrincipalConverter(), new InstantConverter(), - new JsonValueConverter(), new EnvelopeHeadersConverter(), + new JsonValueConverter(), new LanguageConverter(), new NamedGuidIdConverter(), new NamedLongIdConverter(), diff --git a/tools/Migrate_01/Migrations/ConvertEventStore.cs b/tools/Migrate_01/Migrations/ConvertEventStore.cs index 37c6a476d..4a4fc7e7f 100644 --- a/tools/Migrate_01/Migrations/ConvertEventStore.cs +++ b/tools/Migrate_01/Migrations/ConvertEventStore.cs @@ -12,7 +12,6 @@ using MongoDB.Driver; using Newtonsoft.Json.Linq; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Migrations; -using Squidex.Infrastructure.MongoDb; namespace Migrate_01.Migrations { From 038935d0a837b71b55d1408e3154ce601020f170 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Thu, 29 Nov 2018 21:13:59 +0100 Subject: [PATCH 10/15] Better json types --- .../EventSourcing/EnvelopeExtensions.cs | 2 +- .../Json/Newtonsoft/JsonValueConverter.cs | 10 +++--- .../Json/Objects/JsonBoolean.cs | 30 +++++++++++++++++ .../Json/Objects/JsonNumber.cs | 27 +++++++++++++++ .../Json/Objects/JsonScalar.cs | 33 +++++-------------- .../Json/Objects/JsonString.cs | 27 +++++++++++++++ .../Json/Objects/JsonValue.cs | 12 +++---- 7 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonBoolean.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs create mode 100644 src/Squidex.Infrastructure/Json/Objects/JsonString.cs diff --git a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs index 968eadf3c..e08a6ec4f 100644 --- a/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs +++ b/src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs @@ -91,7 +91,7 @@ namespace Squidex.Infrastructure.EventSourcing { if (obj.TryGetValue(key, out var v)) { - if (v is JsonScalar number) + if (v is JsonNumber number) { return (long)number.Value; } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index 749cc9586..917099317 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -115,13 +115,13 @@ namespace Squidex.Infrastructure.Json.Newtonsoft case JsonNull n: writer.WriteNull(); break; - case JsonScalar s: + case JsonBoolean s: writer.WriteValue(s.Value); break; - case JsonScalar s: + case JsonString s: writer.WriteValue(s.Value); break; - case JsonScalar s: + case JsonNumber s: if (s.Value % 1 == 0) { @@ -136,9 +136,9 @@ namespace Squidex.Infrastructure.Json.Newtonsoft case JsonArray array: writer.WriteStartArray(); - foreach (var item in array) + for (var i = 0; i < array.Count; i++) { - WriteJson(writer, item); + WriteJson(writer, array[i]); } writer.WriteEndArray(); diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonBoolean.cs b/src/Squidex.Infrastructure/Json/Objects/JsonBoolean.cs new file mode 100644 index 000000000..de4f1ddf0 --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonBoolean.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonBoolean : JsonScalar + { + public static readonly JsonBoolean True = new JsonBoolean(true); + public static readonly JsonBoolean False = new JsonBoolean(false); + + public override JsonValueType Type + { + get { return JsonValueType.Boolean; } + } + + private JsonBoolean(bool value) + : base(value) + { + } + + public override string ToString() + { + return Value ? "true" : "false"; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs b/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs new file mode 100644 index 000000000..4b0f3220f --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonNumber : JsonScalar + { + public override JsonValueType Type + { + get { return JsonValueType.Number; } + } + + internal JsonNumber(double value) + : base(value) + { + } + + public override string ToJsonString() + { + return $"{Value}"; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs index bea068163..5ea9c2bd7 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonScalar.cs @@ -9,22 +9,15 @@ using System; namespace Squidex.Infrastructure.Json.Objects { - public sealed class JsonScalar : IJsonValue, IEquatable> + public abstract class JsonScalar : IJsonValue, IEquatable> { - private readonly T value; + public abstract JsonValueType Type { get; } - public JsonValueType Type { get; } + public T Value { get; } - public T Value + protected JsonScalar(T value) { - get { return value; } - } - - internal JsonScalar(JsonValueType type, T value) - { - Type = type; - - this.value = value; + Value = value; } public override bool Equals(object obj) @@ -39,31 +32,21 @@ namespace Squidex.Infrastructure.Json.Objects public bool Equals(JsonScalar other) { - return other != null && other.Type == Type && Equals(other.value, value); + return other != null && other.Type == Type && Equals(other.Value, Value); } public override int GetHashCode() { - return value.GetHashCode(); + return Value.GetHashCode(); } public override string ToString() { - if (Type == JsonValueType.Boolean) - { - return Equals(Value, true) ? "true" : "false"; - } - return Value.ToString(); } - public string ToJsonString() + public virtual string ToJsonString() { - if (Type == JsonValueType.String) - { - return $"\"{value}\""; - } - return ToString(); } } diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonString.cs b/src/Squidex.Infrastructure/Json/Objects/JsonString.cs new file mode 100644 index 000000000..4aeccc93f --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Objects/JsonString.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Json.Objects +{ + public sealed class JsonString : JsonScalar + { + public override JsonValueType Type + { + get { return JsonValueType.String; } + } + + internal JsonString(string value) + : base(value) + { + } + + public override string ToJsonString() + { + return $"\"{Value}\""; + } + } +} diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs index 5022aadab..922039f5e 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonValue.cs @@ -14,14 +14,14 @@ namespace Squidex.Infrastructure.Json.Objects { public static class JsonValue { - public static readonly IJsonValue Empty = new JsonScalar(JsonValueType.String, string.Empty); + public static readonly IJsonValue Empty = new JsonString(string.Empty); - public static readonly IJsonValue True = new JsonScalar(JsonValueType.Boolean, true); - public static readonly IJsonValue False = new JsonScalar(JsonValueType.Boolean, false); + public static readonly IJsonValue True = JsonBoolean.True; + public static readonly IJsonValue False = JsonBoolean.False; public static readonly IJsonValue Null = JsonNull.Null; - public static readonly IJsonValue Zero = new JsonScalar(JsonValueType.Number, 0); + public static readonly IJsonValue Zero = new JsonNumber(0); public static JsonArray Array() { @@ -85,7 +85,7 @@ namespace Squidex.Infrastructure.Json.Objects return Zero; } - return new JsonScalar(JsonValueType.Number, value); + return new JsonNumber(value); } public static IJsonValue Create(Instant? value) @@ -130,7 +130,7 @@ namespace Squidex.Infrastructure.Json.Objects return Empty; } - return new JsonScalar(JsonValueType.String, value); + return new JsonString(value); } } } From dbc80adc454888acadc5e1881fd2818681ac189a Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Fri, 30 Nov 2018 16:03:32 +0100 Subject: [PATCH 11/15] Even less newtonsoft. --- .../ElasticSearchActionHandler.cs | 12 ++--- .../Actions/Medium/MediumActionHandler.cs | 53 +++++++++++-------- .../Rules/RuleCreated.cs | 2 +- .../Schemas/FieldAdded.cs | 2 +- .../Json/Objects/JsonNumber.cs | 6 ++- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs index 3688f7f75..991725964 100644 --- a/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; using Elasticsearch.Net; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; @@ -62,8 +61,7 @@ namespace Squidex.Extensions.Actions.ElasticSearch var json = ToJson(contentEvent); - ruleJob.Content = JObject.Parse(json); - ruleJob.Content["objectID"] = contentId; + ruleJob.Content = $"{{ \"objectId\": \"{contentId}\", {json.Substring(1)}"; } ruleJob.Username = action.Username; @@ -88,9 +86,7 @@ namespace Squidex.Extensions.Actions.ElasticSearch { if (job.Content != null) { - var doc = job.Content.ToString(); - - var response = await client.IndexAsync(job.IndexName, job.IndexType, job.ContentId, doc); + var response = await client.IndexAsync(job.IndexName, job.IndexType, job.ContentId, job.Content); return (response.Body, response.OriginalException); } @@ -118,10 +114,10 @@ namespace Squidex.Extensions.Actions.ElasticSearch public string ContentId { get; set; } + public string Content { get; set; } + public string IndexName { get; set; } public string IndexType { get; set; } - - public JObject Content { get; set; } } } diff --git a/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs b/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs index e3fe88fae..2aec919c6 100644 --- a/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs +++ b/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs @@ -9,11 +9,10 @@ using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Infrastructure.Http; +using Squidex.Infrastructure.Json; namespace Squidex.Extensions.Actions.Medium { @@ -22,53 +21,61 @@ namespace Squidex.Extensions.Actions.Medium private const string Description = "Post to medium"; private readonly IHttpClientFactory httpClientFactory; + private readonly IJsonSerializer serializer; - public MediumActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory) + private sealed class UserResponse + { + public UserResponseData Data { get; set; } + } + + private sealed class UserResponseData + { + public string Id { get; set; } + } + + public MediumActionHandler(RuleEventFormatter formatter, IHttpClientFactory httpClientFactory, IJsonSerializer serializer) : base(formatter) { this.httpClientFactory = httpClientFactory; + + this.serializer = serializer; } protected override (string Description, MediumJob Data) CreateJob(EnrichedEvent @event, MediumAction action) { - var requestBody = - new JObject( - new JProperty("title", Format(action.Title, @event)), - new JProperty("contentFormat", action.IsHtml ? "html" : "markdown"), - new JProperty("content", Format(action.Content, @event)), - new JProperty("canonicalUrl", Format(action.CanonicalUrl, @event)), - new JProperty("tags", ParseTags(@event, action))); - - var ruleJob = new MediumJob + var ruleJob = new MediumJob { AccessToken = action.AccessToken, PublicationId = action.PublicationId }; + + var requestBody = new { - AccessToken = action.AccessToken, - PublicationId = action.PublicationId, - RequestBody = requestBody.ToString(Formatting.Indented) + title = Format(action.Title, @event), + contentFormat = action.IsHtml ? "html" : "markdown", + content = Format(action.Content, @event), + canonicalUrl = Format(action.CanonicalUrl, @event), + tags = ParseTags(@event, action) }; + ruleJob.RequestBody = ToJson(requestBody); + return (Description, ruleJob); } - private JArray ParseTags(EnrichedEvent @event, MediumAction action) + private string[] ParseTags(EnrichedEvent @event, MediumAction action) { if (string.IsNullOrWhiteSpace(action.Tags)) { return null; } - string[] tags; try { var jsonTags = Format(action.Tags, @event); - tags = JsonConvert.DeserializeObject(jsonTags); + return serializer.Deserialize(jsonTags); } catch { - tags = action.Tags.Split(','); + return action.Tags.Split(','); } - - return new JArray(tags); } protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(MediumJob job) @@ -96,9 +103,9 @@ namespace Squidex.Extensions.Actions.Medium response = await httpClient.SendAsync(meRequest); var responseString = await response.Content.ReadAsStringAsync(); - var responseJson = JToken.Parse(responseString); + var responseJson = serializer.Deserialize(responseString); - var id = responseJson["data"]["id"].ToString(); + var id = responseJson.Data?.Id; path = $"v1/users/{id}/posts"; } diff --git a/src/Squidex.Domain.Apps.Events/Rules/RuleCreated.cs b/src/Squidex.Domain.Apps.Events/Rules/RuleCreated.cs index 10679e200..bab06a970 100644 --- a/src/Squidex.Domain.Apps.Events/Rules/RuleCreated.cs +++ b/src/Squidex.Domain.Apps.Events/Rules/RuleCreated.cs @@ -17,4 +17,4 @@ namespace Squidex.Domain.Apps.Events.Rules public RuleAction Action { get; set; } } -} +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs b/src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs index 7b136e947..d73358c30 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/FieldAdded.cs @@ -19,4 +19,4 @@ namespace Squidex.Domain.Apps.Events.Schemas public FieldProperties Properties { get; set; } } -} +} \ No newline at end of file diff --git a/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs b/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs index 4b0f3220f..25c3c573f 100644 --- a/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs +++ b/src/Squidex.Infrastructure/Json/Objects/JsonNumber.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Globalization; + namespace Squidex.Infrastructure.Json.Objects { public sealed class JsonNumber : JsonScalar @@ -19,9 +21,9 @@ namespace Squidex.Infrastructure.Json.Objects { } - public override string ToJsonString() + public override string ToString() { - return $"{Value}"; + return Value.ToString(CultureInfo.InvariantCulture); } } } From ac1ccac34cd53909805a18df46e3b23b60cf643c Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 1 Dec 2018 12:59:54 +0100 Subject: [PATCH 12/15] More optimizations. --- .../MongoDb/BsonJsonReader.cs | 20 +++++----- .../MongoDb/MongoExtensions.cs | 5 +-- .../Newtonsoft/ConverterContractResolver.cs | 39 +++++++++++++++++-- .../Newtonsoft/EnvelopeHeadersConverter.cs | 6 +++ .../Json/Newtonsoft/ISupportedTypes.cs | 17 ++++++++ .../Json/Newtonsoft/InstantConverter.cs | 10 +++++ .../Json/Newtonsoft/JsonClassConverter.cs | 16 +++++--- .../Json/Newtonsoft/JsonValueConverter.cs | 21 +++++++++- .../Log/JsonLogWriter.cs | 28 ++++++++----- .../Config/Domain/SerializationServices.cs | 4 -- src/Squidex/Squidex.csproj | 1 - 11 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 src/Squidex.Infrastructure/Json/Newtonsoft/ISupportedTypes.cs diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonReader.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonReader.cs index acb8708fa..f801aeddc 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonReader.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonJsonReader.cs @@ -37,16 +37,6 @@ namespace Squidex.Infrastructure.MongoDb { SetToken(NewtonsoftJsonToken.PropertyName, bsonReader.ReadName().UnescapeBson()); } - else if (bsonReader.State == BsonReaderState.EndOfDocument) - { - SetToken(NewtonsoftJsonToken.EndObject); - bsonReader.ReadEndDocument(); - } - else if (bsonReader.State == BsonReaderState.EndOfArray) - { - SetToken(NewtonsoftJsonToken.EndArray); - bsonReader.ReadEndArray(); - } else if (bsonReader.State == BsonReaderState.Value) { switch (bsonReader.CurrentBsonType) @@ -95,6 +85,16 @@ namespace Squidex.Infrastructure.MongoDb throw new NotSupportedException(); } } + else if (bsonReader.State == BsonReaderState.EndOfDocument) + { + SetToken(NewtonsoftJsonToken.EndObject); + bsonReader.ReadEndDocument(); + } + else if (bsonReader.State == BsonReaderState.EndOfArray) + { + SetToken(NewtonsoftJsonToken.EndArray); + bsonReader.ReadEndArray(); + } if (bsonReader.State == BsonReaderState.Initial) { diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs index 3681a4aa7..d8baba185 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/MongoExtensions.cs @@ -101,10 +101,7 @@ namespace Squidex.Infrastructure.MongoDb { var update = updater(Builders.Update.Set(x => x.Version, newVersion)); - await collection.UpdateOneAsync(x => x.Id.Equals(key) && x.Version == oldVersion, - update - .Set(x => x.Version, newVersion), - Upsert); + await collection.UpdateOneAsync(x => x.Id.Equals(key) && x.Version == oldVersion, update, Upsert); } catch (MongoWriteException ex) { diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs index e626d5e1d..53b203da3 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -14,10 +15,23 @@ namespace Squidex.Infrastructure.Json.Newtonsoft public sealed class ConverterContractResolver : CamelCasePropertyNamesContractResolver { private readonly JsonConverter[] converters; + private readonly object lockObject = new object(); + private Dictionary converterCache = new Dictionary(); public ConverterContractResolver(params JsonConverter[] converters) { this.converters = converters; + + foreach (var converter in converters) + { + if (converter is ISupportedTypes supportedTypes) + { + foreach (var type in supportedTypes.SupportedTypes) + { + converterCache[type] = converter; + } + } + } } protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) @@ -38,15 +52,32 @@ namespace Squidex.Infrastructure.Json.Newtonsoft return result; } - foreach (var converter in converters) + var cache = converterCache; + + if (cache == null || !cache.TryGetValue(objectType, out result)) { - if (converter.CanConvert(objectType)) + foreach (var converter in converters) + { + if (converter.CanConvert(objectType)) + { + result = converter; + } + } + + lock (lockObject) { - return converter; + cache = converterCache; + + var updatedCache = (cache != null) + ? new Dictionary(cache) + : new Dictionary(); + updatedCache[objectType] = result; + + converterCache = updatedCache; } } - return null; + return result; } } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs index a90b97950..d4a529395 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/EnvelopeHeadersConverter.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using Newtonsoft.Json; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Json.Objects; @@ -14,6 +15,11 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class EnvelopeHeadersConverter : JsonValueConverter { + public override IEnumerable SupportedTypes + { + get { yield return typeof(EnvelopeHeaders); } + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var result = base.ReadJson(reader, objectType, existingValue, serializer); diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/ISupportedTypes.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/ISupportedTypes.cs new file mode 100644 index 000000000..fa28fb93c --- /dev/null +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/ISupportedTypes.cs @@ -0,0 +1,17 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; + +namespace Squidex.Infrastructure.Json.Newtonsoft +{ + public interface ISupportedTypes + { + IEnumerable SupportedTypes { get; } + } +} diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs index 463d24411..9663accf9 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/InstantConverter.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using Newtonsoft.Json; using NodaTime; using NodaTime.Text; @@ -14,6 +15,15 @@ namespace Squidex.Infrastructure.Json.Newtonsoft { public sealed class InstantConverter : JsonConverter { + public IEnumerable SupportedTypes + { + get + { + yield return typeof(Instant); + yield return typeof(Instant?); + } + } + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value != null) diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs index 078bd451e..03acfc2f5 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonClassConverter.cs @@ -6,12 +6,18 @@ // ========================================================================== using System; +using System.Collections.Generic; using Newtonsoft.Json; namespace Squidex.Infrastructure.Json.Newtonsoft { - public abstract class JsonClassConverter : JsonConverter where T : class + public abstract class JsonClassConverter : JsonConverter, ISupportedTypes where T : class { + public IEnumerable SupportedTypes + { + get { yield return typeof(T); } + } + public sealed override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) @@ -22,6 +28,8 @@ namespace Squidex.Infrastructure.Json.Newtonsoft return ReadValue(reader, objectType, serializer); } + protected abstract T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer); + public sealed override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) @@ -33,13 +41,11 @@ namespace Squidex.Infrastructure.Json.Newtonsoft WriteValue(writer, (T)value, serializer); } + protected abstract void WriteValue(JsonWriter writer, T value, JsonSerializer serializer); + public override bool CanConvert(Type objectType) { return objectType == typeof(T); } - - protected abstract void WriteValue(JsonWriter writer, T value, JsonSerializer serializer); - - protected abstract T ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer); } } diff --git a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs index 917099317..0e4282826 100644 --- a/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs +++ b/src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs @@ -6,6 +6,7 @@ // ========================================================================== using System; +using System.Collections.Generic; using System.Globalization; using Newtonsoft.Json; using Squidex.Infrastructure.Json.Objects; @@ -14,8 +15,24 @@ using Squidex.Infrastructure.Json.Objects; namespace Squidex.Infrastructure.Json.Newtonsoft { - public class JsonValueConverter : JsonConverter + public class JsonValueConverter : JsonConverter, ISupportedTypes { + private readonly HashSet supportedTypes = new HashSet + { + typeof(IJsonValue), + typeof(JsonArray), + typeof(JsonBoolean), + typeof(JsonNull), + typeof(JsonNumber), + typeof(JsonObject), + typeof(JsonString) + }; + + public virtual IEnumerable SupportedTypes + { + get { return supportedTypes; } + } + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return ReadJson(reader); @@ -161,7 +178,7 @@ namespace Squidex.Infrastructure.Json.Newtonsoft public override bool CanConvert(Type objectType) { - return typeof(IJsonValue).IsAssignableFrom(objectType); + return supportedTypes.Contains(objectType); } } } diff --git a/src/Squidex.Infrastructure/Log/JsonLogWriter.cs b/src/Squidex.Infrastructure/Log/JsonLogWriter.cs index 4d8012f8f..b90cf64eb 100644 --- a/src/Squidex.Infrastructure/Log/JsonLogWriter.cs +++ b/src/Squidex.Infrastructure/Log/JsonLogWriter.cs @@ -96,7 +96,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, string value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value); return this; @@ -104,7 +104,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, double value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value); return this; @@ -112,7 +112,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, long value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value); return this; @@ -120,7 +120,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, bool value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value); return this; @@ -128,7 +128,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, DateTime value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); return this; @@ -136,7 +136,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, DateTimeOffset value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value.ToString("o", CultureInfo.InvariantCulture)); return this; @@ -144,7 +144,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteProperty(string property, TimeSpan value) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteValue(value); return this; @@ -152,7 +152,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteObject(string property, Action objectWriter) { - jsonWriter.WritePropertyName(property); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteStartObject(); objectWriter?.Invoke(this); @@ -164,7 +164,7 @@ namespace Squidex.Infrastructure.Log IObjectWriter IObjectWriter.WriteArray(string property, Action arrayWriter) { - jsonWriter.WritePropertyName(property.ToCamelCase()); + jsonWriter.WritePropertyName(Format(property)); jsonWriter.WriteStartArray(); arrayWriter?.Invoke(this); @@ -185,6 +185,16 @@ namespace Squidex.Infrastructure.Log return this; } + private static string Format(string property) + { + if (ReferenceEquals(string.IsInterned(property), property)) + { + return property; + } + + return property.ToCamelCase(); + } + public override string ToString() { jsonWriter.WriteEndObject(); diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 500a1aad3..87916c8ac 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -9,8 +9,6 @@ using Microsoft.Extensions.DependencyInjection; using Migrate_01; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using NodaTime; -using NodaTime.Serialization.JsonNet; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Apps.Json; using Squidex.Domain.Apps.Core.Rules.Json; @@ -68,8 +66,6 @@ namespace Squidex.Config.Domain settings.DateParseHandling = DateParseHandling.None; settings.TypeNameHandling = typeNameHandling; - - settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); } static SerializationServices() diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 93f9dad74..478449461 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -80,7 +80,6 @@ - From 9bf46ee086a8eaf2ded21a373334ccb1fd1b4bd2 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 1 Dec 2018 13:20:13 +0100 Subject: [PATCH 13/15] Bugfix --- src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs | 4 ++-- .../EventSourcing/DefaultEventDataFormatter.cs | 4 ++-- .../EventSourcing/IEventDataFormatter.cs | 2 +- .../EventSourcing/Grains/EventConsumerGrainTests.cs | 4 ++-- .../States/PersistenceEventSourcingTests.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs index 660ca8627..3aa5bc9c5 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs @@ -127,9 +127,9 @@ namespace Squidex.Domain.Apps.Entities.Backup MapHeaders(data); var eventStream = streamNameResolver.WithNewId(streamName, guidMapper.NewGuidOrNull); - var eventEnvelope = formatter.Parse(data, true, guidMapper.NewGuidOrValue); + var eventEnvelope = formatter.Parse(data, guidMapper.NewGuidOrValue); - await handler((streamName, eventEnvelope)); + await handler((eventStream, eventEnvelope)); } readEvents++; diff --git a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs index d8e27f511..8135566cf 100644 --- a/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs @@ -25,12 +25,12 @@ namespace Squidex.Infrastructure.EventSourcing this.serializer = serializer; } - public Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null) + public Envelope Parse(EventData eventData, Func stringConverter = null) { var payloadType = typeNameRegistry.GetType(eventData.Type); var payloadObj = serializer.Deserialize(eventData.Payload, payloadType, stringConverter); - if (migrate && payloadObj is IMigratedEvent migratedEvent) + if (payloadObj is IMigratedEvent migratedEvent) { payloadObj = migratedEvent.Migrate(); } diff --git a/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs b/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs index 071808fb6..db3c3fdff 100644 --- a/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs +++ b/src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs @@ -11,7 +11,7 @@ namespace Squidex.Infrastructure.EventSourcing { public interface IEventDataFormatter { - Envelope Parse(EventData eventData, bool migrate = true, Func stringConverter = null); + Envelope Parse(EventData eventData, Func stringConverter = null); EventData ToEventData(Envelope envelope, Guid commitId, bool migrate = true); } diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index a65750d97..03e9fb905 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -82,7 +82,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains A.CallTo(() => persistence.WriteSnapshotAsync(A.Ignored)) .Invokes(new Action(s => state = s)); - A.CallTo(() => formatter.Parse(eventData, true, null)) + A.CallTo(() => formatter.Parse(eventData, null)) .Returns(envelope); sut = new MyEventConsumerGrain( @@ -196,7 +196,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains [Fact] public async Task Should_ignore_old_events() { - A.CallTo(() => formatter.Parse(eventData, true, null)) + A.CallTo(() => formatter.Parse(eventData, null)) .Throws(new TypeNameNotFoundException()); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); diff --git a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs index a061b8a61..72e37048b 100644 --- a/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs +++ b/tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs @@ -61,7 +61,7 @@ namespace Squidex.Infrastructure.States A.CallTo(() => eventStore.QueryAsync(key, 0)) .Returns(new List { storedEvent }); - A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, true, null)) + A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, null)) .Throws(new TypeNameNotFoundException()); var persistedEvents = new List(); @@ -256,7 +256,7 @@ namespace Squidex.Infrastructure.States eventsStored.Add(eventStored); - A.CallTo(() => eventDataFormatter.Parse(eventData, true, null)) + A.CallTo(() => eventDataFormatter.Parse(eventData, null)) .Returns(new Envelope(@event)); i++; From 31721abfb467aca1a6d0986ce415b55a16e47690 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 4 Dec 2018 16:27:53 +0100 Subject: [PATCH 14/15] Restore fix. --- src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index 4a2952e15..38bc2394f 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -194,6 +194,8 @@ namespace Squidex.Domain.Apps.Entities.Backup } } + await AssignContributorAsync(); + CurrentJob.Status = JobStatus.Completed; Log("Completed, Yeah!"); @@ -252,6 +254,8 @@ namespace Squidex.Domain.Apps.Entities.Backup FromRestore = true, Role = Role.Owner }); + + Log("Assigned current user."); } private async Task CleanupAsync() @@ -283,7 +287,7 @@ namespace Squidex.Domain.Apps.Entities.Backup await HandleEventAsync(reader, storedEvent.Stream, storedEvent.Event); }); - Log("Reading events completed."); + Log($"Reading {reader.ReadEvents} events and {reader.ReadAttachments} attachments completed.", true); } private async Task HandleEventAsync(BackupReader reader, string stream, Envelope @event) From 6e048bc802675153526e61fbe63825437f33c4f6 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Wed, 5 Dec 2018 19:52:24 +0100 Subject: [PATCH 15/15] Build fix. --- .../EventSourcing/Grains/EventConsumerGrainTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs index 03e9fb905..6c3ae202b 100644 --- a/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs +++ b/tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs @@ -330,7 +330,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains { var ex = new InvalidOperationException(); - A.CallTo(() => formatter.Parse(eventData, true, null)) + A.CallTo(() => formatter.Parse(eventData, null)) .Throws(ex); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);