mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
84 changed files with 1380 additions and 620 deletions
@ -0,0 +1,16 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Contents |
||||
|
{ |
||||
|
public static class StatusColors |
||||
|
{ |
||||
|
public const string Archived = "#eb3142"; |
||||
|
public const string Draft = "#8091a5"; |
||||
|
public const string Published = "#4bb958"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Core.Contents |
||||
|
{ |
||||
|
public sealed class StatusInfo |
||||
|
{ |
||||
|
public Status Status { get; } |
||||
|
|
||||
|
public string Color { get; } |
||||
|
|
||||
|
public StatusInfo(Status status, string color) |
||||
|
{ |
||||
|
Status = status; |
||||
|
|
||||
|
Color = color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Core.Tags; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Log; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Assets |
||||
|
{ |
||||
|
public sealed class AssetEnricher : IAssetEnricher |
||||
|
{ |
||||
|
private readonly ITagService tagService; |
||||
|
|
||||
|
public AssetEnricher(ITagService tagService) |
||||
|
{ |
||||
|
Guard.NotNull(tagService, nameof(tagService)); |
||||
|
|
||||
|
this.tagService = tagService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IEnrichedAssetEntity> EnrichAsync(IAssetEntity asset) |
||||
|
{ |
||||
|
Guard.NotNull(asset, nameof(asset)); |
||||
|
|
||||
|
var enriched = await EnrichAsync(Enumerable.Repeat(asset, 1)); |
||||
|
|
||||
|
return enriched[0]; |
||||
|
} |
||||
|
|
||||
|
public async Task<IReadOnlyList<IEnrichedAssetEntity>> EnrichAsync(IEnumerable<IAssetEntity> assets) |
||||
|
{ |
||||
|
Guard.NotNull(assets, nameof(assets)); |
||||
|
|
||||
|
using (Profiler.TraceMethod<AssetEnricher>()) |
||||
|
{ |
||||
|
var results = new List<IEnrichedAssetEntity>(); |
||||
|
|
||||
|
foreach (var group in assets.GroupBy(x => x.AppId.Id)) |
||||
|
{ |
||||
|
var tagsById = await CalculateTags(group); |
||||
|
|
||||
|
foreach (var asset in group) |
||||
|
{ |
||||
|
var result = SimpleMapper.Map(asset, new AssetEntity()); |
||||
|
|
||||
|
result.TagNames = new HashSet<string>(); |
||||
|
|
||||
|
if (asset.Tags != null) |
||||
|
{ |
||||
|
foreach (var id in asset.Tags) |
||||
|
{ |
||||
|
if (tagsById.TryGetValue(id, out var name)) |
||||
|
{ |
||||
|
result.TagNames.Add(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
results.Add(result); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task<Dictionary<string, string>> CalculateTags(IGrouping<System.Guid, IAssetEntity> group) |
||||
|
{ |
||||
|
var uniqueIds = group.Where(x => x.Tags != null).SelectMany(x => x.Tags).ToHashSet(); |
||||
|
|
||||
|
return await tagService.DenormalizeTagsAsync(group.Key, TagGroups.Assets, uniqueIds); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Assets |
||||
|
{ |
||||
|
public interface IAssetEnricher |
||||
|
{ |
||||
|
Task<IEnrichedAssetEntity> EnrichAsync(IAssetEntity asset); |
||||
|
|
||||
|
Task<IReadOnlyList<IEnrichedAssetEntity>> EnrichAsync(IEnumerable<IAssetEntity> assets); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Entities.Contents.Commands; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public sealed class ContentCommandMiddleware : GrainCommandMiddleware<ContentCommand, IContentGrain> |
||||
|
{ |
||||
|
private readonly IContentEnricher contentEnricher; |
||||
|
|
||||
|
public ContentCommandMiddleware(IGrainFactory grainFactory, IContentEnricher contentEnricher) |
||||
|
: base(grainFactory) |
||||
|
{ |
||||
|
Guard.NotNull(contentEnricher, nameof(contentEnricher)); |
||||
|
|
||||
|
this.contentEnricher = contentEnricher; |
||||
|
} |
||||
|
|
||||
|
public override async Task HandleAsync(CommandContext context, Func<Task> next) |
||||
|
{ |
||||
|
await base.HandleAsync(context, next); |
||||
|
|
||||
|
if (context.PlainResult is IContentEntity content && !(context.PlainResult is IEnrichedContentEntity)) |
||||
|
{ |
||||
|
var enriched = await contentEnricher.EnrichAsync(content); |
||||
|
|
||||
|
context.Complete(enriched); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Log; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public sealed class ContentEnricher : IContentEnricher |
||||
|
{ |
||||
|
private const string DefaultColor = StatusColors.Draft; |
||||
|
private readonly IContentWorkflow contentWorkflow; |
||||
|
|
||||
|
public ContentEnricher(IContentWorkflow contentWorkflow) |
||||
|
{ |
||||
|
this.contentWorkflow = contentWorkflow; |
||||
|
} |
||||
|
|
||||
|
public async Task<IEnrichedContentEntity> EnrichAsync(IContentEntity content) |
||||
|
{ |
||||
|
Guard.NotNull(content, nameof(content)); |
||||
|
|
||||
|
var enriched = await EnrichAsync(Enumerable.Repeat(content, 1)); |
||||
|
|
||||
|
return enriched[0]; |
||||
|
} |
||||
|
|
||||
|
public async Task<IReadOnlyList<IEnrichedContentEntity>> EnrichAsync(IEnumerable<IContentEntity> contents) |
||||
|
{ |
||||
|
Guard.NotNull(contents, nameof(contents)); |
||||
|
|
||||
|
using (Profiler.TraceMethod<ContentEnricher>()) |
||||
|
{ |
||||
|
var results = new List<ContentEntity>(); |
||||
|
|
||||
|
var cache = new Dictionary<(Guid, Status), StatusInfo>(); |
||||
|
|
||||
|
foreach (var content in contents) |
||||
|
{ |
||||
|
var result = SimpleMapper.Map(content, new ContentEntity()); |
||||
|
|
||||
|
await ResolveColorAsync(content, result, cache); |
||||
|
await ResolveNextsAsync(content, result); |
||||
|
await ResolveCanUpdateAsync(content, result); |
||||
|
|
||||
|
results.Add(result); |
||||
|
} |
||||
|
|
||||
|
return results; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private async Task ResolveCanUpdateAsync(IContentEntity content, ContentEntity result) |
||||
|
{ |
||||
|
result.CanUpdate = await contentWorkflow.CanUpdateAsync(content); |
||||
|
} |
||||
|
|
||||
|
private async Task ResolveNextsAsync(IContentEntity content, ContentEntity result) |
||||
|
{ |
||||
|
result.Nexts = await contentWorkflow.GetNextsAsync(content); |
||||
|
} |
||||
|
|
||||
|
private async Task ResolveColorAsync(IContentEntity content, ContentEntity result, Dictionary<(Guid, Status), StatusInfo> cache) |
||||
|
{ |
||||
|
result.StatusColor = await GetColorAsync(content.SchemaId, content.Status, cache); |
||||
|
} |
||||
|
|
||||
|
private async Task<string> GetColorAsync(NamedId<Guid> schemaId, Status status, Dictionary<(Guid, Status), StatusInfo> cache) |
||||
|
{ |
||||
|
if (!cache.TryGetValue((schemaId.Id, status), out var info)) |
||||
|
{ |
||||
|
info = await contentWorkflow.GetInfoAsync(status); |
||||
|
|
||||
|
if (info == null) |
||||
|
{ |
||||
|
info = new StatusInfo(status, DefaultColor); |
||||
|
} |
||||
|
|
||||
|
cache[(schemaId.Id, status)] = info; |
||||
|
} |
||||
|
|
||||
|
return info.Color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public interface IContentEnricher |
||||
|
{ |
||||
|
Task<IEnrichedContentEntity> EnrichAsync(IContentEntity content); |
||||
|
|
||||
|
Task<IReadOnlyList<IEnrichedContentEntity>> EnrichAsync(IEnumerable<IContentEntity> contents); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public interface IEnrichedContentEntity : IContentEntity |
||||
|
{ |
||||
|
bool CanUpdate { get; } |
||||
|
|
||||
|
string StatusColor { get; } |
||||
|
|
||||
|
StatusInfo[] Nexts { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Controllers.Contents.Models |
||||
|
{ |
||||
|
public sealed class StatusInfoDto |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The name of the status.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public Status Status { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The color of the status.
|
||||
|
/// </summary>
|
||||
|
[Required] |
||||
|
public string Color { get; set; } |
||||
|
|
||||
|
public static StatusInfoDto FromStatusInfo(StatusInfo statusInfo) |
||||
|
{ |
||||
|
return new StatusInfoDto { Status = statusInfo.Status, Color = statusInfo.Color }; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Squidex.Domain.Apps.Core.Tags; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Assets |
||||
|
{ |
||||
|
public class AssetEnricherTests |
||||
|
{ |
||||
|
private readonly ITagService tagService = A.Fake<ITagService>(); |
||||
|
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
||||
|
private readonly AssetEnricher sut; |
||||
|
|
||||
|
public AssetEnricherTests() |
||||
|
{ |
||||
|
sut = new AssetEnricher(tagService); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_enrich_if_asset_contains_null_tags() |
||||
|
{ |
||||
|
var source = new AssetEntity { AppId = appId }; |
||||
|
|
||||
|
var result = await sut.EnrichAsync(source); |
||||
|
|
||||
|
Assert.Empty(result.TagNames); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_asset_with_tag_names() |
||||
|
{ |
||||
|
var source = new AssetEntity |
||||
|
{ |
||||
|
Tags = new HashSet<string> |
||||
|
{ |
||||
|
"id1", |
||||
|
"id2" |
||||
|
}, |
||||
|
AppId = appId |
||||
|
}; |
||||
|
|
||||
|
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.IsSameSequenceAs("id1", "id2"))) |
||||
|
.Returns(new Dictionary<string, string> |
||||
|
{ |
||||
|
["id1"] = "name1", |
||||
|
["id2"] = "name2" |
||||
|
}); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(source); |
||||
|
|
||||
|
Assert.Equal(new HashSet<string> { "name1", "name2" }, result.TagNames); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_multiple_assets_with_tag_names() |
||||
|
{ |
||||
|
var source1 = new AssetEntity |
||||
|
{ |
||||
|
Tags = new HashSet<string> |
||||
|
{ |
||||
|
"id1", |
||||
|
"id2" |
||||
|
}, |
||||
|
AppId = appId |
||||
|
}; |
||||
|
|
||||
|
var source2 = new AssetEntity |
||||
|
{ |
||||
|
Tags = new HashSet<string> |
||||
|
{ |
||||
|
"id2", |
||||
|
"id3" |
||||
|
}, |
||||
|
AppId = appId |
||||
|
}; |
||||
|
|
||||
|
A.CallTo(() => tagService.DenormalizeTagsAsync(appId.Id, TagGroups.Assets, A<HashSet<string>>.That.IsSameSequenceAs("id1", "id2", "id3"))) |
||||
|
.Returns(new Dictionary<string, string> |
||||
|
{ |
||||
|
["id1"] = "name1", |
||||
|
["id2"] = "name2", |
||||
|
["id3"] = "name3" |
||||
|
}); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(new[] { source1, source2 }); |
||||
|
|
||||
|
Assert.Equal(new HashSet<string> { "name1", "name2" }, result[0].TagNames); |
||||
|
Assert.Equal(new HashSet<string> { "name2", "name3" }, result[1].TagNames); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,91 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Orleans; |
||||
|
using Squidex.Domain.Apps.Entities.Contents.State; |
||||
|
using Squidex.Domain.Apps.Entities.TestHelpers; |
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public sealed class ContentCommandMiddlewareTests : HandlerTestBase<ContentState> |
||||
|
{ |
||||
|
private readonly IContentEnricher contentEnricher = A.Fake<IContentEnricher>(); |
||||
|
private readonly Guid contentId = Guid.NewGuid(); |
||||
|
private readonly ContentCommandMiddleware sut; |
||||
|
|
||||
|
public sealed class MyCommand : SquidexCommand |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override Guid Id |
||||
|
{ |
||||
|
get { return contentId; } |
||||
|
} |
||||
|
|
||||
|
public ContentCommandMiddlewareTests() |
||||
|
{ |
||||
|
sut = new ContentCommandMiddleware(A.Fake<IGrainFactory>(), contentEnricher); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_invoke_enricher_for_other_result() |
||||
|
{ |
||||
|
var command = CreateCommand(new MyCommand()); |
||||
|
var context = CreateContextForCommand(command); |
||||
|
|
||||
|
context.Complete(12); |
||||
|
|
||||
|
await sut.HandleAsync(context); |
||||
|
|
||||
|
A.CallTo(() => contentEnricher.EnrichAsync(A<IEnrichedContentEntity>.Ignored)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_invoke_enricher_if_already_enriched() |
||||
|
{ |
||||
|
var result = new ContentEntity(); |
||||
|
|
||||
|
var command = CreateCommand(new MyCommand()); |
||||
|
var context = CreateContextForCommand(command); |
||||
|
|
||||
|
context.Complete(result); |
||||
|
|
||||
|
await sut.HandleAsync(context); |
||||
|
|
||||
|
Assert.Same(result, context.Result<IEnrichedContentEntity>()); |
||||
|
|
||||
|
A.CallTo(() => contentEnricher.EnrichAsync(A<IEnrichedContentEntity>.Ignored)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_content_result() |
||||
|
{ |
||||
|
var result = A.Fake<IContentEntity>(); |
||||
|
|
||||
|
var command = CreateCommand(new MyCommand()); |
||||
|
var context = CreateContextForCommand(command); |
||||
|
|
||||
|
context.Complete(result); |
||||
|
|
||||
|
var enriched = new ContentEntity(); |
||||
|
|
||||
|
A.CallTo(() => contentEnricher.EnrichAsync(result)) |
||||
|
.Returns(enriched); |
||||
|
|
||||
|
await sut.HandleAsync(context); |
||||
|
|
||||
|
Assert.Same(enriched, context.Result<IEnrichedContentEntity>()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents |
||||
|
{ |
||||
|
public class ContentEnricherTests |
||||
|
{ |
||||
|
private readonly IContentWorkflow workflow = A.Fake<IContentWorkflow>(); |
||||
|
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
||||
|
private readonly ContentEnricher sut; |
||||
|
|
||||
|
public ContentEnricherTests() |
||||
|
{ |
||||
|
sut = new ContentEnricher(workflow); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_content_with_status_color() |
||||
|
{ |
||||
|
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
||||
|
|
||||
|
A.CallTo(() => workflow.GetInfoAsync(Status.Published)) |
||||
|
.Returns(new StatusInfo(Status.Published, StatusColors.Published)); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(source); |
||||
|
|
||||
|
Assert.Equal(StatusColors.Published, result.StatusColor); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_content_with_default_color_if_not_found() |
||||
|
{ |
||||
|
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
||||
|
|
||||
|
A.CallTo(() => workflow.GetInfoAsync(Status.Published)) |
||||
|
.Returns(Task.FromResult<StatusInfo>(null)); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(source); |
||||
|
|
||||
|
Assert.Equal(StatusColors.Draft, result.StatusColor); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_content_with_can_update() |
||||
|
{ |
||||
|
var source = new ContentEntity { SchemaId = schemaId }; |
||||
|
|
||||
|
A.CallTo(() => workflow.CanUpdateAsync(source)) |
||||
|
.Returns(true); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(source); |
||||
|
|
||||
|
Assert.True(result.CanUpdate); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_enrich_multiple_contents_and_cache_color() |
||||
|
{ |
||||
|
var source1 = new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
||||
|
var source2 = new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
||||
|
|
||||
|
A.CallTo(() => workflow.GetInfoAsync(Status.Published)) |
||||
|
.Returns(new StatusInfo(Status.Published, StatusColors.Published)); |
||||
|
|
||||
|
var result = await sut.EnrichAsync(new[] { source1, source2 }); |
||||
|
|
||||
|
Assert.Equal(StatusColors.Published, result[0].StatusColor); |
||||
|
Assert.Equal(StatusColors.Published, result[1].StatusColor); |
||||
|
|
||||
|
A.CallTo(() => workflow.GetInfoAsync(Status.Published)) |
||||
|
.MustHaveHappenedOnceExactly(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Domain.Apps.Events.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using ContentCreatedV2 = Squidex.Domain.Apps.Events.Contents.ContentCreated; |
||||
|
|
||||
|
namespace Migrate_01.OldEvents |
||||
|
{ |
||||
|
[EventType(nameof(ContentCreated))] |
||||
|
[Obsolete] |
||||
|
public sealed class ContentCreated : ContentEvent, IMigrated<IEvent> |
||||
|
{ |
||||
|
public Status Status { get; set; } |
||||
|
|
||||
|
public NamedContentData Data { get; set; } |
||||
|
|
||||
|
public IEvent Migrate() |
||||
|
{ |
||||
|
var migrated = SimpleMapper.Map(this, new ContentCreatedV2()); |
||||
|
|
||||
|
if (migrated.Status == default) |
||||
|
{ |
||||
|
migrated.Status = Status.Draft; |
||||
|
} |
||||
|
|
||||
|
return migrated; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Squidex.Domain.Apps.Core.Contents; |
||||
|
using Squidex.Domain.Apps.Events.Contents; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using ContentStatusChangedV2 = Squidex.Domain.Apps.Events.Contents.ContentStatusChanged; |
||||
|
|
||||
|
namespace Migrate_01.OldEvents |
||||
|
{ |
||||
|
[EventType(nameof(ContentStatusChanged))] |
||||
|
[Obsolete] |
||||
|
public sealed class ContentStatusChanged : ContentEvent, IMigrated<IEvent> |
||||
|
{ |
||||
|
public string Change { get; set; } |
||||
|
|
||||
|
public Status Status { get; set; } |
||||
|
|
||||
|
public IEvent Migrate() |
||||
|
{ |
||||
|
var migrated = SimpleMapper.Map(this, new ContentStatusChangedV2()); |
||||
|
|
||||
|
if (migrated.Status == default) |
||||
|
{ |
||||
|
migrated.Status = Status.Draft; |
||||
|
} |
||||
|
|
||||
|
if (Enum.TryParse<StatusChange>(Change, out var result)) |
||||
|
{ |
||||
|
migrated.Change = result; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
migrated.Change = StatusChange.Change; |
||||
|
} |
||||
|
|
||||
|
return migrated; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue