mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
17 changed files with 331 additions and 26 deletions
@ -0,0 +1,49 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.Counter |
||||
|
{ |
||||
|
public sealed class CounterGrain : GrainOfGuid, ICounterGrain |
||||
|
{ |
||||
|
private readonly IGrainState<State> state; |
||||
|
|
||||
|
[CollectionName("Counters")] |
||||
|
public sealed class State |
||||
|
{ |
||||
|
public Dictionary<string, long> Counters { get; set; } = new Dictionary<string, long>(); |
||||
|
} |
||||
|
|
||||
|
public CounterGrain(IGrainState<State> state) |
||||
|
{ |
||||
|
Guard.NotNull(state); |
||||
|
|
||||
|
this.state = state; |
||||
|
} |
||||
|
|
||||
|
public Task<long> IncrementAsync(string name) |
||||
|
{ |
||||
|
state.Value.Counters.TryGetValue(name, out var value); |
||||
|
|
||||
|
return ResetAsync(name, value + 1); |
||||
|
} |
||||
|
|
||||
|
public async Task<long> ResetAsync(string name, long value) |
||||
|
{ |
||||
|
state.Value.Counters[name] = value; |
||||
|
|
||||
|
await state.WriteAsync(); |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Core.Scripting; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.Counter |
||||
|
{ |
||||
|
public sealed class CounterScriptExtension : IScriptExtension |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
|
||||
|
public CounterScriptExtension(IGrainFactory grainFactory) |
||||
|
{ |
||||
|
Guard.NotNull(grainFactory); |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public void Extend(ExecutionContext context, bool async) |
||||
|
{ |
||||
|
if (context.TryGetValue("appId", out var temp) && temp is Guid appId) |
||||
|
{ |
||||
|
var engine = context.Engine; |
||||
|
|
||||
|
engine.SetValue("incrementCounter", new Func<string, long>(name => |
||||
|
{ |
||||
|
return Increment(appId, name); |
||||
|
})); |
||||
|
|
||||
|
engine.SetValue("resetCounter", new Func<string, long, long>((name, value) => |
||||
|
{ |
||||
|
return Reset(appId, name, value); |
||||
|
})); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private long Increment(Guid appId, string name) |
||||
|
{ |
||||
|
var grain = grainFactory.GetGrain<ICounterGrain>(appId); |
||||
|
|
||||
|
return Task.Run(() => grain.IncrementAsync(name)).Result; |
||||
|
} |
||||
|
|
||||
|
private long Reset(Guid appId, string name, long value) |
||||
|
{ |
||||
|
var grain = grainFactory.GetGrain<ICounterGrain>(appId); |
||||
|
|
||||
|
return Task.Run(() => grain.ResetAsync(name, value)).Result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.Counter |
||||
|
{ |
||||
|
public interface ICounterGrain : IGrainWithGuidKey |
||||
|
{ |
||||
|
Task<long> IncrementAsync(string name); |
||||
|
|
||||
|
Task<long> ResetAsync(string name, long value); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.Counter |
||||
|
{ |
||||
|
public class CounterGrainTests |
||||
|
{ |
||||
|
private readonly IGrainState<CounterGrain.State> grainState = A.Fake<IGrainState<CounterGrain.State>>(); |
||||
|
private readonly CounterGrain sut; |
||||
|
|
||||
|
public CounterGrainTests() |
||||
|
{ |
||||
|
sut = new CounterGrain(grainState); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_increment_counters() |
||||
|
{ |
||||
|
Assert.Equal(1, await sut.IncrementAsync("Counter1")); |
||||
|
Assert.Equal(2, await sut.IncrementAsync("Counter1")); |
||||
|
|
||||
|
Assert.Equal(1, await sut.IncrementAsync("Counter2")); |
||||
|
Assert.Equal(2, await sut.IncrementAsync("Counter2")); |
||||
|
|
||||
|
A.CallTo(() => grainState.WriteAsync()) |
||||
|
.MustHaveHappened(4, Times.Exactly); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_reset_counter() |
||||
|
{ |
||||
|
Assert.Equal(1, await sut.IncrementAsync("Counter1")); |
||||
|
Assert.Equal(2, await sut.IncrementAsync("Counter1")); |
||||
|
|
||||
|
Assert.Equal(1, await sut.ResetAsync("Counter1", 1)); |
||||
|
|
||||
|
Assert.Equal(2, await sut.IncrementAsync("Counter1")); |
||||
|
|
||||
|
A.CallTo(() => grainState.WriteAsync()) |
||||
|
.MustHaveHappened(4, Times.Exactly); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,89 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using FakeItEasy; |
||||
|
using Microsoft.Extensions.Caching.Memory; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Core.Scripting; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.Counter |
||||
|
{ |
||||
|
public class CounterScriptExtensionTests |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>(); |
||||
|
private readonly ICounterGrain counter = A.Fake<ICounterGrain>(); |
||||
|
private readonly JintScriptEngine sut; |
||||
|
|
||||
|
public CounterScriptExtensionTests() |
||||
|
{ |
||||
|
var extensions = new IScriptExtension[] |
||||
|
{ |
||||
|
new CounterScriptExtension(grainFactory) |
||||
|
}; |
||||
|
|
||||
|
var cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); |
||||
|
|
||||
|
sut = new JintScriptEngine(cache, extensions) |
||||
|
{ |
||||
|
Timeout = TimeSpan.FromSeconds(1) |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_reset_counter() |
||||
|
{ |
||||
|
var appId = Guid.NewGuid(); |
||||
|
|
||||
|
A.CallTo(() => grainFactory.GetGrain<ICounterGrain>(appId, null)) |
||||
|
.Returns(counter); |
||||
|
|
||||
|
A.CallTo(() => counter.ResetAsync("my", 4)) |
||||
|
.Returns(3); |
||||
|
|
||||
|
const string script = @"
|
||||
|
return resetCounter('my', 4); |
||||
|
";
|
||||
|
|
||||
|
var context = new ScriptContext |
||||
|
{ |
||||
|
["appId"] = appId |
||||
|
}; |
||||
|
|
||||
|
var result = sut.Interpolate(context, script); |
||||
|
|
||||
|
Assert.Equal("3", result); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_increment_counter() |
||||
|
{ |
||||
|
var appId = Guid.NewGuid(); |
||||
|
|
||||
|
A.CallTo(() => grainFactory.GetGrain<ICounterGrain>(appId, null)) |
||||
|
.Returns(counter); |
||||
|
|
||||
|
A.CallTo(() => counter.IncrementAsync("my")) |
||||
|
.Returns(3); |
||||
|
|
||||
|
const string script = @"
|
||||
|
return incrementCounter('my'); |
||||
|
";
|
||||
|
|
||||
|
var context = new ScriptContext |
||||
|
{ |
||||
|
["appId"] = appId |
||||
|
}; |
||||
|
|
||||
|
var result = sut.Interpolate(context, script); |
||||
|
|
||||
|
Assert.Equal("3", result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue