mirror of https://github.com/Squidex/squidex.git
22 changed files with 1070 additions and 599 deletions
@ -0,0 +1,21 @@ |
|||
// ==========================================================================
|
|||
// 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 Squidex.Domain.Apps.Entities.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|||
{ |
|||
public delegate Task<ISchemaEntity> ProvideSchema(Guid id); |
|||
|
|||
public interface IContentEnricherStep |
|||
{ |
|||
Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas); |
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
// ==========================================================================
|
|||
// 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.ConvertContent; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class ConvertData : IContentEnricherStep |
|||
{ |
|||
private readonly IAssetUrlGenerator assetUrlGenerator; |
|||
|
|||
public ConvertData(IAssetUrlGenerator assetUrlGenerator) |
|||
{ |
|||
Guard.NotNull(assetUrlGenerator); |
|||
|
|||
this.assetUrlGenerator = assetUrlGenerator; |
|||
} |
|||
|
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
var converters = GenerateConverters(context).ToArray(); |
|||
|
|||
var resolveDataDraft = context.IsUnpublished() || context.IsFrontendClient; |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
foreach (var content in group) |
|||
{ |
|||
if (content.Data != null) |
|||
{ |
|||
content.Data = content.Data.ConvertName2Name(schema.SchemaDef, converters); |
|||
} |
|||
|
|||
if (content.DataDraft != null && resolveDataDraft) |
|||
{ |
|||
content.DataDraft = content.DataDraft.ConvertName2Name(schema.SchemaDef, converters); |
|||
} |
|||
else |
|||
{ |
|||
content.DataDraft = null!; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private IEnumerable<FieldConverter> GenerateConverters(Context context) |
|||
{ |
|||
if (!context.IsFrontendClient) |
|||
{ |
|||
yield return FieldConverters.ExcludeHidden(); |
|||
yield return FieldConverters.ForNestedName2Name(ValueConverters.ExcludeHidden()); |
|||
} |
|||
|
|||
yield return FieldConverters.ExcludeChangedTypes(); |
|||
yield return FieldConverters.ForNestedName2Name(ValueConverters.ExcludeChangedTypes()); |
|||
|
|||
yield return FieldConverters.ResolveInvariant(context.App.LanguagesConfig); |
|||
yield return FieldConverters.ResolveLanguages(context.App.LanguagesConfig); |
|||
|
|||
if (!context.IsFrontendClient) |
|||
{ |
|||
if (!context.IsNoResolveLanguages()) |
|||
{ |
|||
yield return FieldConverters.ResolveFallbackLanguages(context.App.LanguagesConfig); |
|||
} |
|||
|
|||
var languages = context.Languages(); |
|||
|
|||
if (languages.Any()) |
|||
{ |
|||
yield return FieldConverters.FilterLanguages(context.App.LanguagesConfig, languages); |
|||
} |
|||
|
|||
var assetUrls = context.AssetUrls(); |
|||
|
|||
if (assetUrls.Any()) |
|||
{ |
|||
yield return FieldConverters.ResolveAssetUrls(assetUrls.ToList(), assetUrlGenerator); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// ==========================================================================
|
|||
// 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; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class EnrichForCaching : IContentEnricherStep |
|||
{ |
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
var app = context.App; |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
foreach (var content in group) |
|||
{ |
|||
content.CacheDependencies ??= new HashSet<object?>(); |
|||
|
|||
content.CacheDependencies.Add(app.Id); |
|||
content.CacheDependencies.Add(app.Version); |
|||
content.CacheDependencies.Add(schema.Id); |
|||
content.CacheDependencies.Add(schema.Version); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// ==========================================================================
|
|||
// 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.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class EnrichWithSchema : IContentEnricherStep |
|||
{ |
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
var schemaName = schema.SchemaDef.Name; |
|||
var schemaDisplayName = schema.SchemaDef.DisplayNameUnchanged(); |
|||
|
|||
foreach (var content in group) |
|||
{ |
|||
content.SchemaName = schemaName; |
|||
content.SchemaDisplayName = schemaDisplayName; |
|||
} |
|||
|
|||
if (context.IsFrontendClient) |
|||
{ |
|||
var referenceFields = schema.SchemaDef.ReferencesFields().ToArray(); |
|||
|
|||
foreach (var content in group) |
|||
{ |
|||
content.ReferenceFields = referenceFields; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
// ==========================================================================
|
|||
// 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 Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class EnrichWithWorkflows : IContentEnricherStep |
|||
{ |
|||
private const string DefaultColor = StatusColors.Draft; |
|||
|
|||
private readonly IContentWorkflow contentWorkflow; |
|||
|
|||
public EnrichWithWorkflows(IContentWorkflow contentWorkflow) |
|||
{ |
|||
Guard.NotNull(contentWorkflow); |
|||
|
|||
this.contentWorkflow = contentWorkflow; |
|||
} |
|||
|
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
var cache = new Dictionary<(Guid, Status), StatusInfo>(); |
|||
|
|||
foreach (var content in contents) |
|||
{ |
|||
await EnrichColorAsync(content, content, cache); |
|||
|
|||
if (ShouldEnrichWithStatuses(context)) |
|||
{ |
|||
await EnrichNextsAsync(content, context); |
|||
await EnrichCanUpdateAsync(content, context); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task EnrichNextsAsync(ContentEntity content, Context context) |
|||
{ |
|||
content.Nexts = await contentWorkflow.GetNextsAsync(content, context.User); |
|||
} |
|||
|
|||
private async Task EnrichCanUpdateAsync( ContentEntity content, Context context) |
|||
{ |
|||
content.CanUpdate = await contentWorkflow.CanUpdateAsync(content, context.User); |
|||
} |
|||
|
|||
private async Task EnrichColorAsync(IContentEntity content, ContentEntity result, Dictionary<(Guid, Status), StatusInfo> cache) |
|||
{ |
|||
result.StatusColor = await GetColorAsync(content, cache); |
|||
} |
|||
|
|||
private async Task<string> GetColorAsync(IContentEntity content, Dictionary<(Guid, Status), StatusInfo> cache) |
|||
{ |
|||
if (!cache.TryGetValue((content.SchemaId.Id, content.Status), out var info)) |
|||
{ |
|||
info = await contentWorkflow.GetInfoAsync(content); |
|||
|
|||
if (info == null) |
|||
{ |
|||
info = new StatusInfo(content.Status, DefaultColor); |
|||
} |
|||
|
|||
cache[(content.SchemaId.Id, content.Status)] = info; |
|||
} |
|||
|
|||
return info.Color; |
|||
} |
|||
|
|||
private static bool ShouldEnrichWithStatuses(Context context) |
|||
{ |
|||
return context.IsFrontendClient || context.IsResolveFlow(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
// ==========================================================================
|
|||
// 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.Assets; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.ExtractReferenceIds; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Assets; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class ResolveAssets : IContentEnricherStep |
|||
{ |
|||
private static readonly ILookup<Guid, IEnrichedAssetEntity> EmptyAssets = Enumerable.Empty<IEnrichedAssetEntity>().ToLookup(x => x.Id); |
|||
|
|||
private readonly IAssetUrlGenerator assetUrlGenerator; |
|||
private readonly IAssetQueryService assetQuery; |
|||
|
|||
public ResolveAssets(IAssetUrlGenerator assetUrlGenerator, IAssetQueryService assetQuery) |
|||
{ |
|||
Guard.NotNull(assetUrlGenerator); |
|||
Guard.NotNull(assetQuery); |
|||
|
|||
this.assetUrlGenerator = assetUrlGenerator; |
|||
this.assetQuery = assetQuery; |
|||
} |
|||
|
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
if (ShouldEnrich(context)) |
|||
{ |
|||
var ids = new HashSet<Guid>(); |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
AddAssetIds(ids, schema, group); |
|||
} |
|||
|
|||
var assets = await GetAssetsAsync(context, ids); |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
ResolveAssetsUrls(schema, group, assets); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void ResolveAssetsUrls(ISchemaEntity schema, IGrouping<Guid, ContentEntity> contents, ILookup<Guid, IEnrichedAssetEntity> assets) |
|||
{ |
|||
foreach (var field in schema.SchemaDef.ResolvingAssets()) |
|||
{ |
|||
foreach (var content in contents) |
|||
{ |
|||
if (content.ReferenceData == null) |
|||
{ |
|||
content.ReferenceData = new NamedContentData(); |
|||
} |
|||
|
|||
var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!; |
|||
|
|||
if (content.DataDraft.TryGetValue(field.Name, out var fieldData) && fieldData != null) |
|||
{ |
|||
foreach (var (partitionKey, partitionValue) in fieldData) |
|||
{ |
|||
var referencedImage = |
|||
field.GetReferencedIds(partitionValue, Ids.ContentOnly) |
|||
.Select(x => assets[x]) |
|||
.SelectMany(x => x) |
|||
.FirstOrDefault(x => x.Type == AssetType.Image); |
|||
|
|||
if (referencedImage != null) |
|||
{ |
|||
var url = assetUrlGenerator.GenerateUrl(referencedImage.Id.ToString()); |
|||
|
|||
content.CacheDependencies ??= new HashSet<object?>(); |
|||
|
|||
content.CacheDependencies.Add(referencedImage.Id); |
|||
content.CacheDependencies.Add(referencedImage.Version); |
|||
|
|||
fieldReference.AddJsonValue(partitionKey, JsonValue.Create(url)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task<ILookup<Guid, IEnrichedAssetEntity>> GetAssetsAsync(Context context, HashSet<Guid> ids) |
|||
{ |
|||
if (ids.Count == 0) |
|||
{ |
|||
return EmptyAssets; |
|||
} |
|||
|
|||
var assets = await assetQuery.QueryAsync(context.Clone().WithNoAssetEnrichment(true), null, Q.Empty.WithIds(ids)); |
|||
|
|||
return assets.ToLookup(x => x.Id); |
|||
} |
|||
|
|||
private void AddAssetIds(HashSet<Guid> ids, ISchemaEntity schema, IEnumerable<ContentEntity> contents) |
|||
{ |
|||
foreach (var content in contents) |
|||
{ |
|||
ids.AddRange(content.DataDraft.GetReferencedIds(schema.SchemaDef.ResolvingAssets(), Ids.ContentOnly)); |
|||
} |
|||
} |
|||
|
|||
private static bool ShouldEnrich(Context context) |
|||
{ |
|||
return context.IsFrontendClient && !context.IsNoEnrichment(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,167 @@ |
|||
// ==========================================================================
|
|||
// 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.Domain.Apps.Core.ExtractReferenceIds; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class ResolveReferences : IContentEnricherStep |
|||
{ |
|||
private static readonly ILookup<Guid, IEnrichedContentEntity> EmptyContents = Enumerable.Empty<IEnrichedContentEntity>().ToLookup(x => x.Id); |
|||
private readonly Lazy<IContentQueryService> contentQuery; |
|||
|
|||
private IContentQueryService ContentQuery |
|||
{ |
|||
get { return contentQuery.Value; } |
|||
} |
|||
|
|||
public ResolveReferences(Lazy<IContentQueryService> contentQuery) |
|||
{ |
|||
Guard.NotNull(contentQuery); |
|||
|
|||
this.contentQuery = contentQuery; |
|||
} |
|||
|
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
if (ShouldEnrich(context)) |
|||
{ |
|||
var ids = new HashSet<Guid>(); |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
AddReferenceIds(ids, schema, group); |
|||
} |
|||
|
|||
var references = await GetReferencesAsync(context, ids); |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
await ResolveReferencesAsync(context, schema, group, references, schemas); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private async Task ResolveReferencesAsync(Context context, ISchemaEntity schema, IEnumerable<ContentEntity> contents, ILookup<Guid, IEnrichedContentEntity> references, ProvideSchema schemas) |
|||
{ |
|||
var formatted = new Dictionary<IContentEntity, JsonObject>(); |
|||
|
|||
foreach (var field in schema.SchemaDef.ResolvingReferences()) |
|||
{ |
|||
foreach (var content in contents) |
|||
{ |
|||
if (content.ReferenceData == null) |
|||
{ |
|||
content.ReferenceData = new NamedContentData(); |
|||
} |
|||
|
|||
var fieldReference = content.ReferenceData.GetOrAdd(field.Name, _ => new ContentFieldData())!; |
|||
|
|||
try |
|||
{ |
|||
if (content.DataDraft.TryGetValue(field.Name, out var fieldData) && fieldData != null) |
|||
{ |
|||
foreach (var (partition, partitionValue) in fieldData) |
|||
{ |
|||
var referencedContents = |
|||
field.GetReferencedIds(partitionValue, Ids.ContentOnly) |
|||
.Select(x => references[x]) |
|||
.SelectMany(x => x) |
|||
.ToList(); |
|||
|
|||
if (referencedContents.Count == 1) |
|||
{ |
|||
var reference = referencedContents[0]; |
|||
|
|||
var referencedSchema = await schemas(reference.SchemaId.Id); |
|||
|
|||
content.CacheDependencies ??= new HashSet<object?>(); |
|||
|
|||
content.CacheDependencies.Add(referencedSchema.Id); |
|||
content.CacheDependencies.Add(referencedSchema.Version); |
|||
content.CacheDependencies.Add(reference.Id); |
|||
content.CacheDependencies.Add(reference.Version); |
|||
|
|||
var value = formatted.GetOrAdd(reference, x => Format(x, context, referencedSchema)); |
|||
|
|||
fieldReference.AddJsonValue(partition, value); |
|||
} |
|||
else if (referencedContents.Count > 1) |
|||
{ |
|||
var value = CreateFallback(context, referencedContents); |
|||
|
|||
fieldReference.AddJsonValue(partition, value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (DomainObjectNotFoundException) |
|||
{ |
|||
continue; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static JsonObject Format(IContentEntity content, Context context, ISchemaEntity referencedSchema) |
|||
{ |
|||
return content.DataDraft.FormatReferences(referencedSchema.SchemaDef, context.App.LanguagesConfig); |
|||
} |
|||
|
|||
private static JsonObject CreateFallback(Context context, List<IEnrichedContentEntity> referencedContents) |
|||
{ |
|||
var text = $"{referencedContents.Count} Reference(s)"; |
|||
|
|||
var value = JsonValue.Object(); |
|||
|
|||
foreach (var partitionKey in context.App.LanguagesConfig.AllKeys) |
|||
{ |
|||
value.Add(partitionKey, text); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
private void AddReferenceIds(HashSet<Guid> ids, ISchemaEntity schema, IEnumerable<ContentEntity> contents) |
|||
{ |
|||
foreach (var content in contents) |
|||
{ |
|||
ids.AddRange(content.DataDraft.GetReferencedIds(schema.SchemaDef.ResolvingReferences(), Ids.ContentOnly)); |
|||
} |
|||
} |
|||
|
|||
private async Task<ILookup<Guid, IEnrichedContentEntity>> GetReferencesAsync(Context context, HashSet<Guid> ids) |
|||
{ |
|||
if (ids.Count == 0) |
|||
{ |
|||
return EmptyContents; |
|||
} |
|||
|
|||
var references = await ContentQuery.QueryAsync(context.Clone().WithoutContentEnrichment(true), ids.ToList()); |
|||
|
|||
return references.ToLookup(x => x.Id); |
|||
} |
|||
|
|||
private static bool ShouldEnrich(Context context) |
|||
{ |
|||
return context.IsFrontendClient && !context.IsNoEnrichment(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Queries.Steps; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|||
{ |
|||
public class EnrichForCachingTests |
|||
{ |
|||
private readonly ISchemaEntity schema; |
|||
private readonly Context requestContext; |
|||
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
|||
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
|||
private readonly ProvideSchema schemaProvider; |
|||
private readonly EnrichForCaching sut; |
|||
|
|||
public EnrichForCachingTests() |
|||
{ |
|||
requestContext = new Context(Mocks.ApiUser(), Mocks.App(appId)); |
|||
|
|||
schema = Mocks.Schema(appId, schemaId); |
|||
schemaProvider = x => Task.FromResult(schema); |
|||
|
|||
sut = new EnrichForCaching(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_add_app_version_and_schema_as_dependency() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
await sut.EnrichAsync(requestContext, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.Contains(requestContext.App.Version, source.CacheDependencies); |
|||
|
|||
Assert.Contains(schema.Id, source.CacheDependencies); |
|||
Assert.Contains(schema.Version, source.CacheDependencies); |
|||
} |
|||
|
|||
private ContentEntity PublishedContent() |
|||
{ |
|||
return new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Queries.Steps; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|||
{ |
|||
public class EnrichWithSchemaTests |
|||
{ |
|||
private readonly ISchemaEntity schema; |
|||
private readonly Context requestContext; |
|||
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
|||
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
|||
private readonly ProvideSchema schemaProvider; |
|||
private readonly EnrichWithSchema sut; |
|||
|
|||
public EnrichWithSchemaTests() |
|||
{ |
|||
requestContext = new Context(Mocks.ApiUser(), Mocks.App(appId)); |
|||
|
|||
schema = Mocks.Schema(appId, schemaId); |
|||
schemaProvider = x => Task.FromResult(schema); |
|||
|
|||
sut = new EnrichWithSchema(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_enrich_with_reference_fields() |
|||
{ |
|||
var ctx = new Context(Mocks.FrontendUser(), requestContext.App); |
|||
|
|||
var source = PublishedContent(); |
|||
|
|||
await sut.EnrichAsync(ctx, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.NotNull(source.ReferenceFields); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_enrich_with_reference_fields_when_not_frontend() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
await sut.EnrichAsync(requestContext, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.Null(source.ReferenceFields); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_enrich_with_schema_names() |
|||
{ |
|||
var ctx = new Context(Mocks.FrontendUser(), requestContext.App); |
|||
|
|||
var source = PublishedContent(); |
|||
|
|||
await sut.EnrichAsync(requestContext, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.Equal("my-schema", source.SchemaName); |
|||
Assert.Equal("my-schema", source.SchemaDisplayName); |
|||
} |
|||
|
|||
private ContentEntity PublishedContent() |
|||
{ |
|||
return new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,109 @@ |
|||
// ==========================================================================
|
|||
// 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.Domain.Apps.Entities.Contents.Queries.Steps; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|||
{ |
|||
public class EnrichWithWorkflowsTests |
|||
{ |
|||
private readonly IContentWorkflow contentWorkflow = A.Fake<IContentWorkflow>(); |
|||
private readonly IContentQueryService contentQuery = A.Fake<IContentQueryService>(); |
|||
private readonly ISchemaEntity schema; |
|||
private readonly Context requestContext; |
|||
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
|||
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
|||
private readonly ProvideSchema schemaProvider; |
|||
private readonly EnrichWithWorkflows sut; |
|||
|
|||
public EnrichWithWorkflowsTests() |
|||
{ |
|||
requestContext = new Context(Mocks.ApiUser(), Mocks.App(appId)); |
|||
|
|||
schema = Mocks.Schema(appId, schemaId); |
|||
schemaProvider = x => Task.FromResult(schema); |
|||
|
|||
A.CallTo(() => contentQuery.GetSchemaOrThrowAsync(A<Context>.Ignored, schemaId.Id.ToString())) |
|||
.Returns(schema); |
|||
|
|||
sut = new EnrichWithWorkflows(contentWorkflow); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_enrich_content_with_status_color() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
A.CallTo(() => contentWorkflow.GetInfoAsync(source)) |
|||
.Returns(new StatusInfo(Status.Published, StatusColors.Published)); |
|||
|
|||
await sut.EnrichAsync(requestContext, new[] { source }, schemaProvider); |
|||
|
|||
Assert.Equal(StatusColors.Published, source.StatusColor); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_enrich_content_with_default_color_if_not_found() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
A.CallTo(() => contentWorkflow.GetInfoAsync(source)) |
|||
.Returns(Task.FromResult<StatusInfo>(null!)); |
|||
|
|||
var ctx = requestContext.WithResolveFlow(true); |
|||
|
|||
await sut.EnrichAsync(ctx, new[] { source }, schemaProvider); |
|||
|
|||
Assert.Equal(StatusColors.Draft, source.StatusColor); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_enrich_content_with_can_update() |
|||
{ |
|||
var source = new ContentEntity { SchemaId = schemaId }; |
|||
|
|||
A.CallTo(() => contentWorkflow.CanUpdateAsync(source, requestContext.User)) |
|||
.Returns(true); |
|||
|
|||
var ctx = requestContext.WithResolveFlow(true); |
|||
|
|||
await sut.EnrichAsync(ctx, new[] { source }, schemaProvider); |
|||
|
|||
Assert.True(source.CanUpdate); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_enrich_content_with_can_update_if_disabled_in_context() |
|||
{ |
|||
requestContext.WithResolveFlow(false); |
|||
|
|||
var source = new ContentEntity { SchemaId = schemaId }; |
|||
|
|||
var ctx = requestContext.WithResolveFlow(false); |
|||
|
|||
await sut.EnrichAsync(ctx, new[] { source }, schemaProvider); |
|||
|
|||
Assert.False(source.CanUpdate); |
|||
|
|||
A.CallTo(() => contentWorkflow.CanUpdateAsync(source, requestContext.User)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
private ContentEntity PublishedContent() |
|||
{ |
|||
return new ContentEntity { Status = Status.Published, SchemaId = schemaId }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
// ==========================================================================
|
|||
// 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.ClientLibrary.Management; |
|||
using TestSuite.Fixtures; |
|||
using TestSuite.Model; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable SA1300 // Element should begin with upper-case letter
|
|||
#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row
|
|||
|
|||
namespace TestSuite.ApiTests |
|||
{ |
|||
public class ContentCleanupTests : IClassFixture<ClientFixture> |
|||
{ |
|||
public ClientFixture _ { get; } |
|||
|
|||
public ContentCleanupTests(ClientFixture fixture) |
|||
{ |
|||
_ = fixture; |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_cleanup_old_data_from_update_response() |
|||
{ |
|||
var schemaName = $"schema-{DateTime.UtcNow.Ticks}"; |
|||
|
|||
// STEP 1: Create a schema.
|
|||
var schema = await _.Schemas.PostSchemaAsync(_.AppName, new CreateSchemaDto |
|||
{ |
|||
Name = schemaName, |
|||
Fields = new List<UpsertSchemaFieldDto> |
|||
{ |
|||
new UpsertSchemaFieldDto |
|||
{ |
|||
Name = "number", |
|||
Properties = new NumberFieldPropertiesDto |
|||
{ |
|||
IsRequired = true |
|||
} |
|||
}, |
|||
new UpsertSchemaFieldDto |
|||
{ |
|||
Name = "string", |
|||
Properties = new StringFieldPropertiesDto |
|||
{ |
|||
IsRequired = false |
|||
} |
|||
} |
|||
}, |
|||
IsPublished = true |
|||
}); |
|||
|
|||
var contents = _.ClientManager.GetClient<TestEntity, TestEntityData>(schemaName); |
|||
|
|||
// STEP 2: Create a content for this schema.
|
|||
var data = new TestEntityData { Number = 12, String = "hello" }; |
|||
|
|||
var content_1 = await contents.CreateAsync(data); |
|||
|
|||
Assert.Equal(data.String, content_1.DataDraft.String); |
|||
|
|||
|
|||
// STEP 3: Delete a field from schema.
|
|||
await _.Schemas.DeleteFieldAsync(_.AppName, schema.Name, schema.Fields.ElementAt(1).FieldId); |
|||
|
|||
|
|||
// STEP 4: Make any update.
|
|||
var content_2 = await contents.ChangeStatusAsync(content_1.Id, "Published"); |
|||
|
|||
// Should not return deleted field.
|
|||
Assert.Null(content_2.DataDraft.String); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue