From ff5dac51f409be3124c692f9ba0938efea960c68 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 4 Feb 2017 18:11:51 +0100 Subject: [PATCH] Not everything was pushed --- src/Squidex.Core/Schemas/FieldRegistry.cs | 11 +- .../EventStore/MongoEventStore.cs | 14 ++- .../MongoRepositoryBase.cs | 5 + ...tMqEventChannel.cs => RabbitMqEventBus.cs} | 54 +++++++--- .../CQRS/Events/EventReceiver.cs | 7 +- .../CQRS/Events/InMemoryEventBus.cs | 32 ++++++ .../CQRS/Replay/ReplayGenerator.cs | 9 +- .../ConfigurationException.cs | 29 +++++ .../ICliCommand.cs} | 10 +- src/Squidex.Infrastructure/IExternalSystem.cs | 15 +++ src/Squidex.Read.MongoDb/MongoDbModule.cs | 100 ------------------ .../MongoDbStoresExternalSystem.cs | 38 +++++++ src/Squidex.Read.MongoDb/project.json | 1 - .../Config/Identity/MyIdentityOptions.cs | 2 - .../Contents/ContentDataTests.cs | 2 +- 15 files changed, 200 insertions(+), 129 deletions(-) rename src/Squidex.Infrastructure.RabbitMq/{RabbitMqEventChannel.cs => RabbitMqEventBus.cs} (52%) create mode 100644 src/Squidex.Infrastructure/CQRS/Events/InMemoryEventBus.cs create mode 100644 src/Squidex.Infrastructure/ConfigurationException.cs rename src/{Squidex.Read.MongoDb/MyMongoDbOptions.cs => Squidex.Infrastructure/ICliCommand.cs} (63%) create mode 100644 src/Squidex.Infrastructure/IExternalSystem.cs delete mode 100644 src/Squidex.Read.MongoDb/MongoDbModule.cs create mode 100644 src/Squidex.Read.MongoDb/MongoDbStoresExternalSystem.cs diff --git a/src/Squidex.Core/Schemas/FieldRegistry.cs b/src/Squidex.Core/Schemas/FieldRegistry.cs index 7f964813a..caa7ad420 100644 --- a/src/Squidex.Core/Schemas/FieldRegistry.cs +++ b/src/Squidex.Core/Schemas/FieldRegistry.cs @@ -51,9 +51,14 @@ namespace Squidex.Core.Schemas public FieldRegistry() { - Add((id, name, properties) => new BooleanField(id, name, (BooleanFieldProperties)properties)); - Add((id, name, properties) => new NumberField(id, name, (NumberFieldProperties)properties)); - Add((id, name, properties) => new StringField(id, name, (StringFieldProperties)properties)); + Add( + (id, name, p) => new BooleanField(id, name, (BooleanFieldProperties)p)); + + Add( + (id, name, p) => new NumberField(id, name, (NumberFieldProperties)p)); + + Add( + (id, name, p) => new StringField(id, name, (StringFieldProperties)p)); } public void Add(FactoryFunction fieldFactory) diff --git a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs index 7aeb72a9a..691e810b6 100644 --- a/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs +++ b/src/Squidex.Infrastructure.MongoDb/EventStore/MongoEventStore.cs @@ -22,7 +22,7 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Infrastructure.MongoDb.EventStore { - public class MongoEventStore : MongoRepositoryBase, IEventStore + public class MongoEventStore : MongoRepositoryBase, IEventStore, IExternalSystem { private sealed class EventCountEntity { @@ -51,6 +51,18 @@ namespace Squidex.Infrastructure.MongoDb.EventStore return collection.Indexes.CreateOneAsync(IndexKeys.Ascending(x => x.EventStream).Ascending(x => x.EventsVersion), new CreateIndexOptions { Unique = true }); } + public void CheckConnection() + { + try + { + Database.ListCollections(); + } + catch (Exception e) + { + throw new ConfigurationException($"MongoDb Event Store failed to connect to database {Database.DatabaseNamespace.DatabaseName}", e); + } + } + public IObservable GetEventsAsync(string streamName) { Guard.NotNullOrEmpty(streamName, nameof(streamName)); diff --git a/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs b/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs index 8b5bb1275..919d9d5bf 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoRepositoryBase.cs @@ -84,6 +84,11 @@ namespace Squidex.Infrastructure.MongoDb } } + static MongoRepositoryBase() + { + RefTokenSerializer.Register(); + } + protected MongoRepositoryBase(IMongoDatabase database) { Guard.NotNull(database, nameof(database)); diff --git a/src/Squidex.Infrastructure.RabbitMq/RabbitMqEventChannel.cs b/src/Squidex.Infrastructure.RabbitMq/RabbitMqEventBus.cs similarity index 52% rename from src/Squidex.Infrastructure.RabbitMq/RabbitMqEventChannel.cs rename to src/Squidex.Infrastructure.RabbitMq/RabbitMqEventBus.cs index 0e925eb59..6241b4221 100644 --- a/src/Squidex.Infrastructure.RabbitMq/RabbitMqEventChannel.cs +++ b/src/Squidex.Infrastructure.RabbitMq/RabbitMqEventBus.cs @@ -16,32 +16,58 @@ using Squidex.Infrastructure.CQRS.Events; namespace Squidex.Infrastructure.RabbitMq { - public sealed class RabbitMqEventChannel : DisposableObject, IEventPublisher, IEventStream + public sealed class RabbitMqEventBus : DisposableObject, IEventPublisher, IEventStream, IExternalSystem { + private readonly bool isPersistent; private const string Exchange = "Squidex"; - private readonly IConnection connection; - private readonly IModel channel; + private readonly IConnectionFactory connectionFactory; + private readonly Lazy connection; + private readonly Lazy channel; private EventingBasicConsumer consumer; - public RabbitMqEventChannel(IConnectionFactory connectionFactory) + public RabbitMqEventBus(IConnectionFactory connectionFactory, bool isPersistent) { Guard.NotNull(connectionFactory, nameof(connectionFactory)); - connection = connectionFactory.CreateConnection(); - channel = CreateChannel(connection); + this.connectionFactory = connectionFactory; + + connection = new Lazy(connectionFactory.CreateConnection); + channel = new Lazy(() => CreateChannel(connection.Value)); + + this.isPersistent = isPersistent; } protected override void DisposeObject(bool disposing) { - connection.Close(); - connection.Dispose(); + if (connection.IsValueCreated) + { + connection.Value.Close(); + connection.Value.Dispose(); + } } public void Publish(EventData eventData) { ThrowIfDisposed(); - channel.BasicPublish(Exchange, string.Empty, null, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventData))); + channel.Value.BasicPublish(Exchange, string.Empty, null, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventData))); + } + + public void CheckConnection() + { + try + { + var currentConnection = connection.Value; + + if (!currentConnection.IsOpen) + { + throw new ConfigurationException($"RabbitMq event bus failed to connect to {connectionFactory.VirtualHost}"); + } + } + catch (Exception e) + { + throw new ConfigurationException($"RabbitMq event bus failed to connect to {connectionFactory.VirtualHost}", e); + } } public void Connect(string queueName, Action received) @@ -51,14 +77,16 @@ namespace Squidex.Infrastructure.RabbitMq lock (connection) { + var currentChannel = channel.Value; + ThrowIfConnected(); queueName = $"{queueName}_{Environment.MachineName}"; - channel.QueueDeclare(queueName, true, false, false); - channel.QueueBind(queueName, Exchange, string.Empty); + currentChannel.QueueDeclare(queueName, isPersistent, false, !isPersistent); + currentChannel.QueueBind(queueName, Exchange, string.Empty); - consumer = new EventingBasicConsumer(channel); + consumer = new EventingBasicConsumer(currentChannel); consumer.Received += (model, e) => { @@ -67,7 +95,7 @@ namespace Squidex.Infrastructure.RabbitMq received(eventData); }; - channel.BasicConsume(queueName, true, consumer); + currentChannel.BasicConsume(queueName, true, consumer); } } diff --git a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs index 10ed80b5e..7f5ef6686 100644 --- a/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs +++ b/src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs @@ -20,6 +20,7 @@ namespace Squidex.Infrastructure.CQRS.Events public sealed class EventReceiver { private readonly EventDataFormatter formatter; + private readonly bool canCatch; private readonly IEnumerable liveConsumers; private readonly IEnumerable catchConsumers; private readonly IEventStream eventStream; @@ -31,7 +32,8 @@ namespace Squidex.Infrastructure.CQRS.Events IEventStream eventStream, IEnumerable liveConsumers, IEnumerable catchConsumers, - EventDataFormatter formatter) + EventDataFormatter formatter, + bool canCatch = true) { Guard.NotNull(logger, nameof(logger)); Guard.NotNull(formatter, nameof(formatter)); @@ -41,6 +43,7 @@ namespace Squidex.Infrastructure.CQRS.Events this.logger = logger; this.formatter = formatter; + this.canCatch = canCatch; this.eventStream = eventStream; this.liveConsumers = liveConsumers; this.catchConsumers = catchConsumers; @@ -70,7 +73,7 @@ namespace Squidex.Infrastructure.CQRS.Events { DispatchConsumers(catchConsumers.OfType().Union(liveConsumers), @event); } - else + else if (canCatch) { DispatchConsumers(catchConsumers, @event); } diff --git a/src/Squidex.Infrastructure/CQRS/Events/InMemoryEventBus.cs b/src/Squidex.Infrastructure/CQRS/Events/InMemoryEventBus.cs new file mode 100644 index 000000000..3c047389e --- /dev/null +++ b/src/Squidex.Infrastructure/CQRS/Events/InMemoryEventBus.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// InMemoryEventBus.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Reactive.Subjects; + +namespace Squidex.Infrastructure.CQRS.Events +{ + public class InMemoryEventBus : IEventPublisher, IEventStream + { + private readonly Subject subject = new Subject(); + + public void Dispose() + { + } + + public void Publish(EventData eventData) + { + subject.OnNext(eventData); + } + + public void Connect(string queueName, Action received) + { + subject.Subscribe(received); + } + } +} diff --git a/src/Squidex.Infrastructure/CQRS/Replay/ReplayGenerator.cs b/src/Squidex.Infrastructure/CQRS/Replay/ReplayGenerator.cs index 70a4ff96d..ca0a47602 100644 --- a/src/Squidex.Infrastructure/CQRS/Replay/ReplayGenerator.cs +++ b/src/Squidex.Infrastructure/CQRS/Replay/ReplayGenerator.cs @@ -15,13 +15,15 @@ using Squidex.Infrastructure.CQRS.Events; namespace Squidex.Infrastructure.CQRS.Replay { - public sealed class ReplayGenerator + public sealed class ReplayGenerator : ICliCommand { private readonly ILogger logger; private readonly IEventStore eventStore; private readonly IEventPublisher eventPublisher; private readonly IEnumerable stores; + public string Name { get; } = "replay"; + public ReplayGenerator( ILogger logger, IEventStore eventStore, @@ -39,6 +41,11 @@ namespace Squidex.Infrastructure.CQRS.Replay this.eventPublisher = eventPublisher; } + public void Execute(string[] args) + { + ReplayAllAsync().Wait(); + } + public async Task ReplayAllAsync() { logger.LogDebug("Starting to replay all events"); diff --git a/src/Squidex.Infrastructure/ConfigurationException.cs b/src/Squidex.Infrastructure/ConfigurationException.cs new file mode 100644 index 000000000..ad2890248 --- /dev/null +++ b/src/Squidex.Infrastructure/ConfigurationException.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// ConfigurationException.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; + +namespace Squidex.Infrastructure +{ + public sealed class ConfigurationException : Exception + { + public ConfigurationException() + { + } + + public ConfigurationException(string message) + : base(message) + { + } + + public ConfigurationException(string message, Exception inner) + : base(message, inner) + { + } + } +} diff --git a/src/Squidex.Read.MongoDb/MyMongoDbOptions.cs b/src/Squidex.Infrastructure/ICliCommand.cs similarity index 63% rename from src/Squidex.Read.MongoDb/MyMongoDbOptions.cs rename to src/Squidex.Infrastructure/ICliCommand.cs index 21b258a32..5cbf9a2e6 100644 --- a/src/Squidex.Read.MongoDb/MyMongoDbOptions.cs +++ b/src/Squidex.Infrastructure/ICliCommand.cs @@ -1,17 +1,17 @@ // ========================================================================== -// MyMongoDbOptions.cs +// ICommand.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== -namespace Squidex.Read.MongoDb +namespace Squidex.Infrastructure { - public class MyMongoDbOptions + public interface ICliCommand { - public string ConnectionString { get; set; } + string Name { get; } - public string DatabaseName { get; set; } + void Execute(string[] args); } } diff --git a/src/Squidex.Infrastructure/IExternalSystem.cs b/src/Squidex.Infrastructure/IExternalSystem.cs new file mode 100644 index 000000000..8de6ab721 --- /dev/null +++ b/src/Squidex.Infrastructure/IExternalSystem.cs @@ -0,0 +1,15 @@ +// ========================================================================== +// IExternalSystem.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +namespace Squidex.Infrastructure +{ + public interface IExternalSystem + { + void CheckConnection(); + } +} diff --git a/src/Squidex.Read.MongoDb/MongoDbModule.cs b/src/Squidex.Read.MongoDb/MongoDbModule.cs deleted file mode 100644 index a28686976..000000000 --- a/src/Squidex.Read.MongoDb/MongoDbModule.cs +++ /dev/null @@ -1,100 +0,0 @@ -// ========================================================================== -// MongoDbModule.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Autofac; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.MongoDB; -using Microsoft.Extensions.Options; -using MongoDB.Driver; -using Squidex.Infrastructure.CQRS.Events; -using Squidex.Infrastructure.CQRS.Replay; -using Squidex.Infrastructure.MongoDb; -using Squidex.Read.Apps.Repositories; -using Squidex.Read.Contents.Repositories; -using Squidex.Read.History.Repositories; -using Squidex.Read.MongoDb.Apps; -using Squidex.Read.MongoDb.Contents; -using Squidex.Read.MongoDb.History; -using Squidex.Read.MongoDb.Infrastructure; -using Squidex.Read.MongoDb.Schemas; -using Squidex.Read.MongoDb.Users; -using Squidex.Read.Schemas.Repositories; -using Squidex.Read.Users.Repositories; - -namespace Squidex.Read.MongoDb -{ - public class MongoDbModule : Module - { - protected override void Load(ContainerBuilder builder) - { - RefTokenSerializer.Register(); - - builder.Register(context => - { - var options = context.Resolve>().Value; - - var mongoDbClient = new MongoClient(options.ConnectionString); - var mongoDatabase = mongoDbClient.GetDatabase(options.DatabaseName); - - return mongoDatabase; - }).SingleInstance(); - - builder.Register>(context => - { - var usersCollection = context.Resolve().GetCollection("Identity_Users"); - - IndexChecks.EnsureUniqueIndexOnNormalizedEmail(usersCollection); - IndexChecks.EnsureUniqueIndexOnNormalizedUserName(usersCollection); - - return new UserStore(usersCollection); - }).SingleInstance(); - - builder.Register>(context => - { - var rolesCollection = context.Resolve().GetCollection("Identity_Roles"); - - IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(rolesCollection); - - return new RoleStore(rolesCollection); - }).SingleInstance(); - - builder.RegisterType() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .InstancePerLifetimeScope(); - - builder.RegisterType() - .As() - .As() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .As() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .As() - .As() - .SingleInstance(); - - builder.RegisterType() - .As() - .As() - .As() - .SingleInstance(); - } - } -} diff --git a/src/Squidex.Read.MongoDb/MongoDbStoresExternalSystem.cs b/src/Squidex.Read.MongoDb/MongoDbStoresExternalSystem.cs new file mode 100644 index 000000000..17c1dd1c4 --- /dev/null +++ b/src/Squidex.Read.MongoDb/MongoDbStoresExternalSystem.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// MongoDbStoresExternalSystem.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using MongoDB.Driver; +using Squidex.Infrastructure; + +namespace Squidex.Read.MongoDb +{ + public sealed class MongoDbStoresExternalSystem : IExternalSystem + { + private readonly IMongoDatabase database; + + public MongoDbStoresExternalSystem(IMongoDatabase database) + { + Guard.NotNull(database, nameof(database)); + + this.database = database; + } + + public void CheckConnection() + { + try + { + database.ListCollections(); + } + catch (Exception e) + { + throw new ConfigurationException($"MongoDb Event Store failed to connect to database {database.DatabaseNamespace.DatabaseName}", e); + } + } + } +} diff --git a/src/Squidex.Read.MongoDb/project.json b/src/Squidex.Read.MongoDb/project.json index 90933c3d9..6721a0e25 100644 --- a/src/Squidex.Read.MongoDb/project.json +++ b/src/Squidex.Read.MongoDb/project.json @@ -1,6 +1,5 @@ { "dependencies": { - "Autofac": "4.3.0", "IdentityServer4": "1.0.2", "Microsoft.AspNetCore.Identity": "1.1.0", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.2", diff --git a/src/Squidex/Config/Identity/MyIdentityOptions.cs b/src/Squidex/Config/Identity/MyIdentityOptions.cs index 775a924d7..8b2cc4cf6 100644 --- a/src/Squidex/Config/Identity/MyIdentityOptions.cs +++ b/src/Squidex/Config/Identity/MyIdentityOptions.cs @@ -18,8 +18,6 @@ namespace Squidex.Config.Identity public string GoogleSecret { get; set; } - public string KeysFolder { get; set; } - public bool RequiresHttps { get; set; } } } diff --git a/tests/Squidex.Core.Tests/Contents/ContentDataTests.cs b/tests/Squidex.Core.Tests/Contents/ContentDataTests.cs index 1282af754..66bc8c637 100644 --- a/tests/Squidex.Core.Tests/Contents/ContentDataTests.cs +++ b/tests/Squidex.Core.Tests/Contents/ContentDataTests.cs @@ -239,7 +239,7 @@ namespace Squidex.Core.Contents { { "field1", 1 }, { "field2", 4 }, - { "field3", 6 }, + { "field3", 6 } }; Assert.True(expected.EqualsDictionary(output));