Browse Source

App and schema grain.

pull/249/head
Sebastian Stehle 8 years ago
parent
commit
5e8009f458
  1. 46
      src/Squidex.Domain.Apps.Entities/AppProvider.cs
  2. 200
      src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs
  3. 228
      src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs
  4. 320
      src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs
  5. 2
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs
  6. 2
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs
  7. 2
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs
  8. 6
      src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs
  9. 18
      src/Squidex.Domain.Apps.Entities/Apps/IAppGrain.cs
  10. 3
      src/Squidex.Domain.Apps.Entities/Rules/IRuleGrain.cs
  11. 16
      src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs
  12. 3
      src/Squidex.Domain.Apps.Entities/Schemas/ISchemaGrain.cs
  13. 36
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs
  14. 9
      src/Squidex/Config/Domain/WriteServices.cs
  15. 2
      src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs
  16. 21
      tools/Migrate_01/Migrations/AddPatterns.cs
  17. 17
      tools/Migrate_01/Rebuilder.cs

46
src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Repositories;
using Squidex.Domain.Apps.Entities.Rules;
@ -16,52 +17,51 @@ using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities
{
public sealed class AppProvider : IAppProvider
{
private readonly IGrainFactory grainFactory;
private readonly IAppRepository appRepository;
private readonly IRuleRepository ruleRepository;
private readonly ISchemaRepository schemaRepository;
private readonly IStateFactory stateFactory;
public AppProvider(
IGrainFactory grainFactory,
IAppRepository appRepository,
ISchemaRepository schemaRepository,
IStateFactory stateFactory,
IRuleRepository ruleRepository)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
Guard.NotNull(appRepository, nameof(appRepository));
Guard.NotNull(schemaRepository, nameof(schemaRepository));
Guard.NotNull(stateFactory, nameof(stateFactory));
Guard.NotNull(ruleRepository, nameof(ruleRepository));
this.grainFactory = grainFactory;
this.appRepository = appRepository;
this.schemaRepository = schemaRepository;
this.stateFactory = stateFactory;
this.ruleRepository = ruleRepository;
}
public async Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid appId, Guid id)
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appId);
var app = await grainFactory.GetGrain<IAppGrain>(appId).GetStateAsync();
if (!IsFound(app))
if (!IsFound(app.Value))
{
return (null, null);
}
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
var schema = await grainFactory.GetGrain<ISchemaGrain>(id).GetStateAsync();
if (!IsFound(schema) || schema.Snapshot.IsDeleted)
if (!IsFound(schema.Value) || schema.Value.IsDeleted)
{
return (null, null);
}
return (app.Snapshot, schema.Snapshot);
return (app.Value, schema.Value);
}
public async Task<IAppEntity> GetAppAsync(string appName)
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
return (await stateFactory.GetSingleAsync<AppDomainObject>(appId)).Snapshot;
return (await grainFactory.GetGrain<IAppGrain>(appId).GetStateAsync()).Value;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name)
@ -85,19 +85,19 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
return (await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaId)).Snapshot;
return (await grainFactory.GetGrain<ISchemaGrain>(schemaId).GetStateAsync()).Value;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool allowDeleted = false)
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
var schema = await grainFactory.GetGrain<ISchemaGrain>(id).GetStateAsync();
if (!IsFound(schema) || (schema.Snapshot.IsDeleted && !allowDeleted) || schema.Snapshot.AppId.Id != appId)
if (!IsFound(schema.Value) || (schema.Value.IsDeleted && !allowDeleted) || schema.Value.AppId.Id != appId)
{
return null;
}
return schema.Snapshot;
return schema.Value;
}
public async Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId)
@ -106,9 +106,9 @@ namespace Squidex.Domain.Apps.Entities
var schemas =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<SchemaDomainObject>(id)));
ids.Select(id => grainFactory.GetGrain<ISchemaGrain>(id).GetStateAsync()));
return schemas.Where(IsFound).Select(s => (ISchemaEntity)s.Snapshot).ToList();
return schemas.Where(s => IsFound(s.Value)).Select(s => (ISchemaEntity)s.Value).ToList();
}
public async Task<List<IRuleEntity>> GetRulesAsync(Guid appId)
@ -117,9 +117,9 @@ namespace Squidex.Domain.Apps.Entities
var rules =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<RuleDomainObject>(id)));
ids.Select(id => grainFactory.GetGrain<IRuleGrain>(id).GetStateAsync()));
return rules.Where(IsFound).Select(r => (IRuleEntity)r.Snapshot).ToList();
return rules.Where(r => IsFound(r.Value)).Select(r => (IRuleEntity)r.Value).ToList();
}
public async Task<List<IAppEntity>> GetUserApps(string userId)
@ -128,9 +128,9 @@ namespace Squidex.Domain.Apps.Entities
var apps =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<AppDomainObject>(id)));
ids.Select(id => grainFactory.GetGrain<IAppGrain>(id).GetStateAsync()));
return apps.Where(IsFound).Select(a => (IAppEntity)a.Snapshot).ToList();
return apps.Where(a => IsFound(a.Value)).Select(a => (IAppEntity)a.Value).ToList();
}
private Task<Guid> GetAppIdAsync(string name)
@ -143,9 +143,9 @@ namespace Squidex.Domain.Apps.Entities
return await schemaRepository.FindSchemaIdAsync(appId, name);
}
private static bool IsFound(IDomainObject app)
private static bool IsFound(IEntityWithVersion entity)
{
return app.Version > EtagVersion.Empty;
return entity.Version > EtagVersion.Empty;
}
}
}

200
src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs

@ -1,200 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Guards;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Apps
{
public class AppCommandMiddleware : ICommandMiddleware
{
private readonly IAggregateHandler handler;
private readonly IAppProvider appProvider;
private readonly IAppPlansProvider appPlansProvider;
private readonly IAppPlanBillingManager appPlansBillingManager;
private readonly IUserResolver userResolver;
public AppCommandMiddleware(
IAggregateHandler handler,
IAppProvider appProvider,
IAppPlansProvider appPlansProvider,
IAppPlanBillingManager appPlansBillingManager,
IUserResolver userResolver)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(userResolver, nameof(userResolver));
Guard.NotNull(appPlansProvider, nameof(appPlansProvider));
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager));
this.handler = handler;
this.userResolver = userResolver;
this.appProvider = appProvider;
this.appPlansProvider = appPlansProvider;
this.appPlansBillingManager = appPlansBillingManager;
}
protected async Task On(CreateApp command, CommandContext context)
{
var app = await handler.CreateSyncedAsync<AppDomainObject>(context, async a =>
{
await GuardApp.CanCreate(command, appProvider);
a.Create(command);
context.Complete(EntityCreatedResult.Create(command.AppId, a.Version));
});
}
protected Task On(AssignContributor command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
await GuardAppContributors.CanAssign(a.Snapshot.Contributors, command, userResolver, appPlansProvider.GetPlan(a.Snapshot.Plan?.PlanId));
a.AssignContributor(command);
});
}
protected Task On(RemoveContributor command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppContributors.CanRemove(a.Snapshot.Contributors, command);
a.RemoveContributor(command);
});
}
protected Task On(AttachClient command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanAttach(a.Snapshot.Clients, command);
a.AttachClient(command);
});
}
protected Task On(UpdateClient command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanUpdate(a.Snapshot.Clients, command);
a.UpdateClient(command);
});
}
protected Task On(RevokeClient command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanRevoke(a.Snapshot.Clients, command);
a.RevokeClient(command);
});
}
protected Task On(AddLanguage command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanAdd(a.Snapshot.LanguagesConfig, command);
a.AddLanguage(command);
});
}
protected Task On(RemoveLanguage command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanRemove(a.Snapshot.LanguagesConfig, command);
a.RemoveLanguage(command);
});
}
protected Task On(UpdateLanguage command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanUpdate(a.Snapshot.LanguagesConfig, command);
a.UpdateLanguage(command);
});
}
protected Task On(AddPattern command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanAdd(a.Snapshot.Patterns, command);
a.AddPattern(command);
});
}
protected Task On(DeletePattern command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanDelete(a.Snapshot.Patterns, command);
a.DeletePattern(command);
});
}
protected async Task On(UpdatePattern command, CommandContext context)
{
await handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanUpdate(a.Snapshot.Patterns, command);
a.UpdatePattern(command);
});
}
protected Task On(ChangePlan command, CommandContext context)
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
GuardApp.CanChangePlan(command, a.Snapshot.Plan, appPlansProvider);
if (command.FromCallback)
{
a.ChangePlan(command);
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, a.Snapshot.Id, a.Snapshot.Name, command.PlanId);
if (result is PlanChangedResult)
{
a.ChangePlan(command);
}
context.Complete(result);
}
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
await this.DispatchActionAsync(context.Command, context);
await next();
}
}
}

228
src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs

@ -1,228 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Apps
{
public sealed class AppDomainObject : SquidexDomainObjectBase<AppState>
{
private readonly InitialPatterns initialPatterns;
public AppDomainObject(InitialPatterns initialPatterns)
{
Guard.NotNull(initialPatterns, nameof(initialPatterns));
this.initialPatterns = initialPatterns;
}
public AppDomainObject Create(CreateApp command)
{
ThrowIfCreated();
var appId = new NamedId<Guid>(command.AppId, command.Name);
var events = new List<AppEvent>
{
CreateInitalEvent(command.Name),
CreateInitialOwner(command.Actor),
CreateInitialLanguage()
};
foreach (var pattern in initialPatterns)
{
events.Add(CreateInitialPattern(pattern.Key, pattern.Value));
}
foreach (var @event in events)
{
@event.Actor = command.Actor;
@event.AppId = appId;
RaiseEvent(@event);
}
return this;
}
public AppDomainObject UpdateLanguage(UpdateLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
return this;
}
public AppDomainObject UpdateClient(UpdateClient command)
{
ThrowIfNotCreated();
if (!string.IsNullOrWhiteSpace(command.Name))
{
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed()));
}
if (command.Permission.HasValue)
{
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value }));
}
return this;
}
public AppDomainObject AssignContributor(AssignContributor command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned()));
return this;
}
public AppDomainObject RemoveContributor(RemoveContributor command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
return this;
}
public AppDomainObject AttachClient(AttachClient command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached()));
return this;
}
public AppDomainObject RevokeClient(RevokeClient command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
return this;
}
public AppDomainObject AddLanguage(AddLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded()));
return this;
}
public AppDomainObject RemoveLanguage(RemoveLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved()));
return this;
}
public AppDomainObject ChangePlan(ChangePlan command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged()));
return this;
}
public AppDomainObject AddPattern(AddPattern command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppPatternAdded()));
return this;
}
public AppDomainObject DeletePattern(DeletePattern command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppPatternDeleted()));
return this;
}
public AppDomainObject UpdatePattern(UpdatePattern command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppPatternUpdated()));
return this;
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name);
}
RaiseEvent(Envelope.Create(@event));
}
private static AppCreated CreateInitalEvent(string name)
{
return new AppCreated { Name = name };
}
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern)
{
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message };
}
private static AppLanguageAdded CreateInitialLanguage()
{
return new AppLanguageAdded { Language = Language.EN };
}
private static AppContributorAssigned CreateInitialOwner(RefToken actor)
{
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner };
}
private void ThrowIfNotCreated()
{
if (string.IsNullOrWhiteSpace(Snapshot.Name))
{
throw new DomainException("App has not been created.");
}
}
private void ThrowIfCreated()
{
if (!string.IsNullOrWhiteSpace(Snapshot.Name))
{
throw new DomainException("App has already been created.");
}
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

320
src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs

@ -0,0 +1,320 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Guards;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Entities.Apps
{
public sealed class AppGrain : DomainObjectGrain<AppState>, IAppGrain
{
private readonly InitialPatterns initialPatterns;
private readonly IAppProvider appProvider;
private readonly IAppPlansProvider appPlansProvider;
private readonly IAppPlanBillingManager appPlansBillingManager;
private readonly IUserResolver userResolver;
public AppGrain(
IStore<Guid> store,
IAppProvider appProvider,
IAppPlansProvider appPlansProvider,
IAppPlanBillingManager appPlansBillingManager,
IUserResolver userResolver,
InitialPatterns initialPatterns)
: base(store)
{
Guard.NotNull(initialPatterns, nameof(initialPatterns));
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(userResolver, nameof(userResolver));
Guard.NotNull(appPlansProvider, nameof(appPlansProvider));
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager));
this.userResolver = userResolver;
this.appProvider = appProvider;
this.appPlansProvider = appPlansProvider;
this.appPlansBillingManager = appPlansBillingManager;
this.initialPatterns = initialPatterns;
}
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
switch (command)
{
case CreateApp createApp:
return CreateAsync(createApp, async c =>
{
await GuardApp.CanCreate(c, appProvider);
Create(c);
});
case AssignContributor assigneContributor:
return UpdateAsync(assigneContributor, async c =>
{
await GuardAppContributors.CanAssign(Snapshot.Contributors, c, userResolver, appPlansProvider.GetPlan(Snapshot.Plan?.PlanId));
AssignContributor(c);
});
case RemoveContributor removeContributor:
return UpdateAsync(removeContributor, c =>
{
GuardAppContributors.CanRemove(Snapshot.Contributors, c);
RemoveContributor(c);
});
case AttachClient attachClient:
return UpdateAsync(attachClient, c =>
{
GuardAppClients.CanAttach(Snapshot.Clients, c);
AttachClient(c);
});
case UpdateClient updateClient:
return UpdateAsync(updateClient, c =>
{
GuardAppClients.CanUpdate(Snapshot.Clients, c);
UpdateClient(c);
});
case RevokeClient revokeClient:
return UpdateAsync(revokeClient, c =>
{
GuardAppClients.CanRevoke(Snapshot.Clients, c);
RevokeClient(c);
});
case AddLanguage addLanguage:
return UpdateAsync(addLanguage, c =>
{
GuardAppLanguages.CanAdd(Snapshot.LanguagesConfig, c);
AddLanguage(c);
});
case RemoveLanguage removeLanguage:
return UpdateAsync(removeLanguage, c =>
{
GuardAppLanguages.CanRemove(Snapshot.LanguagesConfig, c);
RemoveLanguage(c);
});
case UpdateLanguage updateLanguage:
return UpdateAsync(updateLanguage, c =>
{
GuardAppLanguages.CanUpdate(Snapshot.LanguagesConfig, c);
UpdateLanguage(c);
});
case AddPattern addPattern:
return UpdateAsync(addPattern, c =>
{
GuardAppPattern.CanAdd(Snapshot.Patterns, c);
AddPattern(c);
});
case DeletePattern deletePattern:
return UpdateAsync(deletePattern, c =>
{
GuardAppPattern.CanDelete(Snapshot.Patterns, c);
DeletePattern(c);
});
case UpdatePattern updatePattern:
return UpdateAsync(updatePattern, c =>
{
GuardAppPattern.CanUpdate(Snapshot.Patterns, c);
UpdatePattern(c);
});
case ChangePlan changePlan:
return UpdateReturnAsync(changePlan, async c =>
{
GuardApp.CanChangePlan(c, Snapshot.Plan, appPlansProvider);
if (c.FromCallback)
{
ChangePlan(c);
return null;
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.Id, Snapshot.Name, c.PlanId);
if (result is PlanChangedResult)
{
ChangePlan(c);
}
return result;
}
});
default:
throw new NotSupportedException();
}
}
public void Create(CreateApp command)
{
var appId = new NamedId<Guid>(command.AppId, command.Name);
var events = new List<AppEvent>
{
CreateInitalEvent(command.Name),
CreateInitialOwner(command.Actor),
CreateInitialLanguage()
};
foreach (var pattern in initialPatterns)
{
events.Add(CreateInitialPattern(pattern.Key, pattern.Value));
}
foreach (var @event in events)
{
@event.Actor = command.Actor;
@event.AppId = appId;
RaiseEvent(@event);
}
}
public void UpdateClient(UpdateClient command)
{
if (!string.IsNullOrWhiteSpace(command.Name))
{
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed()));
}
if (command.Permission.HasValue)
{
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value }));
}
}
public void UpdateLanguage(UpdateLanguage command)
{
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
}
public void AssignContributor(AssignContributor command)
{
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned()));
}
public void RemoveContributor(RemoveContributor command)
{
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
}
public void AttachClient(AttachClient command)
{
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached()));
}
public void RevokeClient(RevokeClient command)
{
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
}
public void AddLanguage(AddLanguage command)
{
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded()));
}
public void RemoveLanguage(RemoveLanguage command)
{
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved()));
}
public void ChangePlan(ChangePlan command)
{
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged()));
}
public void AddPattern(AddPattern command)
{
RaiseEvent(SimpleMapper.Map(command, new AppPatternAdded()));
}
public void DeletePattern(DeletePattern command)
{
RaiseEvent(SimpleMapper.Map(command, new AppPatternDeleted()));
}
public void UpdatePattern(UpdatePattern command)
{
RaiseEvent(SimpleMapper.Map(command, new AppPatternUpdated()));
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name);
}
RaiseEvent(Envelope.Create(@event));
}
private static AppCreated CreateInitalEvent(string name)
{
return new AppCreated { Name = name };
}
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern)
{
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message };
}
private static AppLanguageAdded CreateInitialLanguage()
{
return new AppLanguageAdded { Language = Language.EN };
}
private static AppContributorAssigned CreateInitialOwner(RefToken actor)
{
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner };
}
public override void ApplyEvent(Envelope<IEvent> @event)
{
ApplySnapshot(Snapshot.Apply(@event));
}
public Task<J<IAppEntity>> GetStateAsync()
{
return Task.FromResult(new J<IAppEntity>(Snapshot));
}
}
}

2
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs

@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (!clients.TryGetValue(id, out var client))
{
throw new DomainObjectNotFoundException(id, "Clients", typeof(AppDomainObject));
throw new DomainObjectNotFoundException(id, "Clients", typeof(AppGrain));
}
return client;

2
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs

@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (!contributors.ContainsKey(command.ContributorId))
{
throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(AppDomainObject));
throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(AppGrain));
}
}
}

2
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs

@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
if (!languages.TryGetConfig(language, out var languageConfig))
{
throw new DomainObjectNotFoundException(language, "Languages", typeof(AppDomainObject));
throw new DomainObjectNotFoundException(language, "Languages", typeof(AppGrain));
}
return languageConfig;

6
src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs

@ -9,7 +9,11 @@ using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Entities.Apps
{
public interface IAppEntity : IEntity, IEntityWithVersion
public interface IAppEntity :
IEntity,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{
string Name { get; }

18
src/Squidex.Domain.Apps.Entities/Apps/IAppGrain.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Apps
{
public interface IAppGrain : IDomainObjectGrain
{
Task<J<IAppEntity>> GetStateAsync();
}
}

3
src/Squidex.Domain.Apps.Entities/Rules/IRuleGrain.cs

@ -5,11 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Rules
{
public interface IRuleGrain : IDomainObjectGrain
{
Task<J<IRuleEntity>> GetStateAsync();
}
}

16
src/Squidex.Domain.Apps.Entities/Rules/RuleGrain.cs

@ -15,6 +15,7 @@ using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
@ -34,6 +35,8 @@ namespace Squidex.Domain.Apps.Entities.Rules
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
VerifyNotDeleted();
switch (command)
{
case CreateRule createRule:
@ -83,29 +86,21 @@ namespace Squidex.Domain.Apps.Entities.Rules
public void Update(UpdateRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated()));
}
public void Enable(EnableRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled()));
}
public void Disable(DisableRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled()));
}
public void Delete(DeleteRule command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted()));
}
@ -131,5 +126,10 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
ApplySnapshot(Snapshot.Apply(@event));
}
public Task<J<IRuleEntity>> GetStateAsync()
{
return Task.FromResult(new J<IRuleEntity>(Snapshot));
}
}
}

