Browse Source

Configuration improved

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
06fb27eeb5
  1. 16
      src/Squidex.Infrastructure.RabbitMq/RabbitMqEventConsumer.cs
  2. 4
      src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs
  3. 2
      src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs
  4. 2
      src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs
  5. 23
      src/Squidex.Infrastructure/Singletons.cs
  6. 2
      src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs
  7. 2
      src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  8. 2
      src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs
  9. 2
      src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
  10. 2
      src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs
  11. 2
      src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
  12. 85
      src/Squidex/Config/Domain/ClusterModule.cs
  13. 80
      src/Squidex/Config/Domain/EventPublishersModule.cs
  14. 53
      src/Squidex/Config/Domain/EventStoreModule.cs
  15. 10
      src/Squidex/Config/Domain/InfrastructureModule.cs
  16. 70
      src/Squidex/Config/Domain/PubSubModule.cs
  17. 48
      src/Squidex/Config/Domain/RabbitMqModule.cs
  18. 6
      src/Squidex/Config/Domain/StoreModule.cs
  19. 51
      src/Squidex/Config/Domain/StoreMongoDbModule.cs
  20. 34
      src/Squidex/Config/Identity/IdentityServices.cs
  21. 6
      src/Squidex/Config/MyUrlsOptions.cs
  22. 10
      src/Squidex/Startup.cs
  23. 86
      src/Squidex/appsettings.json
  24. 4
      tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs

16
src/Squidex.Infrastructure.RabbitMq/RabbitMqEventConsumer.cs

@ -21,25 +21,27 @@ namespace Squidex.Infrastructure.RabbitMq
public sealed class RabbitMqEventConsumer : DisposableObjectBase, IExternalSystem, IEventConsumer
{
private readonly JsonSerializerSettings serializerSettings;
private readonly string eventPublisherName;
private readonly string exchange;
private readonly string streamFilter;
private readonly string eventsFilter;
private readonly ConnectionFactory connectionFactory;
private readonly Lazy<IConnection> connection;
private readonly Lazy<IModel> channel;
public string Name
{
get { return GetType().Name; }
get { return eventPublisherName; }
}
public string StreamFilter
public string EventsFilter
{
get { return streamFilter; }
get { return eventsFilter; }
}
public RabbitMqEventConsumer(JsonSerializerSettings serializerSettings, string uri, string exchange, string streamFilter)
public RabbitMqEventConsumer(JsonSerializerSettings serializerSettings, string eventPublisherName, string uri, string exchange, string eventsFilter)
{
Guard.NotNullOrEmpty(uri, nameof(uri));
Guard.NotNullOrEmpty(eventPublisherName, nameof(eventPublisherName));
Guard.NotNullOrEmpty(exchange, nameof(exchange));
Guard.NotNull(serializerSettings, nameof(serializerSettings));
@ -49,8 +51,8 @@ namespace Squidex.Infrastructure.RabbitMq
channel = new Lazy<IModel>(() => connection.Value.CreateModel());
this.exchange = exchange;
this.streamFilter = streamFilter;
this.eventsFilter = eventsFilter;
this.eventPublisherName = eventPublisherName;
this.serializerSettings = serializerSettings;
}

4
src/Squidex.Infrastructure/CQRS/Events/CompoundEventConsumer.cs

@ -17,9 +17,9 @@ namespace Squidex.Infrastructure.CQRS.Events
public string Name { get; }
public string StreamFilter
public string EventsFilter
{
get { return inners.FirstOrDefault()?.StreamFilter; }
get { return inners.FirstOrDefault()?.EventsFilter; }
}
public CompoundEventConsumer(IEventConsumer first, params IEventConsumer[] inners)

2
src/Squidex.Infrastructure/CQRS/Events/EventReceiver.cs

@ -111,7 +111,7 @@ namespace Squidex.Infrastructure.CQRS.Events
}
await eventStore.GetEventsAsync(se => HandleEventAsync(eventConsumer, se, consumerName), ct,
eventConsumer.StreamFilter, lastHandledEventNumber);
eventConsumer.EventsFilter, lastHandledEventNumber);
}
catch (Exception ex)
{

2
src/Squidex.Infrastructure/CQRS/Events/IEventConsumer.cs

@ -14,7 +14,7 @@ namespace Squidex.Infrastructure.CQRS.Events
{
string Name { get; }
string StreamFilter { get; }
string EventsFilter { get; }
Task ClearAsync();

23
src/Squidex.Infrastructure/Singletons.cs

@ -0,0 +1,23 @@
// ==========================================================================
// Singletons.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Concurrent;
namespace Squidex.Infrastructure
{
public static class Singletons<T>
{
private static readonly ConcurrentDictionary<string, T> instances = new ConcurrentDictionary<string, T>(StringComparer.OrdinalIgnoreCase);
public static T GetOrAdd(string key, Func<string, T> factory)
{
return instances.GetOrAdd(key, factory);
}
}
}

2
src/Squidex.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs

@ -23,7 +23,7 @@ namespace Squidex.Read.MongoDb.Apps
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "^app-"; }
}

2
src/Squidex.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -37,7 +37,7 @@ namespace Squidex.Read.MongoDb.Contents
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "^content-"; }
}

2
src/Squidex.Read.MongoDb/History/MongoHistoryEventRepository.cs

@ -32,7 +32,7 @@ namespace Squidex.Read.MongoDb.History
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "*"; }
}

2
src/Squidex.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs

@ -26,7 +26,7 @@ namespace Squidex.Read.MongoDb.Schemas
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "^schema-"; }
}

2
src/Squidex.Read/Apps/Services/Implementations/CachingAppProvider.cs

@ -32,7 +32,7 @@ namespace Squidex.Read.Apps.Services.Implementations
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "*"; }
}

2
src/Squidex.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs

@ -32,7 +32,7 @@ namespace Squidex.Read.Schemas.Services.Implementations
get { return GetType().Name; }
}
public string StreamFilter
public string EventsFilter
{
get { return "*"; }
}

85
src/Squidex/Config/Domain/ClusterModule.cs

@ -1,85 +0,0 @@
// ==========================================================================
// ClusterModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Autofac;
using Microsoft.Extensions.Configuration;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Redis;
using StackExchange.Redis;
namespace Squidex.Config.Domain
{
public class ClusterModule : Module
{
private IConfiguration Configuration { get; }
public ClusterModule(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void Load(ContainerBuilder builder)
{
var handleEvents = Configuration.GetValue<bool>("squidex:handleEvents");
if (handleEvents)
{
builder.RegisterType<EventReceiver>()
.AsSelf()
.InstancePerDependency();
}
var clustererType = Configuration.GetValue<string>("squidex:clusterer:type");
if (string.IsNullOrWhiteSpace(clustererType))
{
throw new ConfigurationException("You must specify the clusterer type in the 'squidex:clusterer:type' configuration section.");
}
if (string.Equals(clustererType, "Redis", StringComparison.OrdinalIgnoreCase))
{
var connectionString = Configuration.GetValue<string>("squidex:clusterer:redis:connectionString");
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ConfigurationException("You must specify the Redis connection string in the 'squidex:clusterer:redis:connectionString' configuration section.");
}
try
{
var connectionMultiplexer = ConnectionMultiplexer.Connect(connectionString);
builder.RegisterInstance(connectionMultiplexer)
.As<IConnectionMultiplexer>()
.SingleInstance();
}
catch (Exception ex)
{
throw new ConfigurationException($"Redis connection failed to connect to database {connectionString}", ex);
}
builder.RegisterType<RedisPubSub>()
.As<IPubSub>()
.As<IExternalSystem>()
.SingleInstance();
}
else if (string.Equals(clustererType, "None", StringComparison.OrdinalIgnoreCase))
{
builder.RegisterType<InMemoryPubSub>()
.As<IPubSub>()
.SingleInstance();
}
else
{
throw new ConfigurationException($"Unsupported clusterer type '{clustererType}' for key 'squidex:clusterer:type', supported: Redis, None.");
}
}
}
}

80
src/Squidex/Config/Domain/EventPublishersModule.cs

@ -0,0 +1,80 @@
// ==========================================================================
// RabbitMqModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Autofac;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.RabbitMq;
// ReSharper disable InvertIf
namespace Squidex.Config.Domain
{
public sealed class EventPublishersModule : Module
{
private IConfiguration Configuration { get; }
public EventPublishersModule(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void Load(ContainerBuilder builder)
{
var eventPublishers = Configuration.GetSection("eventPublishers");
foreach (var child in eventPublishers.GetChildren())
{
var eventPublisherType = child.GetValue<string>("type");
if (string.IsNullOrWhiteSpace(eventPublisherType))
{
throw new ConfigurationException($"Configure EventPublisher type with 'eventPublishers:{child.Key}:type'.");
}
var eventsFilter = Configuration.GetValue<string>("eventsFilter");
var enabled = child.GetValue<bool>("enabled");
if (string.Equals(eventPublisherType, "RabbitMq", StringComparison.OrdinalIgnoreCase))
{
var configuration = child.GetValue<string>("configuration");
if (string.IsNullOrWhiteSpace(configuration))
{
throw new ConfigurationException($"Configure EventPublisher RabbitMq configuration with 'eventPublishers:{child.Key}:configuration'.");
}
var exchange = child.GetValue<string>("exchange");
if (string.IsNullOrWhiteSpace(exchange))
{
throw new ConfigurationException($"Configure EventPublisher RabbitMq exchange with 'eventPublishers:{child.Key}:configuration'.");
}
var name = $"EventPublishers_{child.Key}";
if (enabled)
{
builder.Register(c => new RabbitMqEventConsumer(c.Resolve<JsonSerializerSettings>(), name, configuration, exchange, eventsFilter))
.As<IEventConsumer>()
.As<IExternalSystem>()
.SingleInstance();
}
}
else
{
throw new ConfigurationException($"Unsupported value '{child.Key}' for 'eventPublishers:{child.Key}:type', supported: RabbitMq.");
}
}
}
}
}

53
src/Squidex/Config/Domain/EventStoreModule.cs

@ -8,17 +8,20 @@
using System;
using Autofac;
using Autofac.Core;
using Microsoft.Extensions.Configuration;
using MongoDB.Driver;
using NodaTime;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.MongoDb.EventStore;
namespace Squidex.Config.Domain
{
public class EventStoreModule : Module
public sealed class EventStoreModule : Module
{
private const string MongoClientRegistration = "EventStoreMongoClient";
private const string MongoDatabaseRegistration = "EventStoreMongoDatabase";
private IConfiguration Configuration { get; }
public EventStoreModule(IConfiguration configuration)
@ -28,45 +31,55 @@ namespace Squidex.Config.Domain
protected override void Load(ContainerBuilder builder)
{
var storeType = Configuration.GetValue<string>("squidex:eventStore:type");
var consumeEvents = Configuration.GetValue<bool>("eventStore:consume");
if (string.IsNullOrWhiteSpace(storeType))
if (consumeEvents)
{
throw new ConfigurationException("You must specify the store type in the 'squidex:eventStore:type' configuration section.");
builder.RegisterType<EventReceiver>()
.AsSelf()
.InstancePerDependency();
}
if (string.Equals(storeType, "MongoDb", StringComparison.OrdinalIgnoreCase))
var eventStoreType = Configuration.GetValue<string>("eventStore:type");
if (string.IsNullOrWhiteSpace(eventStoreType))
{
var databaseName = Configuration.GetValue<string>("squidex:eventStore:mongoDb:databaseName");
throw new ConfigurationException("Configure EventStore type with 'eventStore:type'.");
}
if (string.IsNullOrWhiteSpace(databaseName))
if (string.Equals(eventStoreType, "MongoDb", StringComparison.OrdinalIgnoreCase))
{
var configuration = Configuration.GetValue<string>("eventStore:mongoDb:configuration");
if (string.IsNullOrWhiteSpace(configuration))
{
throw new ConfigurationException("You must specify the MongoDB database name in the 'squidex:eventStore:mongoDb:databaseName' configuration section.");
throw new ConfigurationException("Configure EventStore MongoDb configuration with 'eventStore:mongoDb:configuration'.");
}
var connectionString = Configuration.GetValue<string>("squidex:eventStore:mongoDb:connectionString");
var database = Configuration.GetValue<string>("eventStore:mongoDb:database");
if (string.IsNullOrWhiteSpace(connectionString))
if (string.IsNullOrWhiteSpace(database))
{
throw new ConfigurationException("You must specify the MongoDB connection string in the 'squidex:eventStore:mongoDb:connectionString' configuration section.");
throw new ConfigurationException("Configure EventStore MongoDb Database name with 'eventStore:mongoDb:database'.");
}
builder.Register(c =>
{
var mongoDbClient = new MongoClient(connectionString);
var mongoDatabase = mongoDbClient.GetDatabase(databaseName);
builder.Register(c => Singletons<IMongoClient>.GetOrAdd(configuration, s => new MongoClient(s)))
.Named<IMongoClient>(MongoClientRegistration)
.SingleInstance();
var eventStore = new MongoEventStore(mongoDatabase, c.Resolve<IEventNotifier>(), c.Resolve<IClock>());
builder.Register(c => c.ResolveNamed<IMongoClient>(MongoClientRegistration).GetDatabase(database))
.Named<IMongoDatabase>(MongoDatabaseRegistration)
.SingleInstance();
return eventStore;
})
builder.RegisterType<MongoEventStore>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IExternalSystem>()
.As<IEventStore>()
.SingleInstance();
}
else
{
throw new ConfigurationException($"Unsupported store type '{storeType}' for key 'squidex:eventStore:type', supported: MongoDb.");
throw new ConfigurationException($"Unsupported value '{eventStoreType}' for 'eventStore:type', supported: MongoDb.");
}
}
}

10
src/Squidex/Config/Domain/InfrastructureModule.cs

@ -28,7 +28,7 @@ using Squidex.Pipeline;
namespace Squidex.Config.Domain
{
public class InfrastructureModule : Module
public sealed class InfrastructureModule : Module
{
private IConfiguration Configuration { get; }
@ -39,7 +39,7 @@ namespace Squidex.Config.Domain
protected override void Load(ContainerBuilder builder)
{
if (Configuration.GetValue<bool>("squidex:logging:human"))
if (Configuration.GetValue<bool>("logging:human"))
{
builder.Register(c => new Func<IObjectWriter>(() => new JsonLogWriter(Formatting.Indented, true)))
.AsSelf()
@ -52,11 +52,11 @@ namespace Squidex.Config.Domain
.SingleInstance();
}
var logFile = Configuration.GetValue<string>("squidex:logging:file");
var loggingFile = Configuration.GetValue<string>("logging:file");
if (!string.IsNullOrWhiteSpace(logFile))
if (!string.IsNullOrWhiteSpace(loggingFile))
{
builder.RegisterInstance(new FileChannel(logFile))
builder.RegisterInstance(new FileChannel(loggingFile))
.As<ILogChannel>()
.As<IExternalSystem>()
.SingleInstance();

70
src/Squidex/Config/Domain/PubSubModule.cs

@ -0,0 +1,70 @@
// ==========================================================================
// ClusterModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Autofac;
using Autofac.Core;
using Microsoft.Extensions.Configuration;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Redis;
using StackExchange.Redis;
namespace Squidex.Config.Domain
{
public sealed class PubSubModule : Module
{
private const string RedisRegistration = "PubSubRedis";
private IConfiguration Configuration { get; }
public PubSubModule(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void Load(ContainerBuilder builder)
{
var pubSubType = Configuration.GetValue<string>("pubSub:type");
if (string.IsNullOrWhiteSpace(pubSubType))
{
throw new ConfigurationException("Configure the PubSub type with 'pubSub:type'.");
}
if (string.Equals(pubSubType, "Redis", StringComparison.OrdinalIgnoreCase))
{
var configuration = Configuration.GetValue<string>("pubsub:redis:configuration");
if (string.IsNullOrWhiteSpace(configuration))
{
throw new ConfigurationException("Configure PubSub Redis configuration with pubSub:redis:configuration'.");
}
builder.Register(c => Singletons<IConnectionMultiplexer>.GetOrAdd(configuration, s => ConnectionMultiplexer.Connect(s)))
.Named<IConnectionMultiplexer>(RedisRegistration)
.SingleInstance();
builder.RegisterType<RedisPubSub>()
.WithParameter(ResolvedParameter.ForNamed<IConnectionMultiplexer>(RedisRegistration))
.As<IPubSub>()
.As<IExternalSystem>()
.SingleInstance();
}
else if (string.Equals(pubSubType, "InMemory", StringComparison.OrdinalIgnoreCase))
{
builder.RegisterType<InMemoryPubSub>()
.As<IPubSub>()
.SingleInstance();
}
else
{
throw new ConfigurationException($"Unsupported value '{pubSubType}' for 'pubSub:type', supported: Redis, InMemory.");
}
}
}
}

48
src/Squidex/Config/Domain/RabbitMqModule.cs

@ -1,48 +0,0 @@
// ==========================================================================
// RabbitMqModule.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Autofac;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.RabbitMq;
// ReSharper disable InvertIf
namespace Squidex.Config.Domain
{
public sealed class RabbitMqModule : Module
{
private IConfiguration Configuration { get; }
public RabbitMqModule(IConfiguration configuration)
{
Configuration = configuration;
}
protected override void Load(ContainerBuilder builder)
{
var connectionString = Configuration.GetValue<string>("squidex:eventPublishers:rabbitMq:connectionString");
var exchange = Configuration.GetValue<string>("squidex:eventPublishers:rabbitMq:exchange");
var enabled = Configuration.GetValue<bool>("squidex:eventPublishers:rabbitMq:enabled");
if (!string.IsNullOrWhiteSpace(connectionString) &&
!string.IsNullOrWhiteSpace(exchange) &&
enabled)
{
var streamFilter = Configuration.GetValue<string>("squidex:eventPublishers:rabbitMq:streamFilter");
builder.Register(c => new RabbitMqEventConsumer(c.Resolve<JsonSerializerSettings>(), connectionString, exchange, streamFilter))
.As<IEventConsumer>()
.As<IExternalSystem>()
.SingleInstance();
}
}
}
}

6
src/Squidex/Config/Domain/StoreModule.cs

@ -24,11 +24,11 @@ namespace Squidex.Config.Domain
protected override void Load(ContainerBuilder builder)
{
var storeType = Configuration.GetValue<string>("squidex:stores:type");
var storeType = Configuration.GetValue<string>("store:type");
if (string.IsNullOrWhiteSpace(storeType))
{
throw new ConfigurationException("You must specify the store type in the 'squidex:stores:type' configuration section.");
throw new ConfigurationException("Configure the Store type with 'store:type'.");
}
if (string.Equals(storeType, "MongoDB", StringComparison.OrdinalIgnoreCase))
@ -37,7 +37,7 @@ namespace Squidex.Config.Domain
}
else
{
throw new ConfigurationException($"Unsupported store type '{storeType}' for key 'squidex:stores:type', supported: MongoDb.");
throw new ConfigurationException($"Unsupported value '{storeType}' for 'stores:type', supported: MongoDb.");
}
}
}

51
src/Squidex/Config/Domain/StoreMongoDbModule.cs

@ -34,8 +34,9 @@ namespace Squidex.Config.Domain
{
public class StoreMongoDbModule : Module
{
private const string MongoDatabaseName = "MongoDatabaseName";
private const string MongoDatabaseNameContent = "MongoDatabaseNameContent";
private const string MongoClientRegistration = "StoreMongoClient";
private const string MongoDatabaseRegistration = "StoreMongoDatabaseName";
private const string MongoContentDatabaseRegistration = "StoreMongoDatabaseNameContent";
private IConfiguration Configuration { get; }
@ -46,42 +47,42 @@ namespace Squidex.Config.Domain
protected override void Load(ContainerBuilder builder)
{
var databaseName = Configuration.GetValue<string>("squidex:stores:mongoDb:databaseName");
var configuration = Configuration.GetValue<string>("store:mongoDb:configuration");
if (string.IsNullOrWhiteSpace(databaseName))
if (string.IsNullOrWhiteSpace(configuration))
{
throw new ConfigurationException("You must specify the MongoDB database name in the 'squidex:stores:mongoDb:databaseName' configuration section.");
throw new ConfigurationException("Configure the Store MongoDb configuration with 'store:mongoDb:configuration'.");
}
var connectionString = Configuration.GetValue<string>("squidex:stores:mongoDb:connectionString");
var database = Configuration.GetValue<string>("store:mongoDb:database");
if (string.IsNullOrWhiteSpace(connectionString))
if (string.IsNullOrWhiteSpace(database))
{
throw new ConfigurationException("You must specify the MongoDB connection string in the 'squidex:stores:mongoDb:connectionString' configuration section.");
throw new ConfigurationException("Configure the Store MongoDb database with 'store:mongoDb:database'.");
}
var databaseNameContent = Configuration.GetValue<string>("squidex:stores:mongoDb:databaseNameContent");
var contentDatabase = Configuration.GetValue<string>("store:mongoDb:databaseNameContent");
if (string.IsNullOrWhiteSpace(databaseNameContent))
if (string.IsNullOrWhiteSpace(contentDatabase))
{
databaseNameContent = databaseName;
contentDatabase = database;
}
builder.Register(c => new MongoClient(connectionString))
.As<IMongoClient>()
builder.Register(c => Singletons<IMongoClient>.GetOrAdd(configuration, s => new MongoClient(s)))
.Named<IMongoClient>(MongoClientRegistration)
.SingleInstance();
builder.Register(c => c.Resolve<IMongoClient>().GetDatabase(databaseName))
.Named<IMongoDatabase>(MongoDatabaseName)
builder.Register(c => c.ResolveNamed<IMongoClient>(MongoClientRegistration).GetDatabase(database))
.Named<IMongoDatabase>(MongoDatabaseRegistration)
.SingleInstance();
builder.Register(c => c.Resolve<IMongoClient>().GetDatabase(databaseNameContent))
.Named<IMongoDatabase>(MongoDatabaseNameContent)
builder.Register(c => c.ResolveNamed<IMongoClient>(MongoClientRegistration).GetDatabase(contentDatabase))
.Named<IMongoDatabase>(MongoContentDatabaseRegistration)
.SingleInstance();
builder.Register<IUserStore<IdentityUser>>(c =>
{
var usersCollection = c.ResolveNamed<IMongoDatabase>(MongoDatabaseName).GetCollection<IdentityUser>("Identity_Users");
var usersCollection = c.ResolveNamed<IMongoDatabase>(MongoDatabaseRegistration).GetCollection<IdentityUser>("Identity_Users");
IndexChecks.EnsureUniqueIndexOnNormalizedEmail(usersCollection);
IndexChecks.EnsureUniqueIndexOnNormalizedUserName(usersCollection);
@ -92,7 +93,7 @@ namespace Squidex.Config.Domain
builder.Register<IRoleStore<IdentityRole>>(c =>
{
var rolesCollection = c.ResolveNamed<IMongoDatabase>(MongoDatabaseName).GetCollection<IdentityRole>("Identity_Roles");
var rolesCollection = c.ResolveNamed<IMongoDatabase>(MongoDatabaseRegistration).GetCollection<IdentityRole>("Identity_Roles");
IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(rolesCollection);
@ -105,25 +106,25 @@ namespace Squidex.Config.Domain
.InstancePerLifetimeScope();
builder.RegisterType<MongoPersistedGrantStore>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseName))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IPersistedGrantStore>()
.SingleInstance();
builder.RegisterType<MongoEventConsumerInfoRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseName))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IEventConsumerInfoRepository>()
.AsSelf()
.SingleInstance();
builder.RegisterType<MongoContentRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseNameContent))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoContentDatabaseRegistration))
.As<IContentRepository>()
.As<IEventConsumer>()
.AsSelf()
.SingleInstance();
builder.RegisterType<MongoHistoryEventRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseName))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IHistoryEventRepository>()
.As<IEventConsumer>()
.As<IExternalSystem>()
@ -131,14 +132,14 @@ namespace Squidex.Config.Domain
.SingleInstance();
builder.RegisterType<MongoSchemaRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseName))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<ISchemaRepository>()
.As<IExternalSystem>()
.AsSelf()
.SingleInstance();
builder.RegisterType<MongoAppRepository>()
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseName))
.WithParameter(ResolvedParameter.ForNamed<IMongoDatabase>(MongoDatabaseRegistration))
.As<IAppRepository>()
.As<IEventConsumer>()
.As<IExternalSystem>()

34
src/Squidex/Config/Identity/IdentityServices.cs

@ -1,4 +1,5 @@
// ==========================================================================

// ==========================================================================
// IdentityServices.cs
// Squidex Headless CMS
// ==========================================================================
@ -30,29 +31,40 @@ namespace Squidex.Config.Identity
{
var dataProtection = services.AddDataProtection().SetApplicationName("Squidex");
var clustererType = configuration.GetValue<string>("squidex:clusterer:type");
var keyStoreType = configuration.GetValue<string>("identity:keysStore:type");
if (string.IsNullOrWhiteSpace(keyStoreType))
{
throw new ConfigurationException("Configure KeyStore type with 'identity:keysStore:type'.");
}
if (clustererType.Equals("redis", StringComparison.OrdinalIgnoreCase))
if (string.Equals(keyStoreType, "Redis", StringComparison.OrdinalIgnoreCase))
{
var connectionString = configuration.GetValue<string>("squidex:clusterer:redis:connectionString");
var redisConfiguration = configuration.GetValue<string>("identity:keysStore:redis:configuration");
if (string.IsNullOrWhiteSpace(connectionString))
if (string.IsNullOrWhiteSpace(redisConfiguration))
{
throw new ConfigurationException("You must specify the Redis connection string in the 'squidex:clusterer:redis:connectionString' configuration section.");
throw new ConfigurationException("Configure KeyStore Redis configuration with 'identity:keysStore:redis:configuration'.");
}
var connectionMultiplexer = ConnectionMultiplexer.Connect(connectionString);
var connectionMultiplexer = Singletons<ConnectionMultiplexer>.GetOrAdd(redisConfiguration, s => ConnectionMultiplexer.Connect(s));
dataProtection.PersistKeysToRedis(connectionMultiplexer);
}
else
else if (string.Equals(keyStoreType, "Folder", StringComparison.OrdinalIgnoreCase))
{
var keysFolder = configuration.GetValue<string>("squidex:identity:keysFolder");
var folderPath = configuration.GetValue<string>("identity:keysStore:folder:path");
if (!string.IsNullOrWhiteSpace(keysFolder))
if (string.IsNullOrWhiteSpace(folderPath))
{
dataProtection.PersistKeysToFileSystem(new DirectoryInfo(keysFolder));
throw new ConfigurationException("Configure KeyStore Folder path with 'identity:keysStore:folder:path'.");
}
dataProtection.PersistKeysToFileSystem(new DirectoryInfo(folderPath));
}
else if (!string.Equals(keyStoreType, "InMemory", StringComparison.OrdinalIgnoreCase))
{
throw new ConfigurationException($"Unsupported value '{keyStoreType}' for 'identity:keysStore:type', supported: Redis, Folder, InMemory.");
}
return services;

6
src/Squidex/Config/MyUrlsOptions.cs

@ -7,6 +7,7 @@
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Config
{
@ -18,6 +19,11 @@ namespace Squidex.Config
public string BuildUrl(string path, bool trailingSlash = true)
{
if (string.IsNullOrWhiteSpace(BaseUrl))
{
throw new ConfigurationException("Configure BaseUrl with 'urls:baseUrl'.");
}
var url = $"{BaseUrl.TrimEnd('/')}/{path.Trim('/')}";
if (trailingSlash &&

10
src/Squidex/Startup.cs

@ -51,7 +51,7 @@ namespace Squidex
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true)
.AddEnvironmentVariables();
.AddEnvironmentVariables("SQUIDEX__");
Configuration = builder.Build();
}
@ -71,16 +71,16 @@ namespace Squidex
services.AddRouting();
services.Configure<MyUrlsOptions>(
Configuration.GetSection("squidex:urls"));
Configuration.GetSection("urls"));
services.Configure<MyIdentityOptions>(
Configuration.GetSection("squidex:identity"));
Configuration.GetSection("identity"));
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new ClusterModule(Configuration));
builder.RegisterModule(new EventPublishersModule(Configuration));
builder.RegisterModule(new EventStoreModule(Configuration));
builder.RegisterModule(new InfrastructureModule(Configuration));
builder.RegisterModule(new RabbitMqModule(Configuration));
builder.RegisterModule(new PubSubModule(Configuration));
builder.RegisterModule(new ReadModule(Configuration));
builder.RegisterModule(new StoreModule(Configuration));
builder.RegisterModule(new WebModule(Configuration));

