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