Browse Source

Immutable for rules.

pull/169/head
Sebastian Stehle 8 years ago
parent
commit
dbe6f11849
  1. 31
      src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs
  2. 30
      src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs
  3. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
  4. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
  5. 18
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
  6. 16
      src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs
  7. 74
      src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
  8. 2
      src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
  9. 2
      src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
  10. 8
      src/Squidex.Domain.Apps.Read/EntityMapper.cs
  11. 17
      src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs
  12. 2
      src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs
  13. 2
      src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs
  14. 6
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs
  15. 17
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs
  16. 17
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs
  17. 10
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs
  18. 4
      src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
  19. 2
      src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs
  20. 2
      src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
  21. 3
      src/Squidex.Domain.Apps.Read/State/Orleans/AppStateEventConsumer.cs
  22. 16
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/IAppStateGrain.cs
  23. 30
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrain.cs
  24. 34
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrainState.cs
  25. 4
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonAppEntity.cs
  26. 10
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonEntity.cs
  27. 11
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonRuleEntity.cs
  28. 9
      src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonSchemaEntity.cs
  29. 57
      src/Squidex.Infrastructure/Json/Orleans/J.cs
  30. 21
      src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs

31
src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs

@ -14,9 +14,36 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
[TypeName(nameof(WebhookAction))] [TypeName(nameof(WebhookAction))]
public sealed class WebhookAction : RuleAction public sealed class WebhookAction : RuleAction
{ {
public Uri Url { get; set; } private Uri url;
private string sharedSecret;
public string SharedSecret { get; set; } public Uri Url
{
get
{
return url;
}
set
{
ThrowIfFrozen();
url = value;
}
}
public string SharedSecret
{
get
{
return sharedSecret;
}
set
{
ThrowIfFrozen();
sharedSecret = value;
}
}
public override T Accept<T>(IRuleActionVisitor<T> visitor) public override T Accept<T>(IRuleActionVisitor<T> visitor)
{ {

30
src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs

@ -11,7 +11,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules namespace Squidex.Domain.Apps.Core.Rules
{ {
public sealed class Rule public sealed class Rule : Cloneable<Rule>
{ {
private RuleTrigger trigger; private RuleTrigger trigger;
private RuleAction action; private RuleAction action;
@ -41,17 +41,23 @@ namespace Squidex.Domain.Apps.Core.Rules
this.action = action; this.action = action;
} }
public void Enable() public Rule Enable()
{ {
this.isEnabled = true; return Clone(clone =>
{
clone.isEnabled = true;
});
} }
public void Disable() public Rule Disable()
{ {
this.isEnabled = false; return Clone(clone =>
{
clone.isEnabled = false;
});
} }
public void Update(RuleTrigger newTrigger) public Rule Update(RuleTrigger newTrigger)
{ {
Guard.NotNull(newTrigger, nameof(newTrigger)); Guard.NotNull(newTrigger, nameof(newTrigger));
@ -60,10 +66,13 @@ namespace Squidex.Domain.Apps.Core.Rules
throw new ArgumentException("New trigger has another type.", nameof(newTrigger)); throw new ArgumentException("New trigger has another type.", nameof(newTrigger));
} }
trigger = newTrigger; return Clone(clone =>
{
clone.trigger = newTrigger;
});
} }
public void Update(RuleAction newAction) public Rule Update(RuleAction newAction)
{ {
Guard.NotNull(newAction, nameof(newAction)); Guard.NotNull(newAction, nameof(newAction));
@ -72,7 +81,10 @@ namespace Squidex.Domain.Apps.Core.Rules
throw new ArgumentException("New action has another type.", nameof(newAction)); throw new ArgumentException("New action has another type.", nameof(newAction));
} }
action = newAction; return Clone(clone =>
{
clone.action = newAction;
});
} }
} }
} }

4
src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs

@ -6,9 +6,11 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules namespace Squidex.Domain.Apps.Core.Rules
{ {
public abstract class RuleAction public abstract class RuleAction : Freezable
{ {
public abstract T Accept<T>(IRuleActionVisitor<T> visitor); public abstract T Accept<T>(IRuleActionVisitor<T> visitor);
} }

4
src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs

@ -6,9 +6,11 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules namespace Squidex.Domain.Apps.Core.Rules
{ {
public abstract class RuleTrigger public abstract class RuleTrigger : Freezable
{ {
public abstract T Accept<T>(IRuleTriggerVisitor<T> visitor); public abstract T Accept<T>(IRuleTriggerVisitor<T> visitor);
} }

18
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs

@ -6,7 +6,7 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Triggers namespace Squidex.Domain.Apps.Core.Rules.Triggers
@ -14,7 +14,21 @@ namespace Squidex.Domain.Apps.Core.Rules.Triggers
[TypeName(nameof(ContentChangedTrigger))] [TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger public sealed class ContentChangedTrigger : RuleTrigger
{ {
public List<ContentChangedTriggerSchema> Schemas { get; set; } private ImmutableList<ContentChangedTriggerSchema> schemas;
public ImmutableList<ContentChangedTriggerSchema> Schemas
{
get
{
return schemas;
}
set
{
ThrowIfFrozen();
schemas = value;
}
}
public override T Accept<T>(IRuleTriggerVisitor<T> visitor) public override T Accept<T>(IRuleTriggerVisitor<T> visitor)
{ {

16
src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs

@ -17,27 +17,29 @@ namespace Squidex.Domain.Apps.Events.Rules.Utils
return new Rule(@event.Trigger, @event.Action); return new Rule(@event.Trigger, @event.Action);
} }
public static void Apply(this Rule rule, RuleUpdated @event) public static Rule Apply(this Rule rule, RuleUpdated @event)
{ {
if (@event.Trigger != null) if (@event.Trigger != null)
{ {
rule.Update(@event.Trigger); return rule.Update(@event.Trigger);
} }
if (@event.Action != null) if (@event.Action != null)
{ {
rule.Update(@event.Action); return rule.Update(@event.Action);
} }
return rule;
} }
public static void Apply(this Rule rule, RuleEnabled @event) public static Rule Apply(this Rule rule, RuleEnabled @event)
{ {
rule.Enable(); return rule.Enable();
} }
public static void Apply(this Rule rule, RuleDisabled @event) public static Rule Apply(this Rule rule, RuleDisabled @event)
{ {
rule.Disable(); return rule.Disable();
} }
} }
} }

74
src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs

@ -9,7 +9,6 @@
using System; using System;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Events.Schemas.Utils namespace Squidex.Domain.Apps.Events.Schemas.Utils
{ {
@ -61,7 +60,7 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
return schema; return schema;
} }
public static void Apply(this Schema schema, FieldAdded @event, FieldRegistry registry) public static Schema Apply(this Schema schema, FieldAdded @event, FieldRegistry registry)
{ {
var partitioning = var partitioning =
string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ?
@ -71,86 +70,65 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
var fieldId = @event.FieldId.Id; var fieldId = @event.FieldId.Id;
var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties); var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties);
schema.DeleteField(fieldId); schema = schema.DeleteField(fieldId);
schema.AddField(field); schema = schema.AddField(field);
}
public static void Apply(this Schema schema, FieldUpdated @event) return schema;
{
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Update(@event.Properties);
}
} }
public static void Apply(this Schema schema, FieldLocked @event) public static Schema Apply(this Schema schema, FieldUpdated @event)
{ {
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) return schema.UpdateField(@event.FieldId.Id, @event.Properties);
{
field.Lock();
}
} }
public static void Apply(this Schema schema, FieldHidden @event) public static Schema Apply(this Schema schema, FieldLocked @event)
{ {
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) return schema.LockField(@event.FieldId.Id);
{
field.Hide();
}
} }
public static void Apply(this Schema schema, FieldShown @event) public static Schema Apply(this Schema schema, FieldHidden @event)
{ {
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) return schema.HideField(@event.FieldId.Id);
{
field.Show();
}
} }
public static void Apply(this Schema schema, FieldDisabled @event) public static Schema Apply(this Schema schema, FieldShown @event)
{ {
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) return schema.ShowField(@event.FieldId.Id);
{
field.Disable();
}
} }
public static void Apply(this Schema schema, FieldEnabled @event) public static Schema Apply(this Schema schema, FieldDisabled @event)
{ {
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) return schema.DisableField(@event.FieldId.Id);
{
field.Enable();
}
} }
public static void Apply(this Schema schema, SchemaUpdated @event) public static Schema Apply(this Schema schema, FieldEnabled @event)
{ {
schema.Update(@event.Properties); return schema.EnableField(@event.FieldId.Id);
} }
public static void Apply(this Schema schema, SchemaFieldsReordered @event) public static Schema Apply(this Schema schema, SchemaUpdated @event)
{ {
schema.ReorderFields(@event.FieldIds); return schema.Update(@event.Properties);
} }
public static void Apply(this Schema schema, FieldDeleted @event) public static Schema Apply(this Schema schema, SchemaFieldsReordered @event)
{ {
schema.DeleteField(@event.FieldId.Id); return schema.ReorderFields(@event.FieldIds);
} }
public static void Apply(this Schema schema, SchemaPublished @event) public static Schema Apply(this Schema schema, FieldDeleted @event)
{ {
schema.Publish(); return schema.DeleteField(@event.FieldId.Id);
} }
public static void Apply(this Schema schema, SchemaUnpublished @event) public static Schema Apply(this Schema schema, SchemaPublished @event)
{ {
schema.Unpublish(); return schema.Publish();
} }
public static void Apply(this Schema schema, ScriptsConfigured @event) public static Schema Apply(this Schema schema, SchemaUnpublished @event)
{ {
SimpleMapper.Map(@event, schema); return schema.Unpublish();
} }
} }
} }

2
src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs

@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Read.Assets namespace Squidex.Domain.Apps.Read.Assets
{ {
public interface IAssetEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IAssetEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{ {
string MimeType { get; } string MimeType { get; }

2
src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs

@ -11,7 +11,7 @@ using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Read.Contents namespace Squidex.Domain.Apps.Read.Contents
{ {
public interface IContentEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IContentEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{ {
Status Status { get; } Status Status { get; }

8
src/Squidex.Domain.Apps.Read/EntityMapper.cs

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Read
private static void SetVersion(EnvelopeHeaders headers, IEntity entity) private static void SetVersion(EnvelopeHeaders headers, IEntity entity)
{ {
if (entity is IEntityWithVersion withVersion) if (entity is IUpdateableEntityWithVersion withVersion)
{ {
withVersion.Version = headers.EventStreamNumber(); withVersion.Version = headers.EventStreamNumber();
} }
@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Read
private static void SetCreatedBy(SquidexEvent @event, IEntity entity) private static void SetCreatedBy(SquidexEvent @event, IEntity entity)
{ {
if (entity is IEntityWithCreatedBy withCreatedBy) if (entity is IUpdateableEntityWithCreatedBy withCreatedBy)
{ {
withCreatedBy.CreatedBy = @event.Actor; withCreatedBy.CreatedBy = @event.Actor;
} }
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Read
private static void SetLastModifiedBy(SquidexEvent @event, IEntity entity) private static void SetLastModifiedBy(SquidexEvent @event, IEntity entity)
{ {
if (entity is IEntityWithLastModifiedBy withModifiedBy) if (entity is IUpdateableEntityWithLastModifiedBy withModifiedBy)
{ {
withModifiedBy.LastModifiedBy = @event.Actor; withModifiedBy.LastModifiedBy = @event.Actor;
} }
@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Read
private static void SetAppId(SquidexEvent @event, IEntity entity) private static void SetAppId(SquidexEvent @event, IEntity entity)
{ {
if (entity is IAppRefEntity app && @event is AppEvent appEvent) if (entity is IUpdateableEntityWithAppRef app && @event is AppEvent appEvent)
{ {
app.AppId = appEvent.AppId.Id; app.AppId = appEvent.AppId.Id;
} }

17
src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs

@ -0,0 +1,17 @@
// ==========================================================================
// IEntityWithAppRef.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithAppRef : IEntity
{
Guid AppId { get; }
}
}

2
src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs

@ -12,6 +12,6 @@ namespace Squidex.Domain.Apps.Read
{ {
public interface IEntityWithCreatedBy public interface IEntityWithCreatedBy
{ {
RefToken CreatedBy { get; set; } RefToken CreatedBy { get; }
} }
} }

2
src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs

@ -10,6 +10,6 @@ namespace Squidex.Domain.Apps.Read
{ {
public interface IEntityWithVersion public interface IEntityWithVersion
{ {
long Version { get; set; } long Version { get; }
} }
} }

6
src/Squidex.Domain.Apps.Read/IAppRefEntity.cs → src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// IAppRefEntity.cs // IUpdateableEntityWithAppRef.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -10,8 +10,8 @@ using System;
namespace Squidex.Domain.Apps.Read namespace Squidex.Domain.Apps.Read
{ {
public interface IAppRefEntity : IEntity public interface IUpdateableEntityWithAppRef
{ {
Guid AppId { get; set; } Guid AppId { get; set; }
} }
} }

17
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs

@ -0,0 +1,17 @@
// ==========================================================================
// IUpdateableEntityWithCreatedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithCreatedBy
{
RefToken CreatedBy { get; set; }
}
}

17
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs

@ -0,0 +1,17 @@
// ==========================================================================
// IUpdateableEntityWithLastModifiedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithLastModifiedBy
{
RefToken LastModifiedBy { get; set; }
}
}

10
src/Squidex.Infrastructure/Json/Orleans/IJsonValue.cs → src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs

@ -1,17 +1,15 @@
// ========================================================================== // ==========================================================================
// IJsonValue.cs // IUpdateableEntityWithVersion.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
namespace Squidex.Infrastructure.Json.Orleans namespace Squidex.Domain.Apps.Read
{ {
public interface IJsonValue public interface IUpdateableEntityWithVersion
{ {
object Value { get; } long Version { get; set; }
bool IsImmutable { get; }
} }
} }

4
src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs

@ -10,8 +10,8 @@ using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules namespace Squidex.Domain.Apps.Read.Rules
{ {
public interface IRuleEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface IRuleEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{ {
Rule Rule { get; } Rule RuleDef { get; }
} }
} }

2
src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs

@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Read.Rules
foreach (var ruleEntity in rules) foreach (var ruleEntity in rules)
{ {
var job = ruleService.CreateJob(ruleEntity.Rule, @event); var job = ruleService.CreateJob(ruleEntity.RuleDef, @event);
if (job != null) if (job != null)
{ {

2
src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs

@ -10,7 +10,7 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.Schemas namespace Squidex.Domain.Apps.Read.Schemas
{ {
public interface ISchemaEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion public interface ISchemaEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{ {
string Name { get; } string Name { get; }

3
src/Squidex.Domain.Apps.Read/State/Orleans/AppStateEventConsumer.cs

@ -8,6 +8,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans; using Orleans;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.State.Orleans.Grains; using Squidex.Domain.Apps.Read.State.Orleans.Grains;
@ -49,7 +50,7 @@ namespace Squidex.Domain.Apps.Read.State.Orleans
{ {
var appGrain = factory.GetGrain<IAppStateGrain>(appEvent.AppId.Name); var appGrain = factory.GetGrain<IAppStateGrain>(appEvent.AppId.Name);
await appGrain.HandleAsync(@event); await appGrain.HandleAsync(new Immutable<Envelope<IEvent>>(@event));
} }
if (@event.Payload is AppContributorAssigned contributorAssigned) if (@event.Payload is AppContributorAssigned contributorAssigned)

16
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/IAppStateGrain.cs

@ -10,28 +10,28 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans; using Orleans;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules; using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains namespace Squidex.Domain.Apps.Read.State.Orleans.Grains
{ {
public interface IAppStateGrain : IGrainWithStringKey public interface IAppStateGrain : IGrainWithStringKey
{ {
Task<J<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id); Task<Immutable<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id);
Task<J<IAppEntity>> GetAppAsync(); Task<Immutable<IAppEntity>> GetAppAsync();
Task<J<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false); Task<Immutable<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false);
Task<J<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false); Task<Immutable<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false);
Task<J<List<ISchemaEntity>>> GetSchemasAsync(); Task<Immutable<List<ISchemaEntity>>> GetSchemasAsync();
Task<J<List<IRuleEntity>>> GetRulesAsync(); Task<Immutable<List<IRuleEntity>>> GetRulesAsync();
Task HandleAsync(J<Envelope<IEvent>> message); Task HandleAsync(Immutable<Envelope<IEvent>> message);
} }
} }

30
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrain.cs

@ -10,13 +10,13 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Orleans; using Orleans;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules; using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Json.Orleans;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
@ -31,51 +31,51 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
this.fieldRegistry = fieldRegistry; this.fieldRegistry = fieldRegistry;
} }
public Task<J<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id) public Task<Immutable<(IAppEntity, ISchemaEntity)>> GetAppWithSchemaAsync(Guid id)
{ {
var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted); var schema = State.FindSchema(x => x.Id == id && !x.IsDeleted);
return J<(IAppEntity AppEntity, ISchemaEntity SchemaEntity)>.AsTask((State.GetApp(), schema)); return Task.FromResult((State.GetApp(), schema).AsImmutable());
} }
public Task<J<IAppEntity>> GetAppAsync() public Task<Immutable<IAppEntity>> GetAppAsync()
{ {
var value = State.GetApp(); var value = State.GetApp();
return J<IAppEntity>.AsTask(value); return Task.FromResult(value.AsImmutable());
} }
public Task<J<List<IRuleEntity>>> GetRulesAsync() public Task<Immutable<List<IRuleEntity>>> GetRulesAsync()
{ {
var value = State.FindRules(); var value = State.FindRules();
return J<List<IRuleEntity>>.AsTask(value); return Task.FromResult(value.AsImmutable());
} }
public Task<J<List<ISchemaEntity>>> GetSchemasAsync() public Task<Immutable<List<ISchemaEntity>>> GetSchemasAsync()
{ {
var value = State.FindSchemas(x => !x.IsDeleted); var value = State.FindSchemas(x => !x.IsDeleted);
return J<List<ISchemaEntity>>.AsTask(value); return Task.FromResult(value.AsImmutable());
} }
public Task<J<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false) public Task<Immutable<ISchemaEntity>> GetSchemaAsync(Guid id, bool provideDeleted = false)
{ {
var value = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted)); var value = State.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted));
return J<ISchemaEntity>.AsTask(value); return Task.FromResult(value.AsImmutable());
} }
public Task<J<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false) public Task<Immutable<ISchemaEntity>> GetSchemaAsync(string name, bool provideDeleted = false)
{ {
var value = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted)); var value = State.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted));
return J<ISchemaEntity>.AsTask(value); return Task.FromResult(value.AsImmutable());
} }
public Task HandleAsync(J<Envelope<IEvent>> message) public Task HandleAsync(Immutable<Envelope<IEvent>> message)
{ {
State.Apply(message, fieldRegistry); State.Apply(message.Value, fieldRegistry);
return WriteStateAsync(); return WriteStateAsync();
} }

34
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/AppStateGrainState.cs

@ -163,21 +163,21 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
case RuleCreated @event: case RuleCreated @event:
Rules[@event.RuleId] = EntityMapper.Create<JsonRuleEntity>(@event, envelope.Headers, r => Rules[@event.RuleId] = EntityMapper.Create<JsonRuleEntity>(@event, envelope.Headers, r =>
{ {
r.Rule = RuleEventDispatcher.Create(@event); r.RuleDef = RuleEventDispatcher.Create(@event);
}); });
break; break;
case RuleUpdated @event: case RuleUpdated @event:
UpdateRule(envelope, r => UpdateRule(envelope, r =>
{ {
r.Rule.Apply(@event); r.RuleDef = r.RuleDef.Apply(@event);
}); });
break; break;
case RuleEnabled @event: case RuleEnabled @event:
UpdateRule(envelope, r => UpdateRule(envelope, r =>
{ {
r.Rule.Apply(@event); r.RuleDef = r.RuleDef.Apply(@event);
}); });
break; break;
@ -197,84 +197,84 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
case FieldAdded @event: case FieldAdded @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event, registry); s.SchemaDef = s.SchemaDef.Apply(@event, registry);
}); });
break; break;
case FieldDeleted @event: case FieldDeleted @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldLocked @event: case FieldLocked @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldHidden @event: case FieldHidden @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldShown @event: case FieldShown @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldDisabled @event: case FieldDisabled @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldEnabled @event: case FieldEnabled @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case FieldUpdated @event: case FieldUpdated @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case SchemaFieldsReordered @event: case SchemaFieldsReordered @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case SchemaUpdated @event: case SchemaUpdated @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case SchemaPublished @event: case SchemaPublished @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); s.SchemaDef = s.SchemaDef.Apply(@event);
}); });
break; break;
case ScriptsConfigured @event: case ScriptsConfigured @event:
UpdateSchema(envelope, s => UpdateSchema(envelope, s =>
{ {
s.SchemaDef.Apply(@event); SimpleMapper.Map(s, @event);
}); });
break; break;
@ -308,14 +308,14 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
var e = envelope.To<RuleEvent>(); var e = envelope.To<RuleEvent>();
Rules[e.Payload.RuleId].Update(e.Payload, e.Headers, updater); Rules[e.Payload.RuleId].Clone().Update(e.Payload, e.Headers, updater);
} }
private void UpdateSchema(Envelope<IEvent> envelope, Action<JsonSchemaEntity> updater = null) private void UpdateSchema(Envelope<IEvent> envelope, Action<JsonSchemaEntity> updater = null)
{ {
var e = envelope.To<SchemaEvent>(); var e = envelope.To<SchemaEvent>();
Schemas[e.Payload.SchemaId.Id].Copy().Update(e.Payload, e.Headers, updater); Schemas[e.Payload.SchemaId.Id].Clone().Update(e.Payload, e.Headers, updater);
} }
} }
} }

