Skip to content

Commit

Permalink
Merge pull request #59 from marcwittke/release/5.1.0
Browse files Browse the repository at this point in the history
Release/5.1.0
  • Loading branch information
marcwittke committed Feb 23, 2019
2 parents 46135cd + d4c970a commit d87eda4
Show file tree
Hide file tree
Showing 18 changed files with 249 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
/// </summary>
public interface ITenantManager : IDisposable
{
event EventHandler<TenantId> TenantCreated;

TenantId[] GetTenantIds();
Tenant[] GetTenants();
Tenant GetTenant(TenantId id);
Expand Down Expand Up @@ -55,6 +57,8 @@ public TenantId GetDefaultTenantId()

public abstract void SaveTenant(Tenant tenant);

public event EventHandler<TenantId> TenantCreated;

public abstract TenantId[] GetTenantIds();

public abstract Tenant[] GetTenants();
Expand All @@ -70,7 +74,7 @@ public Tenant GetTenant(TenantId tenantId)

return tenant;
}

public abstract Tenant FindTenant(TenantId tenantId);

private TenantId CreateTenant([NotNull] string name, string description, bool isDemo, bool isDefault, CultureInfo defaultCultureInfo, string uriMatchingExpression)
Expand All @@ -84,7 +88,9 @@ private TenantId CreateTenant([NotNull] string name, string description, bool is

Tenant tenant = new Tenant(name, description, isDemo, defaultCultureInfo) { State = TenantState.Created, IsDefault = isDefault, UriMatchingExpression = uriMatchingExpression};
SaveTenant(tenant);
return new TenantId(tenant.Id);
var tenantId = new TenantId(tenant.Id);
TenantCreated?.Invoke(this, tenantId);
return tenantId;
}

protected abstract void Dispose(bool disposing);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@
{
using Logging;

public interface IDataGenerator
{
/// <summary>
/// simple way of ordering the execution of DataGenerators. Priority 0 will be executed first.
/// </summary>
int Priority { get; }

void Generate();
}

/// <summary>
/// Implement this abstract class and mark it either with the <see cref="IDemoDataGenerator"/>
/// or <see cref="IProductiveDataGenerator"/> depending whether you want it to run in all environments
/// or only on development environments.
/// Any implementation is automatically picked up by the injection container, so no extra plumbing is required.
/// You can require any application or domain service including repositories via constructor parameter.
/// </summary>
public abstract class DataGenerator
public abstract class DataGenerator : IDataGenerator
{
private static readonly ILogger Logger = LogManager.Create<DataGenerator>();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
namespace Backend.Fx.Patterns.DataGeneration
{
using System.Collections.Generic;
using System.Linq;
using Logging;
using Patterns.UnitOfWork;

public class DataGeneratorContext
{
private static readonly ILogger Logger = LogManager.Create<DataGeneratorContext>();
private readonly IEnumerable<IDataGenerator> _dataGenerators;
private readonly ICanFlush _canFlush;

public DataGeneratorContext(IEnumerable<IDataGenerator> dataGenerators, ICanFlush canFlush)
{
_dataGenerators = dataGenerators;
_canFlush = canFlush;
}

public void RunProductiveDataGenerators()
{
Logger.Info("Loading productive data into database");
RunDataGenerators<IProductiveDataGenerator>();
}

public void RunDemoDataGenerators()
{
Logger.Info("Loading demonstration data into database");
RunDataGenerators<IDemoDataGenerator>();
}

private void RunDataGenerators<TDataGenerator>()
{
foreach (var dataGenerator in _dataGenerators.Where(dg => dg is TDataGenerator).OrderBy(dg => dg.Priority))
{
dataGenerator.Generate();
_canFlush.Flush();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using Backend.Fx.Patterns.Jobs;
using Backend.Fx.Patterns.UnitOfWork;

namespace Backend.Fx.Patterns.DataGeneration
{
public class DemoDataGenerationJob : IJob
{
private readonly IEnumerable<IDataGenerator> _dataGenerators;
private readonly ICanFlush _canFlush;

public DemoDataGenerationJob(IEnumerable<IDataGenerator> dataGenerators, ICanFlush canFlush)
{
_dataGenerators = dataGenerators;
_canFlush = canFlush;
}

public void Run()
{
var dataGeneratorContext = new DataGeneratorContext(_dataGenerators, _canFlush);
dataGeneratorContext.RunProductiveDataGenerators();
dataGeneratorContext.RunDemoDataGenerators();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
/// <summary>
/// Marks an <see cref="DataGenerator"/> as active in development environments only
/// </summary>
public interface IDemoDataGenerator { }
public interface IDemoDataGenerator : IDataGenerator { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
/// <summary>
/// Marks an <see cref="DataGenerator"/> as active in all environments
/// </summary>
public interface IProductiveDataGenerator { }
public interface IProductiveDataGenerator : IDataGenerator { }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using Backend.Fx.Patterns.Jobs;
using Backend.Fx.Patterns.UnitOfWork;

namespace Backend.Fx.Patterns.DataGeneration
{
public class ProdDataGenerationJob : IJob
{
private readonly IEnumerable<IDataGenerator> _dataGenerators;
private readonly ICanFlush _canFlush;

public ProdDataGenerationJob(IEnumerable<IDataGenerator> dataGenerators, ICanFlush canFlush)
{
_dataGenerators = dataGenerators;
_canFlush = canFlush;
}

public void Run()
{
var dataGeneratorContext = new DataGeneratorContext(_dataGenerators, _canFlush);
dataGeneratorContext.RunProductiveDataGenerators();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Backend.Fx.Environment.Authentication;
using Backend.Fx.Environment.MultiTenancy;
using Backend.Fx.Exceptions;
using Backend.Fx.Logging;
using Backend.Fx.Patterns.DataGeneration;
using Backend.Fx.Patterns.Jobs;
using Backend.Fx.Patterns.UnitOfWork;

namespace Backend.Fx.Patterns.DependencyInjection
{
Expand All @@ -22,12 +24,13 @@ public abstract class BackendFxApplication : IBackendFxApplication
/// <param name="compositionRoot">The composition root of the dependency injection framework</param>
/// <param name="scopeManager">The scope manager for the current application</param>
/// <param name="tenantManager">The tenant manager for the current appliastion</param>
protected BackendFxApplication(ICompositionRoot compositionRoot, IScopeManager scopeManager, ITenantManager tenantManager)
protected BackendFxApplication(ICompositionRoot compositionRoot, IScopeManager scopeManager,
ITenantManager tenantManager)
{
CompositionRoot = compositionRoot;
ScopeManager = scopeManager;
TenantManager = tenantManager;
JobEngine = new JobEngine(scopeManager);
TenantManager = tenantManager;
}

/// <inheritdoc />
Expand Down Expand Up @@ -56,41 +59,74 @@ public bool WaitForBoot(int timeoutMilliSeconds = int.MaxValue)
public async Task Boot()
{
Logger.Info("Booting application");
CompositionRoot.Verify();
await OnBoot();
CompositionRoot.Verify();
SeedTenants();

TenantManager.TenantCreated += async (sender, tenantId) =>
{
try
{
var tenant = TenantManager.GetTenant(tenantId);
tenant.State = TenantState.Seeding;
TenantManager.SaveTenant(tenant);
if (tenant.IsDemoTenant)
{
await JobEngine.ExecuteJobAsync<DemoDataGenerationJob>(tenantId);
tenant.State = TenantState.Active;
TenantManager.SaveTenant(tenant);
}
else
{
await JobEngine.ExecuteJobAsync<ProdDataGenerationJob>(tenantId);
tenant.State = TenantState.Active;
TenantManager.SaveTenant(tenant);
}
}
catch (Exception ex)
{
Logger.Error(ex, "Handling TenantCreated event failed");
}
};

await OnBooted();
_isBooted.Set();
}

private void SeedTenants()
{
foreach (var tenant in TenantManager.GetTenants())
Logger.Info("Beginning startup seeding");
foreach (var tenant in TenantManager.GetTenantIds())
{
Logger.Debug($"Startup seeding of tenant[{tenant.Value}]");
SeedTenant(tenant);
}
}

private void SeedTenant(Tenant tenant)
private void SeedTenant(TenantId tenantId)
{
var tenant = TenantManager.GetTenant(tenantId);

switch (tenant.State)
{
case TenantState.Inactive:
throw new UnprocessableException($"Cannot seed inactive Tenant[{tenant.Id}]");
Logger.Info($"Skipping seeding for inactive Tenant[{tenant.Id}]");
return;

case TenantState.Created:
case TenantState.Active:
case TenantState.Created:
tenant.State = TenantState.Seeding;
tenant.State = TenantState.Seeding;
TenantManager.SaveTenant(tenant);

{
Logger.Info($"Seeding {(tenant.IsDemoTenant ? "demonstration" : "production")} tenant[{tenant.Id}] ({tenant.Name})");
var tenantDataGenerator = new TenantDataGenerator(ScopeManager);
tenantDataGenerator.RunProductiveDataGenerators(tenant);
if (tenant.IsDemoTenant)
using (var scope = ScopeManager.BeginScope(new SystemIdentity(), tenantId))
{
tenantDataGenerator.RunDemoDataGenerators(tenant);
var dataGeneratorContext = new DataGeneratorContext(scope.GetAllInstances<IDataGenerator>(), scope.GetInstance<ICanFlush>());
dataGeneratorContext.RunProductiveDataGenerators();
if (tenant.IsDemoTenant)
{
dataGeneratorContext.RunDemoDataGenerators();
}
}
}

tenant.State = TenantState.Active;
return;
Expand All @@ -99,12 +135,21 @@ private void SeedTenant(Tenant tenant)
return;
}
}

/// <summary>
/// Extension point to do additional initialization before composition root is initialized
/// </summary>
/// <returns></returns>
protected virtual async Task OnBoot()
{
await Task.CompletedTask;
}

/// <summary>
/// Extension point to do additional initialization after composition root is initialized
/// </summary>
/// <returns></returns>
protected virtual async Task OnBoot()
protected virtual async Task OnBooted()
{
await Task.CompletedTask;
}
Expand Down

0 comments on commit d87eda4

Please sign in to comment.