3
src/Squidex.Domain.Apps.Entities/Schemas/ISchemaGrain.cs

@ -5,11 +5,14 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Schemas
{
public interface ISchemaGrain : IDomainObjectGrain
{
Task<J<ISchemaEntity>> GetStateAsync();
}
}

36
src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
@ -41,6 +42,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas
public override Task<object> ExecuteAsync(IAggregateCommand command)
{
VerifyNotDeleted();
switch (command)
{
case CreateSchema createSchema:
@ -191,99 +194,71 @@ namespace Squidex.Domain.Apps.Entities.Schemas
public void Add(AddField command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) }));
}
public void UpdateField(UpdateField command)
{
VerifyNotDeleted();
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
}
public void LockField(LockField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldLocked());
}
public void HideField(HideField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldHidden());
}
public void ShowField(ShowField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldShown());
}
public void DisableField(DisableField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldDisabled());
}
public void EnableField(EnableField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldEnabled());
}
public void DeleteField(DeleteField command)
{
VerifyNotDeleted();
RaiseEvent(command, new FieldDeleted());
}
public void Reorder(ReorderFields command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
}
public void Publish(PublishSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
}
public void Unpublish(UnpublishSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
}
public void ConfigureScripts(ConfigureScripts command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
}
public void Delete(DeleteSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted()));
}
public void Update(UpdateSchema command)
{
VerifyNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));
}
@ -326,5 +301,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
ApplySnapshot(Snapshot.Apply(@event, registry));
}
public Task<J<ISchemaEntity>> GetStateAsync()
{
return Task.FromResult(new J<ISchemaEntity>(Snapshot));
}
}
}

9
src/Squidex/Config/Domain/WriteServices.cs

@ -14,6 +14,7 @@ using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Templates;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents;
@ -52,15 +53,15 @@ namespace Squidex.Config.Domain
services.AddSingletonAs<EnrichWithSchemaIdCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<AppCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<AssetCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<ContentCommandMiddleware>()
.As<ICommandMiddleware>();
services.AddSingletonAs<GrainCommandMiddleware<AppCommand, IAppGrain>>()
.As<ICommandMiddleware>();
services.AddSingletonAs<GrainCommandMiddleware<SchemaCommand, ISchemaGrain>>()
.As<ICommandMiddleware>();
@ -91,7 +92,7 @@ namespace Squidex.Config.Domain
services.AddTransientAs<Rebuilder>()
.AsSelf();
services.AddTransientAs<AppDomainObject>()
services.AddTransientAs<AppGrain>()
.AsSelf();
services.AddTransientAs<ContentDomainObject>()

2
src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs

@ -77,7 +77,7 @@ namespace Squidex.Pipeline.CommandMiddlewares
if (schema == null)
{
throw new DomainObjectNotFoundException(schemaName, typeof(SchemaDomainObject));
throw new DomainObjectNotFoundException(schemaName, typeof(ISchemaEntity));
}
schemaCommand.SchemaId = new NamedId<Guid>(schema.Id, schema.Name);

21
tools/Migrate_01/Migrations/AddPatterns.cs

@ -7,6 +7,7 @@
using System;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Repositories;
@ -18,14 +19,14 @@ namespace Migrate_01.Migrations
public sealed class AddPatterns : IMigration
{
private readonly InitialPatterns initialPatterns;
private readonly IStateFactory stateFactory;
private readonly IGrainFactory grainFactory;
private readonly IAppRepository appRepository;
public AddPatterns(InitialPatterns initialPatterns, IAppRepository appRepository, IStateFactory stateFactory)
public AddPatterns(InitialPatterns initialPatterns, IAppRepository appRepository, IGrainFactory grainFactory)
{
this.initialPatterns = initialPatterns;
this.appRepository = appRepository;
this.stateFactory = stateFactory;
this.grainFactory = grainFactory;
}
public async Task UpdateAsync()
@ -34,27 +35,29 @@ namespace Migrate_01.Migrations
foreach (var id in ids)
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(id);
var app = grainFactory.GetGrain<IAppGrain>(id);
if (app.Snapshot.Patterns.Count == 0)
var state = await app.GetStateAsync();
if (state.Value.Patterns.Count == 0)
{
foreach (var pattern in initialPatterns.Values)
{
var command =
new AddPattern
{
Actor = app.Snapshot.CreatedBy,
AppId = app.Snapshot.Id,
Actor = state.Value.CreatedBy,
AppId = state.Value.Id,
Name = pattern.Name,
PatternId = Guid.NewGuid(),
Pattern = pattern.Pattern,
Message = pattern.Message
};
app.AddPattern(command);
await app.ExecuteAsync(command);
}
await app.WriteAsync();
await app.WriteSnapshotAsync();
}
}
}

17
tools/Migrate_01/Rebuilder.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.State;
@ -39,7 +40,7 @@ namespace Migrate_01
private readonly ISnapshotStore<ContentState, Guid> snapshotContentStore;
private readonly ISnapshotStore<RuleState, Guid> snapshotRuleStore;
private readonly ISnapshotStore<SchemaState, Guid> snapshotSchemaStore;
private readonly IStateFactory stateFactory;
private readonly IGrainFactory grainFactory;
public Rebuilder(
FieldRegistry fieldRegistry,
@ -50,7 +51,7 @@ namespace Migrate_01
ISnapshotStore<AssetState, Guid> snapshotAssetStore,
ISnapshotStore<RuleState, Guid> snapshotRuleStore,
ISnapshotStore<SchemaState, Guid> snapshotSchemaStore,
IStateFactory stateFactory)
IGrainFactory grainFactory)
{
this.fieldRegistry = fieldRegistry;
this.eventDataFormatter = eventDataFormatter;
@ -60,7 +61,7 @@ namespace Migrate_01
this.snapshotContentStore = snapshotContentStore;
this.snapshotRuleStore = snapshotRuleStore;
this.snapshotSchemaStore = snapshotSchemaStore;
this.stateFactory = stateFactory;
this.grainFactory = grainFactory;
}
public async Task RebuildAssetsAsync()
@ -79,9 +80,7 @@ namespace Migrate_01
{
if (@event.Payload is AssetEvent assetEvent && handledIds.Add(assetEvent.AssetId))
{
var asset = await stateFactory.CreateAsync<AssetDomainObject>(assetEvent.AssetId);
asset.ApplySnapshot(asset.Snapshot.Apply(@event));
var asset = grainFactory.GetGrain<IAssetGrain>(assetEvent.AssetId);
await asset.WriteSnapshotAsync();
}
@ -107,19 +106,19 @@ namespace Migrate_01
{
if (@event.Payload is SchemaEvent schemaEvent && handledIds.Add(schemaEvent.SchemaId.Id))
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaEvent.SchemaId.Id);
var schema = grainFactory.GetGrain<ISchemaGrain>(schemaEvent.SchemaId.Id);
await schema.WriteSnapshotAsync();
}
else if (@event.Payload is RuleEvent ruleEvent && handledIds.Add(ruleEvent.RuleId))
{
var rule = await stateFactory.GetSingleAsync<RuleDomainObject>(ruleEvent.RuleId);
var rule = grainFactory.GetGrain<IRuleGrain>(ruleEvent.RuleId);
await rule.WriteSnapshotAsync();
}
else if (@event.Payload is AppEvent appEvent && handledIds.Add(appEvent.AppId.Id))
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appEvent.AppId.Id);
var app = grainFactory.GetGrain<IAppGrain>(appEvent.AppId.Id);
await app.WriteSnapshotAsync();
}

Loading…
Cancel
Save