Browse Source

1) Synchronization fixes

2) Stylecop.json settings added
pull/221/head
Sebastian Stehle 8 years ago
parent
commit
48c8dd6f68
  1. 2
      Squidex.ruleset
  2. 3
      src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj
  3. 3
      src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  4. 3
      src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj
  5. 3
      src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj
  6. 3
      src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj
  7. 3
      src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
  8. 3
      src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj
  9. 3
      src/Squidex.Domain.Users/Squidex.Domain.Users.csproj
  10. 3
      src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj
  11. 2
      src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs
  12. 3
      src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj
  13. 3
      src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj
  14. 3
      src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj
  15. 3
      src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj
  16. 3
      src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj
  17. 11
      src/Squidex.Infrastructure/Commands/AggregateHandler.cs
  18. 3
      src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
  19. 4
      src/Squidex.Infrastructure/States/IStateFactory.cs
  20. 6
      src/Squidex.Infrastructure/States/Persistence.cs
  21. 28
      src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs
  22. 20
      src/Squidex.Infrastructure/States/StateFactory.cs
  23. 12
      src/Squidex.Infrastructure/States/Store.cs
  24. 3
      src/Squidex.Shared/Squidex.Shared.csproj
  25. 5
      src/Squidex/AppConfiguration.cs
  26. 4
      src/Squidex/Squidex.csproj
  27. 16
      stylecop.json
  28. 3
      tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj
  29. 3
      tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj
  30. 3
      tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj
  31. 3
      tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj
  32. 75
      tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs
  33. 79
      tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs
  34. 146
      tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs
  35. 3
      tools/GenerateLanguages/GenerateLanguages.csproj
  36. 3
      tools/Migrate_00/Migrate_00.csproj
  37. 3
      tools/Migrate_01/Migrate_01.csproj

2
Squidex.ruleset

@ -54,7 +54,7 @@
<Rule Id="SA1612" Action="None" /> <Rule Id="SA1612" Action="None" />
<Rule Id="SA1615" Action="None" /> <Rule Id="SA1615" Action="None" />
<Rule Id="SA1623" Action="None" /> <Rule Id="SA1623" Action="None" />
<Rule Id="SA1633" Action="None" /> <Rule Id="SA1633" Action="Error" />
<Rule Id="SA1649" Action="Error" /> <Rule Id="SA1649" Action="Error" />
<Rule Id="SA1634" Action="None" /> <Rule Id="SA1634" Action="None" />
<Rule Id="SA1635" Action="None" /> <Rule Id="SA1635" Action="None" />

3
src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj

@ -17,4 +17,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -26,4 +26,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Apps.Core/Squidex.Domain.Apps.Core.csproj

@ -23,4 +23,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj

@ -24,4 +24,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj

@ -22,4 +22,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj

@ -22,4 +22,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj

@ -24,4 +24,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Domain.Users/Squidex.Domain.Users.csproj

@ -21,4 +21,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Infrastructure.Azure/Squidex.Infrastructure.Azure.csproj

@ -14,4 +14,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

2
src/Squidex.Infrastructure.GetEventStore/EventSourcing/GetEventStore.cs

@ -72,7 +72,7 @@ namespace Squidex.Infrastructure.EventSourcing
StreamEventsSlice currentSlice; StreamEventsSlice currentSlice;
do do
{ {
currentSlice = await connection.ReadStreamEventsForwardAsync(streamName, sliceStart, ReadPageSize, false); currentSlice = await connection.ReadStreamEventsForwardAsync(streamName, sliceStart, ReadPageSize, true);
if (currentSlice.Status == SliceReadStatus.Success) if (currentSlice.Status == SliceReadStatus.Success)
{ {

3
src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj

@ -18,4 +18,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Infrastructure.GoogleCloud/Squidex.Infrastructure.GoogleCloud.csproj

@ -19,4 +19,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj

@ -19,4 +19,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Infrastructure.RabbitMq/Squidex.Infrastructure.RabbitMq.csproj

@ -19,4 +19,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
src/Squidex.Infrastructure.Redis/Squidex.Infrastructure.Redis.csproj

@ -19,4 +19,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

11
src/Squidex.Infrastructure/Commands/AggregateHandler.cs

@ -106,8 +106,19 @@ namespace Squidex.Infrastructure.Commands
await handler(domainObject); await handler(domainObject);
try
{
await domainObject.WriteAsync(); await domainObject.WriteAsync();
stateFactory.Synchronize<T, Guid>(domainObjectId);
}
catch
{
stateFactory.Remove<T, Guid>(domainObjectId);
throw;
}
if (!context.IsCompleted) if (!context.IsCompleted)
{ {
if (isUpdate) if (isUpdate)

3
src/Squidex.Infrastructure/Squidex.Infrastructure.csproj

@ -26,4 +26,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

4
src/Squidex.Infrastructure/States/IStateFactory.cs

@ -24,5 +24,9 @@ namespace Squidex.Infrastructure.States
Task<T> CreateAsync<T>(Guid key) where T : IStatefulObject<Guid>; Task<T> CreateAsync<T>(Guid key) where T : IStatefulObject<Guid>;
Task<T> CreateAsync<T, TKey>(TKey key) where T : IStatefulObject<TKey>; Task<T> CreateAsync<T, TKey>(TKey key) where T : IStatefulObject<TKey>;
void Remove<T, TKey>(TKey key) where T : IStatefulObject<TKey>;
void Synchronize<T, TKey>(TKey key) where T : IStatefulObject<TKey>;
} }
} }

6
src/Squidex.Infrastructure/States/Persistence.cs

@ -10,21 +10,17 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
#pragma warning disable RECS0012 // 'if' statement can be re-written as 'switch' statement
namespace Squidex.Infrastructure.States namespace Squidex.Infrastructure.States
{ {
internal sealed class Persistence<TOwner, TKey> : Persistence<TOwner, object, TKey>, IPersistence internal sealed class Persistence<TOwner, TKey> : Persistence<TOwner, object, TKey>, IPersistence
{ {
public Persistence(TKey ownerKey, public Persistence(TKey ownerKey,
Action invalidate,
Action failed,
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
ISnapshotStore<object, TKey> snapshotStore, ISnapshotStore<object, TKey> snapshotStore,
IStreamNameResolver streamNameResolver, IStreamNameResolver streamNameResolver,
Func<Envelope<IEvent>, Task> applyEvent) Func<Envelope<IEvent>, Task> applyEvent)
: base(ownerKey, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent) : base(ownerKey, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, PersistenceMode.EventSourcing, null, applyEvent)
{ {
} }
} }

28
src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs

@ -24,8 +24,6 @@ namespace Squidex.Infrastructure.States
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter; private readonly IEventDataFormatter eventDataFormatter;
private readonly PersistenceMode persistenceMode; private readonly PersistenceMode persistenceMode;
private readonly Action invalidate;
private readonly Action failed;
private readonly Func<TSnapshot, Task> applyState; private readonly Func<TSnapshot, Task> applyState;
private readonly Func<Envelope<IEvent>, Task> applyEvent; private readonly Func<Envelope<IEvent>, Task> applyEvent;
private long versionSnapshot = EtagVersion.Empty; private long versionSnapshot = EtagVersion.Empty;
@ -38,8 +36,6 @@ namespace Squidex.Infrastructure.States
} }
public Persistence(TKey ownerKey, public Persistence(TKey ownerKey,
Action invalidate,
Action failed,
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
ISnapshotStore<TSnapshot, TKey> snapshotStore, ISnapshotStore<TSnapshot, TKey> snapshotStore,
@ -53,8 +49,6 @@ namespace Squidex.Infrastructure.States
this.applyEvent = applyEvent; this.applyEvent = applyEvent;
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.invalidate = invalidate;
this.failed = failed;
this.persistenceMode = persistenceMode; this.persistenceMode = persistenceMode;
this.snapshotStore = snapshotStore; this.snapshotStore = snapshotStore;
this.streamNameResolver = streamNameResolver; this.streamNameResolver = streamNameResolver;
@ -130,8 +124,6 @@ namespace Squidex.Infrastructure.States
} }
public async Task WriteSnapshotAsync(TSnapshot state) public async Task WriteSnapshotAsync(TSnapshot state)
{
try
{ {
var newVersion = UseEventSourcing() ? versionEvents : versionSnapshot + 1; var newVersion = UseEventSourcing() ? versionEvents : versionSnapshot + 1;
@ -150,23 +142,12 @@ namespace Squidex.Infrastructure.States
} }
UpdateVersion(); UpdateVersion();
invalidate?.Invoke();
}
catch
{
failed?.Invoke();
throw;
}
} }
public async Task WriteEventsAsync(IEnumerable<Envelope<IEvent>> events) public async Task WriteEventsAsync(IEnumerable<Envelope<IEvent>> events)
{ {
Guard.NotNull(events, nameof(@events)); Guard.NotNull(events, nameof(@events));
try
{
var eventArray = events.ToArray(); var eventArray = events.ToArray();
if (eventArray.Length > 0) if (eventArray.Length > 0)
@ -191,15 +172,6 @@ namespace Squidex.Infrastructure.States
} }
UpdateVersion(); UpdateVersion();
invalidate?.Invoke();
}
catch
{
failed?.Invoke();
throw;
}
} }
private EventData[] GetEventData(Envelope<IEvent>[] events, Guid commitId) private EventData[] GetEventData(Envelope<IEvent>[] events, Guid commitId)