4
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonAppEntity.cs

@ -7,12 +7,14 @@
// ========================================================================== // ==========================================================================
using Newtonsoft.Json; using Newtonsoft.Json;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
public sealed class JsonAppEntity : JsonEntity, IAppEntity [Immutable]
public sealed class JsonAppEntity : JsonEntity<JsonAppEntity>, IAppEntity
{ {
[JsonProperty] [JsonProperty]
public string Name { get; set; } public string Name { get; set; }

10
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonEntity.cs

@ -9,10 +9,13 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Orleans.Concurrency;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
public abstract class JsonEntity [Immutable]
public abstract class JsonEntity<T> : Cloneable<T>, IUpdateableEntityWithVersion where T : Cloneable
{ {
[JsonProperty] [JsonProperty]
public Guid Id { get; set; } public Guid Id { get; set; }
@ -25,5 +28,10 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
[JsonProperty] [JsonProperty]
public long Version { get; set; } public long Version { get; set; }
public T Clone()
{
return Clone(x => { });
}
} }
} }

11
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonRuleEntity.cs

@ -8,13 +8,20 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Read.Rules; using Squidex.Domain.Apps.Read.Rules;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
public sealed class JsonRuleEntity : JsonEntity, IRuleEntity [Immutable]
public sealed class JsonRuleEntity :
JsonEntity<JsonRuleEntity>,
IRuleEntity,
IUpdateableEntityWithAppRef,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy
{ {
[JsonProperty] [JsonProperty]
public Guid AppId { get; set; } public Guid AppId { get; set; }
@ -26,6 +33,6 @@ namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
public RefToken LastModifiedBy { get; set; } public RefToken LastModifiedBy { get; set; }
[JsonProperty] [JsonProperty]
public Rule Rule { get; set; } public Rule RuleDef { get; set; }
} }
} }

9
src/Squidex.Domain.Apps.Read/State/Orleans/Grains/Implementations/JsonSchemaEntity.cs

@ -8,13 +8,20 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Orleans.Concurrency;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations namespace Squidex.Domain.Apps.Read.State.Orleans.Grains.Implementations
{ {
public sealed class JsonSchemaEntity : JsonEntity, ISchemaEntity [Immutable]
public sealed class JsonSchemaEntity :
JsonEntity<JsonSchemaEntity>,
ISchemaEntity,
IUpdateableEntityWithAppRef,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy
{ {
[JsonProperty] [JsonProperty]
public string Name { get; set; } public string Name { get; set; }

57
src/Squidex.Infrastructure/Json/Orleans/J.cs

@ -1,57 +0,0 @@
// ==========================================================================
// J.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Squidex.Infrastructure.Json.Orleans
{
public struct J<T> : IJsonValue
{
private readonly T value;
private readonly bool isImmutable;
public T Value
{
get { return value; }
}
bool IJsonValue.IsImmutable
{
get { return isImmutable; }
}
object IJsonValue.Value
{
get { return Value; }
}
[JsonConstructor]
public J(T value, bool isImmutable = false)
{
this.value = value;
this.isImmutable = isImmutable;
}
public static implicit operator T(J<T> value)
{
return value.Value;
}
public static implicit operator J<T>(T d)
{
return new J<T>(d);
}
public static Task<J<T>> AsTask(T value)
{
return Task.FromResult<J<T>>(value);
}
}
}

21
src/Squidex.Infrastructure/Json/Orleans/JsonExternalSerializer.cs

@ -7,8 +7,8 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Orleans.Runtime; using Orleans.Runtime;
@ -19,12 +19,15 @@ namespace Squidex.Infrastructure.Json.Orleans
public class JsonExternalSerializer : IExternalSerializer public class JsonExternalSerializer : IExternalSerializer
{ {
private readonly JsonSerializer serializer; private readonly JsonSerializer serializer;
private readonly HashSet<Type> types;
public JsonExternalSerializer(JsonSerializer serializer) public JsonExternalSerializer(JsonSerializer serializer, params Type[] types)
{ {
Guard.NotNull(serializer, nameof(serializer)); Guard.NotNull(serializer, nameof(serializer));
this.serializer = serializer; this.serializer = serializer;
this.types = new HashSet<Type>(types);
} }
public void Initialize(Logger logger) public void Initialize(Logger logger)
@ -33,25 +36,15 @@ namespace Squidex.Infrastructure.Json.Orleans
public bool IsSupportedType(Type itemType) public bool IsSupportedType(Type itemType)
{ {
return itemType.GetInterfaces().Contains(typeof(IJsonValue)); return types.Contains(itemType);
} }
public object DeepCopy(object source, ICopyContext context) public object DeepCopy(object source, ICopyContext context)
{ {
var jsonValue = source as IJsonValue; if (source == null)
if (jsonValue == null)
{ {
return null; return null;
} }
else if (jsonValue.IsImmutable)
{
return jsonValue;
}
else if (jsonValue.Value == null)
{
return jsonValue;
}
else else
{ {
return JObject.FromObject(source, serializer).ToObject(source.GetType(), serializer); return JObject.FromObject(source, serializer).ToObject(source.GetType(), serializer);

Loading…
Cancel
Save