From 08233b2a78f2034fda630dc0f24f3a74374a3229 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 19 Mar 2017 21:46:55 +0100 Subject: [PATCH 1/2] Improved event handling. --- .../CQRS/DomainObjectBase.cs | 2 +- .../CQRS/Events/CompoundEventConsumer.cs | 53 ++++++++++++++ .../CQRS/Events/Envelope.cs | 43 +---------- .../CQRS/Events/Envelope_1.cs | 65 +++++++++++++++++ .../CQRS/Events/EventReceiver.cs | 2 +- .../CQRS/Events/IEventConsumer.cs | 2 + .../Timers/CompletionTimer.cs | 3 +- .../Apps/MongoAppRepository_EventHandling.cs | 46 +++++------- .../MongoContentRepository_EventHandling.cs | 9 ++- .../History/MongoHistoryEventRepository.cs | 5 ++ .../MongoSchemaRepository_EventHandling.cs | 25 +++---- .../Apps/Repositories/IAppRepository.cs | 3 - .../Apps/Services/IAppProvider.cs | 3 - .../Implementations/CachingAppProvider.cs | 48 ++++++++++--- .../Schemas/Repositories/ISchemaRepository.cs | 3 - .../Schemas/Services/ISchemaProvider.cs | 3 - .../Implementations/CachingSchemaProvider.cs | 47 +++++++++--- src/Squidex.Write/Apps/AppDomainObject.cs | 2 +- src/Squidex/Config/Domain/ReadModule.cs | 2 + .../Config/Domain/StoreMongoDbModule.cs | 43 +++++++---- src/Squidex/Config/Domain/Usages.cs | 18 ----- .../CQRS/Events/CompoundEventConsumerTests.cs | 71 +++++++++++++++++++ .../CQRS/Events/EventReceiverTests.cs | 1 + .../Apps/CachingAppProviderTests.cs | 6 +- .../Schemas/CachingSchemaProviderTests.cs | 6 +- 25 files changed, 361 insertions(+), 150 deletions(-) create mode 100644 src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs create mode 100644 src/Squidex.Infrastructure/CQRS/Events/Envelope_1.cs create mode 100644 tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs diff --git a/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs b/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs index b8e1ceb30..e47a98c25 100644 --- a/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs +++ b/src/Squidex.Infrastructure/CQRS/DomainObjectBase.cs @@ -47,7 +47,7 @@ namespace Squidex.Infrastructure.CQRS protected void RaiseEvent(IEvent @event) { - RaiseEvent(Envelope.Create(@event)); + RaiseEvent(Envelope.Create(@event)); } protected void RaiseEvent(Envelope @event) where TEvent : class, IEvent diff --git a/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs new file mode 100644 index 000000000..83c1843ba --- /dev/null +++ b/src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs @@ -0,0 +1,53 @@ +// ========================================================================== +// CompoundEventConsumer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Linq; +using System.Threading.Tasks; + +namespace Squidex.Infrastructure.CQRS.Events +{ + public sealed class CompoundEventConsumer : IEventConsumer + { + private readonly IEventConsumer[] inners; + + public string Name { get; } + + public CompoundEventConsumer(IEventConsumer first, params IEventConsumer[] inners) + { + Guard.NotNull(first, nameof(first)); + Guard.NotNull(inners, nameof(inners)); + + this.inners = new[] { first }.Union(inners).ToArray(); + + Name = first.GetType().Name; + } + + public CompoundEventConsumer(string name, params IEventConsumer[] inners) + { + Guard.NotNull(inners, nameof(inners)); + Guard.NotNullOrEmpty(name, nameof(name)); + + this.inners = inners; + + Name = name; + } + + public Task ClearAsync() + { + return Task.WhenAll(inners.Select(i => i.ClearAsync())); + } + + public async Task On(Envelope @event) + { + foreach (var inner in inners) + { + await inner.On(@event); + } + } + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Events/Envelope.cs b/src/Squidex.Infrastructure/CQRS/Events/Envelope.cs index e2bf2aa45..8e00e88c0 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/Envelope.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/Envelope.cs @@ -11,55 +11,18 @@ using NodaTime; namespace Squidex.Infrastructure.CQRS.Events { - public class Envelope where TPayload : class + public static class Envelope { - private readonly EnvelopeHeaders headers; - private readonly TPayload payload; - - public EnvelopeHeaders Headers - { - get { return headers; } - } - - public TPayload Payload - { - get { return payload; } - } - - public Envelope(TPayload payload) - : this(payload, new EnvelopeHeaders()) - { - } - - public Envelope(TPayload payload, PropertiesBag bag) - : this(payload, new EnvelopeHeaders(bag)) - { - } - - public Envelope(TPayload payload, EnvelopeHeaders headers) - { - Guard.NotNull(payload, nameof(payload)); - Guard.NotNull(headers, nameof(headers)); - - this.payload = payload; - this.headers = headers; - } - - public static Envelope Create(TPayload payload) + public static Envelope Create(TPayload payload) where TPayload : IEvent { var eventId = Guid.NewGuid(); var envelope = - new Envelope(payload) + new Envelope(payload) .SetEventId(eventId) .SetTimestamp(SystemClock.Instance.GetCurrentInstant()); return envelope; } - - public Envelope To() where TOther : class - { - return new Envelope(payload as TOther, headers.Clone()); - } } } diff --git a/src/Squidex.Infrastructure/CQRS/Events/Envelope_1.cs b/src/Squidex.Infrastructure/CQRS/Events/Envelope_1.cs new file mode 100644 index 000000000..e2bf2aa45 --- /dev/null +++ b/src/Squidex.Infrastructure/CQRS/Events/Envelope_1.cs @@ -0,0 +1,65 @@ +// ========================================================================== +// Envelope.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using NodaTime; + +namespace Squidex.Infrastructure.CQRS.Events +{ + public class Envelope where TPayload : class + { + private readonly EnvelopeHeaders headers; + private readonly TPayload payload; + + public EnvelopeHeaders Headers + { + get { return headers; } + } + + public TPayload Payload + { + get { return payload; } + } + + public Envelope(TPayload payload) + : this(payload, new EnvelopeHeaders()) + { + } + + public Envelope(TPayload payload, PropertiesBag bag) + : this(payload, new EnvelopeHeaders(bag)) + { + } + + public Envelope(TPayload payload, EnvelopeHeaders headers) + { + Guard.NotNull(payload, nameof(payload)); + Guard.NotNull(headers, nameof(headers)); + + this.payload = payload; + this.headers = headers; + } + + public static Envelope Create(TPayload payload) + { + var eventId = Guid.NewGuid(); + + var envelope = + new Envelope(payload) + .SetEventId(eventId) + .SetTimestamp(SystemClock.Instance.GetCurrentInstant()); + + return envelope; + } + + public Envelope To() where TOther : class + { + return new Envelope(payload as TOther, headers.Clone()); + } + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs index aca57463c..c0660a18e 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs @@ -76,7 +76,7 @@ namespace Squidex.Infrastructure.CQRS.Events return; } - var consumerName = eventConsumer.GetType().Name; + var consumerName = eventConsumer.Name; var consumerStarted = false; timer = new CompletionTimer(delay, async ct => diff --git a/src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs b/src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs index b6eb06cb9..da0eedbb8 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs @@ -12,6 +12,8 @@ namespace Squidex.Infrastructure.CQRS.Events { public interface IEventConsumer { + string Name { get; } + Task ClearAsync(); Task On(Envelope @event); diff --git a/src/Squidex.Infrastructure/Timers/CompletionTimer.cs b/src/Squidex.Infrastructure/Timers/CompletionTimer.cs index b4dc061bf..f334a6197 100644 --- a/src/Squidex.Infrastructure/Timers/CompletionTimer.cs +++ b/src/Squidex.Infrastructure/Timers/CompletionTimer.cs @@ -7,6 +7,7 @@ // ========================================================================== using System; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -45,7 +46,7 @@ namespace Squidex.Infrastructure.Timers } catch (TaskCanceledException) { - Console.WriteLine("Task in TriggerTimer has been cancelled."); + Debug.WriteLine("Task in TriggerTimer has been cancelled."); } } } diff --git a/src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs b/src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs index 606a28e8c..ff92bae3b 100644 --- a/src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs +++ b/src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs @@ -6,9 +6,7 @@ // All rights reserved. // ========================================================================== -using System; using System.Threading.Tasks; -using Squidex.Events; using Squidex.Events.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; @@ -20,36 +18,27 @@ namespace Squidex.Read.MongoDb.Apps { public partial class MongoAppRepository { - public event Action> AppSaved; + public string Name + { + get { return GetType().Name; } + } public Task On(Envelope @event) { return this.DispatchActionAsync(@event.Payload, @event.Headers); } - protected async Task On(AppCreated @event, EnvelopeHeaders headers) + protected Task On(AppCreated @event, EnvelopeHeaders headers) { - await Collection.CreateAsync(@event, headers, a => + return Collection.CreateAsync(@event, headers, a => { SimpleMapper.Map(@event, a); }); - - AppSaved?.Invoke(@event.AppId); - } - - protected Task On(AppContributorAssigned @event, EnvelopeHeaders headers) - { - return UpdateAsync(@event, headers, a => - { - var contributor = a.Contributors.GetOrAddNew(@event.ContributorId); - - SimpleMapper.Map(@event, contributor); - }); } protected Task On(AppContributorRemoved @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Contributors.Remove(@event.ContributorId); }); @@ -57,7 +46,7 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppClientAttached @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Clients[@event.Id] = SimpleMapper.Map(@event, new MongoAppClientEntity()); }); @@ -65,7 +54,7 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppClientRevoked @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Clients.Remove(@event.Id); }); @@ -73,7 +62,7 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppClientRenamed @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Clients[@event.Id].Name = @event.Name; }); @@ -81,7 +70,7 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppLanguageAdded @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Languages.Add(@event.Language.Iso2Code); }); @@ -89,7 +78,7 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppLanguageRemoved @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.Languages.Remove(@event.Language.Iso2Code); }); @@ -97,17 +86,20 @@ namespace Squidex.Read.MongoDb.Apps protected Task On(AppMasterLanguageSet @event, EnvelopeHeaders headers) { - return UpdateAsync(@event, headers, a => + return Collection.UpdateAsync(@event, headers, a => { a.MasterLanguage = @event.Language.Iso2Code; }); } - public async Task UpdateAsync(AppEvent @event, EnvelopeHeaders headers, Action updater) + protected Task On(AppContributorAssigned @event, EnvelopeHeaders headers) { - await Collection.UpdateAsync(@event, headers, updater); + return Collection.UpdateAsync(@event, headers, a => + { + var contributor = a.Contributors.GetOrAddNew(@event.ContributorId); - AppSaved?.Invoke(@event.AppId); + SimpleMapper.Map(@event, contributor); + }); } } } diff --git a/src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs index d2abc0e59..4d13b2e00 100644 --- a/src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs +++ b/src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs @@ -32,6 +32,11 @@ namespace Squidex.Read.MongoDb.Contents } } + public string Name + { + get { return GetType().Name; } + } + public async Task ClearAsync() { using (var collections = await database.ListCollectionsAsync()) @@ -127,11 +132,11 @@ namespace Squidex.Read.MongoDb.Contents }); } - private async Task ForSchemaIdAsync(Guid schemaId, Func, Task> action) + private Task ForSchemaIdAsync(Guid schemaId, Func, Task> action) { var collection = GetCollection(schemaId); - await action(collection); + return action(collection); } private IMongoCollection GetCollection(Guid schemaId) diff --git a/src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs b/src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs index 874764150..da4376ba3 100644 --- a/src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs +++ b/src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs @@ -67,6 +67,11 @@ namespace Squidex.Read.MongoDb.History return entities.Select(x => (IHistoryEventEntity)new ParsedHistoryEvent(x, texts)).ToList(); } + public string Name + { + get { return GetType().Name; } + } + public async Task On(Envelope @event) { foreach (var creator in creators) diff --git a/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs index f0a94b350..93e2229b4 100644 --- a/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs +++ b/src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs @@ -8,12 +8,10 @@ using System; using System.Threading.Tasks; -using MongoDB.Driver; using Squidex.Core.Schemas; using Squidex.Events; using Squidex.Events.Schemas; using Squidex.Events.Schemas.Utils; -using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Reflection; @@ -23,20 +21,21 @@ namespace Squidex.Read.MongoDb.Schemas { public partial class MongoSchemaRepository { - public event Action, NamedId> SchemaSaved; + public string Name + { + get { return GetType().Name; } + } public Task On(Envelope @event) { return this.DispatchActionAsync(@event.Payload, @event.Headers); } - protected async Task On(SchemaCreated @event, EnvelopeHeaders headers) + protected Task On(SchemaCreated @event, EnvelopeHeaders headers) { var schema = SchemaEventDispatcher.Dispatch(@event); - await Collection.CreateAsync(@event, headers, s => { UpdateSchema(s, schema); SimpleMapper.Map(@event, s); }); - - SchemaSaved?.Invoke(@event.AppId, @event.SchemaId); + return Collection.CreateAsync(@event, headers, s => { UpdateSchema(s, schema); SimpleMapper.Map(@event, s); }); } protected Task On(FieldDeleted @event, EnvelopeHeaders headers) @@ -89,18 +88,14 @@ namespace Squidex.Read.MongoDb.Schemas return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s, registry)); } - protected async Task On(SchemaDeleted @event, EnvelopeHeaders headers) + protected Task On(SchemaDeleted @event, EnvelopeHeaders headers) { - await Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); - - SchemaSaved?.Invoke(@event.AppId, @event.SchemaId); + return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); } - private async Task UpdateSchema(SchemaEvent @event, EnvelopeHeaders headers, Func updater) + private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func updater) { - await Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater)); - - SchemaSaved?.Invoke(@event.AppId, @event.SchemaId); + return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater)); } private void UpdateSchema(MongoSchemaEntity entity, Func updater) diff --git a/src/Squidex.Read/Apps/Repositories/IAppRepository.cs b/src/Squidex.Read/Apps/Repositories/IAppRepository.cs index 4ab28435f..2b473cd8b 100644 --- a/src/Squidex.Read/Apps/Repositories/IAppRepository.cs +++ b/src/Squidex.Read/Apps/Repositories/IAppRepository.cs @@ -9,14 +9,11 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Squidex.Infrastructure; namespace Squidex.Read.Apps.Repositories { public interface IAppRepository { - event Action> AppSaved; - Task> QueryAllAsync(string subjectId); Task FindAppAsync(Guid appId); diff --git a/src/Squidex.Read/Apps/Services/IAppProvider.cs b/src/Squidex.Read/Apps/Services/IAppProvider.cs index b861ae4e6..0a1e78e25 100644 --- a/src/Squidex.Read/Apps/Services/IAppProvider.cs +++ b/src/Squidex.Read/Apps/Services/IAppProvider.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; -using Squidex.Infrastructure; namespace Squidex.Read.Apps.Services { @@ -17,7 +16,5 @@ namespace Squidex.Read.Apps.Services Task FindAppByIdAsync(Guid id); Task FindAppByNameAsync(string name); - - void Remove(NamedId id); } } diff --git a/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs b/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs index 3537e873b..18223174e 100644 --- a/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs +++ b/src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs @@ -9,8 +9,12 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Squidex.Events; +using Squidex.Events.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Tasks; using Squidex.Read.Apps.Repositories; using Squidex.Read.Utils; @@ -18,11 +22,16 @@ using Squidex.Read.Utils; namespace Squidex.Read.Apps.Services.Implementations { - public class CachingAppProvider : CachingProviderBase, IAppProvider + public class CachingAppProvider : CachingProviderBase, IAppProvider, IEventConsumer { private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30); private readonly IAppRepository repository; + public string Name + { + get { return GetType().Name; } + } + public CachingAppProvider(IMemoryCache cache, IAppRepository repository) : base(cache) { @@ -71,16 +80,34 @@ namespace Squidex.Read.Apps.Services.Implementations return result; } - public void Remove(NamedId id) + public Task On(Envelope @event) { - var cacheKeyById = BuildIdCacheKey(id.Id); - var cacheKeyByName = BuildNameCacheKey(id.Name); + void Remove(NamedId id) + { + var cacheKeyById = BuildIdCacheKey(id.Id); + var cacheKeyByName = BuildNameCacheKey(id.Name); + + Cache.Remove(cacheKeyById); + Cache.Remove(cacheKeyByName); + + Cache.Invalidate(cacheKeyById); + Cache.Invalidate(cacheKeyByName); + } - Cache.Remove(cacheKeyById); - Cache.Remove(cacheKeyByName); + if (@event.Payload is AppClientAttached || + @event.Payload is AppClientRenamed || + @event.Payload is AppClientRevoked || + @event.Payload is AppContributorAssigned || + @event.Payload is AppContributorRemoved || + @event.Payload is AppCreated || + @event.Payload is AppLanguageAdded || + @event.Payload is AppLanguageRemoved || + @event.Payload is AppMasterLanguageSet) + { + Remove(((AppEvent)@event.Payload).AppId); + } - Cache.Invalidate(cacheKeyById); - Cache.Invalidate(cacheKeyByName); + return TaskHelper.Done; } private static string BuildNameCacheKey(string name) @@ -92,5 +119,10 @@ namespace Squidex.Read.Apps.Services.Implementations { return $"App_Names_{schemaId}"; } + + public Task ClearAsync() + { + return TaskHelper.Done; + } } } diff --git a/src/Squidex.Read/Schemas/Repositories/ISchemaRepository.cs b/src/Squidex.Read/Schemas/Repositories/ISchemaRepository.cs index d540c343c..8766bc530 100644 --- a/src/Squidex.Read/Schemas/Repositories/ISchemaRepository.cs +++ b/src/Squidex.Read/Schemas/Repositories/ISchemaRepository.cs @@ -9,14 +9,11 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Squidex.Infrastructure; namespace Squidex.Read.Schemas.Repositories { public interface ISchemaRepository { - event Action, NamedId> SchemaSaved; - Task> QueryAllAsync(Guid appId); Task> QueryAllWithSchemaAsync(Guid appId); diff --git a/src/Squidex.Read/Schemas/Services/ISchemaProvider.cs b/src/Squidex.Read/Schemas/Services/ISchemaProvider.cs index 73b8a1df8..ef3234afe 100644 --- a/src/Squidex.Read/Schemas/Services/ISchemaProvider.cs +++ b/src/Squidex.Read/Schemas/Services/ISchemaProvider.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; -using Squidex.Infrastructure; namespace Squidex.Read.Schemas.Services { @@ -17,7 +16,5 @@ namespace Squidex.Read.Schemas.Services Task FindSchemaByIdAsync(Guid id, bool provideDeleted = false); Task FindSchemaByNameAsync(Guid appId, string name); - - void Remove(NamedId appId, NamedId schemaId); } } diff --git a/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs b/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs index 1d600f32f..2b9c1e268 100644 --- a/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs +++ b/src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs @@ -9,8 +9,11 @@ using System; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Squidex.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Caching; +using Squidex.Infrastructure.CQRS.Events; +using Squidex.Infrastructure.Tasks; using Squidex.Read.Schemas.Repositories; using Squidex.Read.Utils; @@ -19,11 +22,16 @@ using Squidex.Read.Utils; namespace Squidex.Read.Schemas.Services.Implementations { - public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider + public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider, IEventConsumer { private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10); private readonly ISchemaRepository repository; + public string Name + { + get { return GetType().Name; } + } + public CachingSchemaProvider(IMemoryCache cache, ISchemaRepository repository) : base(cache) { @@ -82,16 +90,34 @@ namespace Squidex.Read.Schemas.Services.Implementations return result; } - public void Remove(NamedId appId, NamedId schemaId) + public Task On(Envelope @event) { - var cacheKeyById = BuildIdCacheKey(schemaId.Id); - var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name); + void Remove(NamedId appId, NamedId schemaId) + { + var cacheKeyById = BuildIdCacheKey(schemaId.Id); + var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name); - Cache.Remove(cacheKeyById); - Cache.Remove(cacheKeyByName); + Cache.Remove(cacheKeyById); + Cache.Remove(cacheKeyByName); + + Cache.Invalidate(cacheKeyById); + Cache.Invalidate(cacheKeyByName); + } + + if (@event.Payload is FieldEvent fieldEvent) + { + Remove(fieldEvent.AppId, fieldEvent.SchemaId); + } + else if (@event.Payload is SchemaDeleted schemaDeletedEvent) + { + Remove(schemaDeletedEvent.AppId, schemaDeletedEvent.SchemaId); + } + else if (@event.Payload is SchemaUpdated schemaUpdatedEvent) + { + Remove(schemaUpdatedEvent.AppId, schemaUpdatedEvent.SchemaId); + } - Cache.Invalidate(cacheKeyById); - Cache.Invalidate(cacheKeyByName); + return TaskHelper.Done; } private static string BuildNameCacheKey(Guid appId, string name) @@ -103,5 +129,10 @@ namespace Squidex.Read.Schemas.Services.Implementations { return $"Schema_Names_{schemaId}"; } + + public Task ClearAsync() + { + return TaskHelper.Done; + } } } diff --git a/src/Squidex.Write/Apps/AppDomainObject.cs b/src/Squidex.Write/Apps/AppDomainObject.cs index b1d8a1aae..52dc6ea60 100644 --- a/src/Squidex.Write/Apps/AppDomainObject.cs +++ b/src/Squidex.Write/Apps/AppDomainObject.cs @@ -207,7 +207,7 @@ namespace Squidex.Write.Apps @event.AppId = new NamedId(Id, name); } - base.RaiseEvent(Envelope.Create(@event)); + RaiseEvent(Envelope.Create(@event)); } private static AppLanguageAdded CreateInitialLanguage(NamedId id) diff --git a/src/Squidex/Config/Domain/ReadModule.cs b/src/Squidex/Config/Domain/ReadModule.cs index eec94620a..6e2b0d96b 100644 --- a/src/Squidex/Config/Domain/ReadModule.cs +++ b/src/Squidex/Config/Domain/ReadModule.cs @@ -35,10 +35,12 @@ namespace Squidex.Config.Domain { builder.RegisterType() .As() + .AsSelf() .SingleInstance(); builder.RegisterType() .As() + .AsSelf() .SingleInstance(); builder.RegisterType() diff --git a/src/Squidex/Config/Domain/StoreMongoDbModule.cs b/src/Squidex/Config/Domain/StoreMongoDbModule.cs index 5999a9215..151ac5d9e 100644 --- a/src/Squidex/Config/Domain/StoreMongoDbModule.cs +++ b/src/Squidex/Config/Domain/StoreMongoDbModule.cs @@ -17,6 +17,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.MongoDb; using Squidex.Read.Apps.Repositories; +using Squidex.Read.Apps.Services.Implementations; using Squidex.Read.Contents.Repositories; using Squidex.Read.History.Repositories; using Squidex.Read.MongoDb.Apps; @@ -26,6 +27,7 @@ using Squidex.Read.MongoDb.Infrastructure; using Squidex.Read.MongoDb.Schemas; using Squidex.Read.MongoDb.Users; using Squidex.Read.Schemas.Repositories; +using Squidex.Read.Schemas.Services.Implementations; using Squidex.Read.Users.Repositories; namespace Squidex.Config.Domain @@ -78,23 +80,25 @@ namespace Squidex.Config.Domain .SingleInstance(); builder.Register>(c => - { - var usersCollection = c.ResolveNamed(MongoDatabaseName).GetCollection("Identity_Users"); + { + var usersCollection = c.ResolveNamed(MongoDatabaseName).GetCollection("Identity_Users"); - IndexChecks.EnsureUniqueIndexOnNormalizedEmail(usersCollection); - IndexChecks.EnsureUniqueIndexOnNormalizedUserName(usersCollection); + IndexChecks.EnsureUniqueIndexOnNormalizedEmail(usersCollection); + IndexChecks.EnsureUniqueIndexOnNormalizedUserName(usersCollection); - return new UserStore(usersCollection); - }).SingleInstance(); + return new UserStore(usersCollection); + }) + .SingleInstance(); builder.Register>(c => - { - var rolesCollection = c.ResolveNamed(MongoDatabaseName).GetCollection("Identity_Roles"); + { + var rolesCollection = c.ResolveNamed(MongoDatabaseName).GetCollection("Identity_Roles"); - IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(rolesCollection); + IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(rolesCollection); - return new RoleStore(rolesCollection); - }).SingleInstance(); + return new RoleStore(rolesCollection); + }) + .SingleInstance(); builder.RegisterType() .As() @@ -129,7 +133,6 @@ namespace Squidex.Config.Domain builder.RegisterType() .WithParameter(ResolvedParameter.ForNamed(MongoDatabaseName)) .As() - .As() .As() .AsSelf() .SingleInstance(); @@ -141,6 +144,22 @@ namespace Squidex.Config.Domain .As() .AsSelf() .SingleInstance(); + + builder.Register(c => + new CompoundEventConsumer( + c.Resolve(), + c.Resolve())) + .As() + .AsSelf() + .SingleInstance(); + + builder.Register(c => + new CompoundEventConsumer( + c.Resolve(), + c.Resolve())) + .As() + .AsSelf() + .SingleInstance(); } } } diff --git a/src/Squidex/Config/Domain/Usages.cs b/src/Squidex/Config/Domain/Usages.cs index b3d4b9da1..150a03410 100644 --- a/src/Squidex/Config/Domain/Usages.cs +++ b/src/Squidex/Config/Domain/Usages.cs @@ -11,10 +11,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; -using Squidex.Read.Apps.Repositories; -using Squidex.Read.Apps.Services; -using Squidex.Read.Schemas.Repositories; -using Squidex.Read.Schemas.Services; namespace Squidex.Config.Domain { @@ -31,20 +27,6 @@ namespace Squidex.Config.Domain receiver?.Subscribe(catchConsumer); } - var appProvider = app.ApplicationServices.GetRequiredService(); - - app.ApplicationServices.GetRequiredService().AppSaved += appId => - { - appProvider.Remove(appId); - }; - - var schemaProvider = app.ApplicationServices.GetRequiredService(); - - app.ApplicationServices.GetRequiredService().SchemaSaved += (appId, schemaId) => - { - schemaProvider.Remove(appId, schemaId); - }; - return app; } diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs new file mode 100644 index 000000000..e01b23c43 --- /dev/null +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs @@ -0,0 +1,71 @@ +// ========================================================================== +// CompoundEventConsumerTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Threading.Tasks; +using Moq; +using Squidex.Infrastructure.Tasks; +using Xunit; + +namespace Squidex.Infrastructure.CQRS.Events +{ + public class CompoundEventConsumerTests + { + private readonly Mock consumer1 = new Mock(); + private readonly Mock consumer2 = new Mock(); + + private sealed class MyEvent : IEvent + { + } + + [Fact] + public void Should_return_given_name() + { + var sut = new CompoundEventConsumer("consumer-name"); + + Assert.Equal("consumer-name", sut.Name); + } + + [Fact] + public void Should_return_first_inner_name() + { + var sut = new CompoundEventConsumer(consumer1.Object, consumer2.Object); + + Assert.Equal(consumer1.Object.GetType().Name, sut.Name); + } + + [Fact] + public async Task Should_clear_all_consumers() + { + consumer1.Setup(x => x.ClearAsync()).Returns(TaskHelper.Done).Verifiable(); + consumer2.Setup(x => x.ClearAsync()).Returns(TaskHelper.Done).Verifiable(); + + var sut = new CompoundEventConsumer("consumer-name", consumer1.Object, consumer2.Object); + + await sut.ClearAsync(); + + consumer1.VerifyAll(); + consumer2.VerifyAll(); + } + + [Fact] + public async Task Should_invoke_all_consumers() + { + var @event = Envelope.Create(new MyEvent()); + + consumer1.Setup(x => x.On(@event)).Returns(TaskHelper.Done).Verifiable(); + consumer2.Setup(x => x.On(@event)).Returns(TaskHelper.Done).Verifiable(); + + var sut = new CompoundEventConsumer("consumer-name", consumer1.Object, consumer2.Object); + + await sut.On(@event); + + consumer1.VerifyAll(); + consumer2.VerifyAll(); + } + } +} diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs index 08d8aa308..2a8b25b61 100644 --- a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs @@ -90,6 +90,7 @@ namespace Squidex.Infrastructure.CQRS.Events eventStore.Setup(x => x.GetEventsAsync(2)).Returns(events.ToObservable()); + eventConsumer.Setup(x => x.Name).Returns(consumerName); eventConsumerInfoRepository.Setup(x => x.FindAsync(consumerName)).Returns(Task.FromResult(consumerInfo)); formatter.Setup(x => x.Parse(eventData1)).Returns(envelope1); diff --git a/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs b/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs index a3f80b572..26d060a25 100644 --- a/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs +++ b/tests/Squidex.Read.Tests/Apps/CachingAppProviderTests.cs @@ -11,7 +11,9 @@ using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using Moq; +using Squidex.Events.Apps; using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; using Squidex.Read.Apps.Repositories; using Squidex.Read.Apps.Services.Implementations; using Xunit; @@ -80,7 +82,7 @@ namespace Squidex.Read.Apps await ProvideAppById(appV1); - sut.Remove(appId); + sut.On(Envelope.Create(new AppLanguageAdded {AppId = appId }).To()).Wait(); await ProvideAppById(appV2); @@ -96,7 +98,7 @@ namespace Squidex.Read.Apps await ProvideAppByName(appV1); - sut.Remove(appId); + sut.On(Envelope.Create(new AppLanguageAdded { AppId = appId }).To()).Wait(); await ProvideAppByName(appV2); diff --git a/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs b/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs index a86cb4db7..1041f0400 100644 --- a/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs +++ b/tests/Squidex.Read.Tests/Schemas/CachingSchemaProviderTests.cs @@ -11,7 +11,9 @@ using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using Moq; +using Squidex.Events.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.CQRS.Events; using Squidex.Read.Schemas.Repositories; using Squidex.Read.Schemas.Services.Implementations; using Xunit; @@ -83,7 +85,7 @@ namespace Squidex.Read.Schemas await ProvideSchemaById(schemaV1); - sut.Remove(appId, schemaId); + sut.On(Envelope.Create(new FieldAdded { AppId = appId, SchemaId = schemaId })).Wait(); await ProvideSchemaById(schemaV2); @@ -99,7 +101,7 @@ namespace Squidex.Read.Schemas await ProvideSchemaByName(schemaV1); - sut.Remove(appId, schemaId); + sut.On(Envelope.Create(new SchemaUpdated { AppId = appId, SchemaId = schemaId })).Wait(); await ProvideSchemaByName(schemaV2); From e66e7c4b10102344c27cbde9c6b8f4ea3bd5e129 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 20 Mar 2017 21:30:07 +0100 Subject: [PATCH 2/2] OnPush for some low level components. --- .../content/pages/content/content-field.component.ts | 5 +++-- .../content/pages/contents/content-item.component.ts | 3 ++- .../app/features/schemas/pages/schema/field.component.ts | 3 ++- .../schemas/pages/schema/types/boolean-ui.component.ts | 5 +++-- .../pages/schema/types/boolean-validation.component.ts | 5 +++-- .../schemas/pages/schema/types/date-time-ui.component.ts | 5 +++-- .../pages/schema/types/date-time-validation.component.ts | 5 +++-- .../schemas/pages/schema/types/geolocation-ui.component.ts | 5 +++-- .../pages/schema/types/geolocation-validation.component.ts | 5 +++-- .../features/schemas/pages/schema/types/json-ui.component.ts | 5 +++-- .../schemas/pages/schema/types/json-validation.component.ts | 5 +++-- .../schemas/pages/schema/types/number-ui.component.ts | 5 +++-- .../pages/schema/types/number-validation.component.ts | 5 +++-- .../schemas/pages/schema/types/string-ui.component.ts | 5 +++-- .../pages/schema/types/string-validation.component.ts | 5 +++-- 15 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/Squidex/app/features/content/pages/content/content-field.component.ts b/src/Squidex/app/features/content/pages/content/content-field.component.ts index a8bbd45da..2cd05a56a 100644 --- a/src/Squidex/app/features/content/pages/content/content-field.component.ts +++ b/src/Squidex/app/features/content/pages/content/content-field.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { AppLanguageDto, FieldDto } from 'shared'; @@ -13,7 +13,8 @@ import { AppLanguageDto, FieldDto } from 'shared'; @Component({ selector: 'sqx-content-field', styleUrls: ['./content-field.component.scss'], - templateUrl: './content-field.component.html' + templateUrl: './content-field.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class ContentFieldComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/content/pages/contents/content-item.component.ts b/src/Squidex/app/features/content/pages/contents/content-item.component.ts index ebfd0bf4a..6c1a2db46 100644 --- a/src/Squidex/app/features/content/pages/contents/content-item.component.ts +++ b/src/Squidex/app/features/content/pages/contents/content-item.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; import { AppComponentBase, @@ -24,6 +24,7 @@ import { selector: '[sqxContent]', styleUrls: ['./content-item.component.scss'], templateUrl: './content-item.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeAnimation ] diff --git a/src/Squidex/app/features/schemas/pages/schema/field.component.ts b/src/Squidex/app/features/schemas/pages/schema/field.component.ts index 864f4ea15..4bfcc9379 100644 --- a/src/Squidex/app/features/schemas/pages/schema/field.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/field.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { @@ -19,6 +19,7 @@ import { selector: 'sqx-field', styleUrls: ['./field.component.scss'], templateUrl: './field.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, animations: [ fadeAnimation ] diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts index 0e45f586a..0b9c1d18f 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { BooleanFieldPropertiesDto } from 'shared'; @@ -13,7 +13,8 @@ import { BooleanFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-boolean-ui', styleUrls: ['boolean-ui.component.scss'], - templateUrl: 'boolean-ui.component.html' + templateUrl: 'boolean-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class BooleanUIComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts index 58eff1ab4..f73d4dc58 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/boolean-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -14,7 +14,8 @@ import { BooleanFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-boolean-validation', styleUrls: ['boolean-validation.component.scss'], - templateUrl: 'boolean-validation.component.html' + templateUrl: 'boolean-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class BooleanValidationComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts index 26d7e34fe..01bd49676 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -14,7 +14,8 @@ import { FloatConverter, NumberFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-date-time-ui', styleUrls: ['date-time-ui.component.scss'], - templateUrl: 'date-time-ui.component.html' + templateUrl: 'date-time-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class DateTimeUIComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts index c3d65a206..3c0803105 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/date-time-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -14,7 +14,8 @@ import { NumberFieldPropertiesDto, ValidatorsEx } from 'shared'; @Component({ selector: 'sqx-date-time-validation', styleUrls: ['date-time-validation.component.scss'], - templateUrl: 'date-time-validation.component.html' + templateUrl: 'date-time-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class DateTimeValidationComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.ts index a0fc725a5..1fd5b9346 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { GeolocationFieldPropertiesDto } from 'shared'; @@ -13,7 +13,8 @@ import { GeolocationFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-geolocation-ui', styleUrls: ['geolocation-ui.component.scss'], - templateUrl: 'geolocation-ui.component.html' + templateUrl: 'geolocation-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class GeolocationUIComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-validation.component.ts index 098fcacdc..e3f3a089f 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/geolocation-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/geolocation-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { GeolocationFieldPropertiesDto } from 'shared'; @@ -13,7 +13,8 @@ import { GeolocationFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-geolocation-validation', styleUrls: ['geolocation-validation.component.scss'], - templateUrl: 'geolocation-validation.component.html' + templateUrl: 'geolocation-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class GeolocationValidationComponent { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/json-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/json-ui.component.ts index 9feebc666..615759c53 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/json-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/json-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { JsonFieldPropertiesDto } from 'shared'; @@ -13,7 +13,8 @@ import { JsonFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-json-ui', styleUrls: ['json-ui.component.scss'], - templateUrl: 'json-ui.component.html' + templateUrl: 'json-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class JsonUIComponent { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/json-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/json-validation.component.ts index dcdbae808..498c4eec5 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/json-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/json-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { JsonFieldPropertiesDto } from 'shared'; @@ -13,7 +13,8 @@ import { JsonFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-json-validation', styleUrls: ['json-validation.component.scss'], - templateUrl: 'json-validation.component.html' + templateUrl: 'json-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class JsonValidationComponent { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts index 7f8edc9c0..24c20b8d6 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; @@ -14,7 +14,8 @@ import { FloatConverter, NumberFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-number-ui', styleUrls: ['number-ui.component.scss'], - templateUrl: 'number-ui.component.html' + templateUrl: 'number-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class NumberUIComponent implements OnDestroy, OnInit { private editorSubscription: Subscription; diff --git a/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts index 51fc8b779..5cdb1f7bb 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/number-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; @@ -14,7 +14,8 @@ import { NumberFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-number-validation', styleUrls: ['number-validation.component.scss'], - templateUrl: 'number-validation.component.html' + templateUrl: 'number-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class NumberValidationComponent implements OnInit { @Input() diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts index bb8a8f923..e08efe887 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-ui.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; @@ -14,7 +14,8 @@ import { StringFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-string-ui', styleUrls: ['string-ui.component.scss'], - templateUrl: 'string-ui.component.html' + templateUrl: 'string-ui.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class StringUIComponent implements OnDestroy, OnInit { private editorSubscription: Subscription; diff --git a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts index 0387c7099..9ecb0ae7c 100644 --- a/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/types/string-validation.component.ts @@ -5,7 +5,7 @@ * Copyright (c) Sebastian Stehle. All rights reserved */ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Observable, Subscription } from 'rxjs'; @@ -14,7 +14,8 @@ import { StringFieldPropertiesDto } from 'shared'; @Component({ selector: 'sqx-string-validation', styleUrls: ['string-validation.component.scss'], - templateUrl: 'string-validation.component.html' + templateUrl: 'string-validation.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class StringValidationComponent implements OnDestroy, OnInit { private patternSubscription: Subscription;