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();