86
src/Squidex/appsettings.json

@ -1,45 +1,53 @@
{
"squidex": {
"urls": {
"baseUrl": "http://localhost:5000"
"urls": {
"baseUrl": "http://localhost:5000"
},
"logging": {
"human": false
},
"pubSub": {
"type": "InMemory",
"redis": {
"configuration": "localhost:6379,resolveDns=1"
}
},
"eventStore": {
"type": "MongoDb",
"mongoDb": {
"configuration": "mongodb://localhost",
"database": "Squidex"
},
"logging": {
"human": false
},
"clusterer": {
"type": "none",
"consume": true
},
"eventPublishers": {
"allToRabbitMq": {
"type": "RabbitMq",
"configuration": "amqp://guest:guest@localhost/",
"exchange": "squidex",
"enabled": false,
"eventsFilter": "*"
}
},
"store": {
"type": "MongoDb",
"mongoDb": {
"configuration": "mongodb://localhost",
"contentDatabase": "SquidexContent",
"database": "Squidex"
}
},
"identity": {
"googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com",
"googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg",
"lockAutomatically": true,
"keysStore": {
"type": "InMemory",
"redis": {
"connectionString": "localhost:6379,resolveDns=1"
}
},
"eventStore": {
"type": "mongoDb",
"mongoDb": {
"connectionString": "mongodb://localhost",
"databaseName": "Squidex"
"configuration": "localhost:6379,resolveDns=1"
},
"folder": {
"path": "keys"
}
},
"eventPublishers": {
"rabbitMq": {
"connectionString": "amqp://guest:guest@localhost/",
"exchange": "squidex",
"enabled": false,
"streamFilter": "*"
}
},
"stores": {
"type": "mongoDb",
"mongoDb": {
"connectionString": "mongodb://localhost",
"databaseName": "Squidex",
"databaseNameContent": "SquidexContent"
}
},
"identity": {
"googleClient": "1006817248705-t3lb3ge808m9am4t7upqth79hulk456l.apps.googleusercontent.com",
"googleSecret": "QsEi-fHqkGw2_PjJmtNHf2wg",
"lockAutomatically": true
},
"handleEvents": true
}
}
}

4
tests/Squidex.Infrastructure.Tests/CQRS/Events/CompoundEventConsumerTests.cs

@ -47,11 +47,11 @@ namespace Squidex.Infrastructure.CQRS.Events
{
const string filter = "my-inner-filter";
consumer1.Setup(x => x.StreamFilter).Returns(filter);
consumer1.Setup(x => x.EventsFilter).Returns(filter);
var sut = new CompoundEventConsumer(consumer1.Object, consumer2.Object);
Assert.Equal(filter, sut.StreamFilter);
Assert.Equal(filter, sut.EventsFilter);
}
[Fact]

Loading…
Cancel
Save