20
src/Squidex.Infrastructure/States/StateFactory.cs

@ -125,15 +125,7 @@ namespace Squidex.Infrastructure.States
} }
var state = (T)services.GetService(typeof(T)); var state = (T)services.GetService(typeof(T));
var stateStore = new Store<T, TKey>(eventStore, eventDataFormatter, services, streamNameResolver);
var stateStore = new Store<T, TKey>(eventStore, eventDataFormatter, services, streamNameResolver,
() =>
{
pubSub.Publish(new InvalidateMessage { Key = key.ToString() }, false);
}, () =>
{
statesCache.Remove(key);
});
stateObj = new ObjectHolder<T, TKey>(state, key, stateStore); stateObj = new ObjectHolder<T, TKey>(state, key, stateStore);
@ -146,6 +138,16 @@ namespace Squidex.Infrastructure.States
} }
} }
public void Remove<T, TKey>(TKey key) where T : IStatefulObject<TKey>
{
statesCache.Remove(key);
}
public void Synchronize<T, TKey>(TKey key) where T : IStatefulObject<TKey>
{
pubSub.Publish(new InvalidateMessage { Key = key.ToString() }, false);
}
protected override void DisposeObject(bool disposing) protected override void DisposeObject(bool disposing)
{ {
if (disposing && pubSubSubscription != null) if (disposing && pubSubSubscription != null)

12
src/Squidex.Infrastructure/States/Store.cs

@ -14,8 +14,6 @@ namespace Squidex.Infrastructure.States
{ {
internal sealed class Store<TOwner, TKey> : IStore<TKey> internal sealed class Store<TOwner, TKey> : IStore<TKey>
{ {
private readonly Action invalidate;
private readonly Action failed;
private readonly IServiceProvider services; private readonly IServiceProvider services;
private readonly IStreamNameResolver streamNameResolver; private readonly IStreamNameResolver streamNameResolver;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
@ -25,14 +23,10 @@ namespace Squidex.Infrastructure.States
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
IServiceProvider services, IServiceProvider services,
IStreamNameResolver streamNameResolver, IStreamNameResolver streamNameResolver)
Action invalidate = null,
Action failed = null)
{ {
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.failed = failed;
this.invalidate = invalidate;
this.services = services; this.services = services;
this.streamNameResolver = streamNameResolver; this.streamNameResolver = streamNameResolver;
} }
@ -53,7 +47,7 @@ namespace Squidex.Infrastructure.States
var snapshotStore = (ISnapshotStore<object, TKey>)services.GetService(typeof(ISnapshotStore<object, TKey>)); var snapshotStore = (ISnapshotStore<object, TKey>)services.GetService(typeof(ISnapshotStore<object, TKey>));
return new Persistence<TOwner, TKey>(key, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent); return new Persistence<TOwner, TKey>(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, applyEvent);
} }
private IPersistence<TState> CreatePersistence<TState>(TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent) private IPersistence<TState> CreatePersistence<TState>(TKey key, PersistenceMode mode, Func<TState, Task> applySnapshot, Func<Envelope<IEvent>, Task> applyEvent)
@ -62,7 +56,7 @@ namespace Squidex.Infrastructure.States
var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>)); var snapshotStore = (ISnapshotStore<TState, TKey>)services.GetService(typeof(ISnapshotStore<TState, TKey>));
return new Persistence<TOwner, TState, TKey>(key, invalidate, failed, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent); return new Persistence<TOwner, TState, TKey>(key, eventStore, eventDataFormatter, snapshotStore, streamNameResolver, mode, applySnapshot, applyEvent);
} }
} }
} }

3
src/Squidex.Shared/Squidex.Shared.csproj

@ -14,4 +14,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

5
src/Squidex/AppConfiguration.cs

@ -1,9 +1,8 @@
// ========================================================================== // ==========================================================================
// AppConfiguration.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;

4
src/Squidex/Squidex.csproj

@ -91,4 +91,8 @@
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

16
stylecop.json

@ -0,0 +1,16 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
},
"documentationRules": {
"companyName": "Squidex UG (haftungsbeschraenkt)",
"copyrightText": "==========================================================================\n Squidex Headless CMS\n==========================================================================\n Copyright (c) {companyName}\n All rights reserved. Licensed under the {licenseName} license.\n==========================================================================",
"variables": {
"licenseName": "MIT"
},
"xmlHeader": false
}
}
}

3
tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj

@ -25,4 +25,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj

@ -35,4 +35,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj

@ -26,4 +26,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj

@ -29,4 +29,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

75
tests/Squidex.Infrastructure.Tests/States/StateEventSourcingTests.cs → tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// StateEventSourcingTests.cs // PersistenceEventSourcingTests.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -20,7 +20,7 @@ using Xunit;
namespace Squidex.Infrastructure.States namespace Squidex.Infrastructure.States
{ {
public class StateEventSourcingTests public class PersistenceEventSourcingTests
{ {
private class MyStatefulObject : IStatefulObject<string> private class MyStatefulObject : IStatefulObject<string>
{ {
@ -73,7 +73,7 @@ namespace Squidex.Infrastructure.States
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>(); private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>();
private readonly StateFactory sut; private readonly StateFactory sut;
public StateEventSourcingTests() public PersistenceEventSourcingTests()
{ {
A.CallTo(() => services.GetService(typeof(MyStatefulObject))) A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.Returns(statefulObject); .Returns(statefulObject);
@ -179,7 +179,7 @@ namespace Squidex.Infrastructure.States
} }
[Fact] [Fact]
public async Task Should_not_throw_exception_if_noting_expected() public async Task Should_not_throw_exception_if_nothing_expected()
{ {
statefulObject.ExpectedVersion = EtagVersion.Any; statefulObject.ExpectedVersion = EtagVersion.Any;
@ -188,45 +188,9 @@ namespace Squidex.Infrastructure.States
await sut.GetSingleAsync<MyStatefulObject>(key); await sut.GetSingleAsync<MyStatefulObject>(key);
} }
[Fact]
public async Task Should_provide_state_from_services_and_add_to_cache()
{
statefulObject.ExpectedVersion = EtagVersion.Any;
SetupEventStore(0);
var actualObject = await sut.GetSingleAsync<MyStatefulObject>(key);
Assert.Same(statefulObject, actualObject);
Assert.NotNull(cache.Get<object>(key));
}
[Fact]
public async Task Should_serve_next_request_from_cache()
{
SetupEventStore(0);
var actualObject1 = await sut.GetSingleAsync<MyStatefulObject>(key);
Assert.Same(statefulObject, actualObject1);
Assert.NotNull(cache.Get<object>(key));
var actualObject2 = await sut.GetSingleAsync<MyStatefulObject>(key);
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.MustHaveHappened(Repeated.Exactly.Once);
}
[Fact] [Fact]
public async Task Should_write_to_store_with_previous_position() public async Task Should_write_to_store_with_previous_position()
{ {
InvalidateMessage message = null;
pubSub.Subscribe<InvalidateMessage>(m =>
{
message = m;
});
SetupEventStore(3); SetupEventStore(3);
var actualObject = await sut.GetSingleAsync<MyStatefulObject>(key); var actualObject = await sut.GetSingleAsync<MyStatefulObject>(key);
@ -240,9 +204,6 @@ namespace Squidex.Infrastructure.States
.MustHaveHappened(); .MustHaveHappened();
A.CallTo(() => eventStore.AppendEventsAsync(A<Guid>.Ignored, key, 4, A<ICollection<EventData>>.That.Matches(x => x.Count == 2))) A.CallTo(() => eventStore.AppendEventsAsync(A<Guid>.Ignored, key, 4, A<ICollection<EventData>>.That.Matches(x => x.Count == 2)))
.MustHaveHappened(); .MustHaveHappened();
Assert.NotNull(message);
Assert.Equal(key, message.Key);
} }
[Fact] [Fact]
@ -259,17 +220,7 @@ namespace Squidex.Infrastructure.States
} }
[Fact] [Fact]
public async Task Should_remove_from_cache_when_invalidation_message_received() public async Task Should_not_remove_from_cache_when_write_failed()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject>(key);
await InvalidateCacheAsync();
Assert.False(cache.TryGetValue(key, out var t));
}
[Fact]
public async Task Should_remove_from_cache_when_write_failed()
{ {
A.CallTo(() => eventStore.AppendEventsAsync(A<Guid>.Ignored, A<string>.Ignored, A<long>.Ignored, A<ICollection<EventData>>.Ignored)) A.CallTo(() => eventStore.AppendEventsAsync(A<Guid>.Ignored, A<string>.Ignored, A<long>.Ignored, A<ICollection<EventData>>.Ignored))
.Throws(new InvalidOperationException()); .Throws(new InvalidOperationException());
@ -278,7 +229,7 @@ namespace Squidex.Infrastructure.States
await Assert.ThrowsAsync<InvalidOperationException>(() => statefulObject.WriteEventsAsync(new MyEvent())); await Assert.ThrowsAsync<InvalidOperationException>(() => statefulObject.WriteEventsAsync(new MyEvent()));
Assert.False(cache.TryGetValue(key, out var t)); Assert.True(cache.TryGetValue(key, out var t));
} }
[Fact] [Fact]
@ -305,20 +256,6 @@ namespace Squidex.Infrastructure.States
.MustHaveHappened(Repeated.Exactly.Once); .MustHaveHappened(Repeated.Exactly.Once);
} }
private async Task RemoveFromCacheAsync()
{
cache.Remove(key);
await Task.Delay(400);
}
private async Task InvalidateCacheAsync()
{
pubSub.Publish(new InvalidateMessage { Key = key }, true);
await Task.Delay(400);
}
private void SetupEventStore(int count, int eventOffset = 0, int readPosition = 0) private void SetupEventStore(int count, int eventOffset = 0, int readPosition = 0)
{ {
SetupEventStore(Enumerable.Repeat(0, count).Select(x => new MyEvent()).ToArray(), eventOffset, readPosition); SetupEventStore(Enumerable.Repeat(0, count).Select(x => new MyEvent()).ToArray(), eventOffset, readPosition);

79
tests/Squidex.Infrastructure.Tests/States/StateSnapshotTests.cs → tests/Squidex.Infrastructure.Tests/States/PersistenceSnapshotTests.cs

@ -19,7 +19,7 @@ using Xunit;
namespace Squidex.Infrastructure.States namespace Squidex.Infrastructure.States
{ {
public class StateSnapshotTests : IDisposable public class PersistenceSnapshotTests : IDisposable
{ {
private class MyStatefulObject : IStatefulObject<string> private class MyStatefulObject : IStatefulObject<string>
{ {
@ -67,7 +67,7 @@ namespace Squidex.Infrastructure.States
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>(); private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>();
private readonly StateFactory sut; private readonly StateFactory sut;
public StateSnapshotTests() public PersistenceSnapshotTests()
{ {
A.CallTo(() => services.GetService(typeof(MyStatefulObject))) A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.Returns(statefulObject); .Returns(statefulObject);
@ -142,53 +142,9 @@ namespace Squidex.Infrastructure.States
await sut.GetSingleAsync<MyStatefulObject, string>(key); await sut.GetSingleAsync<MyStatefulObject, string>(key);
} }
[Fact]
public async Task Should_provide_state_from_services_and_add_to_cache()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject);
Assert.NotNull(cache.Get<object>(key));
}
[Fact]
public async Task Should_serve_next_request_from_cache()
{
var actualObject1 = await sut.GetSingleAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject1);
Assert.NotNull(cache.Get<object>(key));
var actualObject2 = await sut.GetSingleAsync<MyStatefulObject, string>(key);
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.MustHaveHappened(Repeated.Exactly.Once);
}
[Fact]
public async Task Should_not_serve_next_request_from_cache_when_detached()
{
var actualObject1 = await sut.CreateAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject1);
Assert.Null(cache.Get<object>(key));
var actualObject2 = await sut.CreateAsync<MyStatefulObject, string>(key);
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.MustHaveHappened(Repeated.Exactly.Twice);
}
[Fact] [Fact]
public async Task Should_write_to_store_with_previous_version() public async Task Should_write_to_store_with_previous_version()
{ {
InvalidateMessage message = null;
pubSub.Subscribe<InvalidateMessage>(m =>
{
message = m;
});
A.CallTo(() => snapshotStore.ReadAsync(key)) A.CallTo(() => snapshotStore.ReadAsync(key))
.Returns((123, 13)); .Returns((123, 13));
@ -203,9 +159,6 @@ namespace Squidex.Infrastructure.States
A.CallTo(() => snapshotStore.WriteAsync(key, 456, 13, 14)) A.CallTo(() => snapshotStore.WriteAsync(key, 456, 13, 14))
.MustHaveHappened(); .MustHaveHappened();
Assert.NotNull(message);
Assert.Equal(key, message.Key);
} }
[Fact] [Fact]
@ -223,17 +176,7 @@ namespace Squidex.Infrastructure.States
} }
[Fact] [Fact]
public async Task Should_remove_from_cache_when_invalidation_message_received() public async Task Should_not_remove_from_cache_when_write_failed()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject, string>(key);
await InvalidateCacheAsync();
Assert.False(cache.TryGetValue(key, out var t));
}
[Fact]
public async Task Should_remove_from_cache_when_write_failed()
{ {
A.CallTo(() => snapshotStore.WriteAsync(A<string>.Ignored, A<int>.Ignored, A<long>.Ignored, A<long>.Ignored)) A.CallTo(() => snapshotStore.WriteAsync(A<string>.Ignored, A<int>.Ignored, A<long>.Ignored, A<long>.Ignored))
.Throws(new InvalidOperationException()); .Throws(new InvalidOperationException());
@ -242,7 +185,7 @@ namespace Squidex.Infrastructure.States
await Assert.ThrowsAsync<InvalidOperationException>(() => statefulObject.WriteStateAsync()); await Assert.ThrowsAsync<InvalidOperationException>(() => statefulObject.WriteStateAsync());
Assert.False(cache.TryGetValue(key, out var t)); Assert.True(cache.TryGetValue(key, out var t));
} }
[Fact] [Fact]
@ -268,19 +211,5 @@ namespace Squidex.Infrastructure.States
A.CallTo(() => snapshotStore.ReadAsync(key)) A.CallTo(() => snapshotStore.ReadAsync(key))
.MustHaveHappened(Repeated.Exactly.Once); .MustHaveHappened(Repeated.Exactly.Once);
} }
private async Task RemoveFromCacheAsync()
{
cache.Remove(key);
await Task.Delay(400);
}
private async Task InvalidateCacheAsync()
{
pubSub.Publish(new InvalidateMessage { Key = key }, true);
await Task.Delay(400);
}
} }
} }

146
tests/Squidex.Infrastructure.Tests/States/StateFactoryTests.cs

@ -0,0 +1,146 @@
// ==========================================================================
// StateSnapshotTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
using Xunit;
#pragma warning disable RECS0002 // Convert anonymous method to method group
namespace Squidex.Infrastructure.States
{
public class StateFactoryTests : IDisposable
{
private class MyStatefulObject : IStatefulObject<string>
{
public Task ActivateAsync(string key, IStore<string> store)
{
return TaskHelper.Done;
}
}
private readonly string key = Guid.NewGuid().ToString();
private readonly MyStatefulObject statefulObject = new MyStatefulObject();
private readonly IEventDataFormatter eventDataFormatter = A.Fake<IEventDataFormatter>();
private readonly IEventStore eventStore = A.Fake<IEventStore>();
private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
private readonly IPubSub pubSub = new InMemoryPubSub(true);
private readonly IServiceProvider services = A.Fake<IServiceProvider>();
private readonly ISnapshotStore<int, string> snapshotStore = A.Fake<ISnapshotStore<int, string>>();
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>();
private readonly StateFactory sut;
public StateFactoryTests()
{
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.Returns(statefulObject);
A.CallTo(() => services.GetService(typeof(ISnapshotStore<int, string>)))
.Returns(snapshotStore);
sut = new StateFactory(pubSub, cache, eventStore, eventDataFormatter, services, streamNameResolver);
sut.Initialize();
}
public void Dispose()
{
sut.Dispose();
}
[Fact]
public async Task Should_provide_state_from_services_and_add_to_cache()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject);
Assert.NotNull(cache.Get<object>(key));
}
[Fact]
public async Task Should_serve_next_request_from_cache()
{
var actualObject1 = await sut.GetSingleAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject1);
Assert.NotNull(cache.Get<object>(key));
var actualObject2 = await sut.GetSingleAsync<MyStatefulObject, string>(key);
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.MustHaveHappened(Repeated.Exactly.Once);
}
[Fact]
public async Task Should_not_serve_next_request_from_cache_when_detached()
{
var actualObject1 = await sut.CreateAsync<MyStatefulObject, string>(key);
Assert.Same(statefulObject, actualObject1);
Assert.Null(cache.Get<object>(key));
var actualObject2 = await sut.CreateAsync<MyStatefulObject, string>(key);
A.CallTo(() => services.GetService(typeof(MyStatefulObject)))
.MustHaveHappened(Repeated.Exactly.Twice);
}
[Fact]
public async Task Should_remove_from_cache_when_invalidation_message_received()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject, string>(key);
await InvalidateCacheAsync();
Assert.False(cache.TryGetValue(key, out var t));
}
[Fact]
public async Task Should_remove_from_cache_when_method_called()
{
var actualObject = await sut.GetSingleAsync<MyStatefulObject>(key);
sut.Remove<MyStatefulObject, string>(key);
Assert.False(cache.TryGetValue(key, out var t));
}
[Fact]
public void Should_send_invalidation_message_on_refresh()
{
InvalidateMessage message = null;
pubSub.Subscribe<InvalidateMessage>(m =>
{
message = m;
});
sut.Synchronize<MyStatefulObject, string>(key);
Assert.NotNull(message);
Assert.Equal(key, message.Key);
}
private async Task RemoveFromCacheAsync()
{
cache.Remove(key);
await Task.Delay(400);
}
private async Task InvalidateCacheAsync()
{
pubSub.Publish(new InvalidateMessage { Key = key }, true);
await Task.Delay(400);
}
}
}

3
tools/GenerateLanguages/GenerateLanguages.csproj

@ -6,4 +6,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
tools/Migrate_00/Migrate_00.csproj

@ -11,4 +11,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

3
tools/Migrate_01/Migrate_01.csproj

@ -11,4 +11,7 @@
<PropertyGroup> <PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project> </Project>

Loading…
Cancel
Save