mirror of https://github.com/Squidex/squidex.git
Browse Source
* Splitted the enrichment in several steps.
* Get rid of unused content.
* Improved value converter.
* Content enrichment improvements.
* Naming improved.
* Auto implementation reverted.
* Tests fixed.
* Index fixes.
* FIx to queries.
* Cleanup.
* Revert "Cleanup."
This reverts commit 4b59ce937c.
* Tests fixed.
pull/481/head
committed by
GitHub
77 changed files with 1848 additions and 1421 deletions
@ -1,52 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents |
|||
{ |
|||
public partial class MongoContentRepository : IEventConsumer |
|||
{ |
|||
public string Name |
|||
{ |
|||
get { return GetType().Name; } |
|||
} |
|||
|
|||
public string EventsFilter |
|||
{ |
|||
get { return "^(content-)|(asset-)"; } |
|||
} |
|||
|
|||
public bool Handles(StoredEvent @event) |
|||
{ |
|||
return @event.Data.Type == typeAssetDeleted || @event.Data.Type == typeContentDeleted; |
|||
} |
|||
|
|||
public Task On(Envelope<IEvent> @event) |
|||
{ |
|||
switch (@event.Payload) |
|||
{ |
|||
case AssetDeleted e: |
|||
return cleanupReferences.DoAsync(e.AssetId); |
|||
|
|||
case ContentDeleted e: |
|||
return cleanupReferences.DoAsync(e.ContentId); |
|||
} |
|||
|
|||
return TaskHelper.Done; |
|||
} |
|||
|
|||
Task IEventConsumer.ClearAsync() |
|||
{ |
|||
return TaskHelper.Done; |
|||
} |
|||
} |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
|||
{ |
|||
internal sealed class CleanupReferences : OperationBase |
|||
{ |
|||
protected override Task PrepareAsync(CancellationToken ct = default) |
|||
{ |
|||
var index = |
|||
new CreateIndexModel<MongoContentEntity>( |
|||
Index.Ascending(x => x.ReferencedIds)); |
|||
|
|||
return Collection.Indexes.CreateOneAsync(index, cancellationToken: ct); |
|||
} |
|||
|
|||
public Task DoAsync(Guid id) |
|||
{ |
|||
return Collection.UpdateManyAsync( |
|||
Filter.And( |
|||
Filter.AnyEq(x => x.ReferencedIds, id), |
|||
Filter.AnyNe(x => x.ReferencedIdsDeleted, id)), |
|||
Update.AddToSet(x => x.ReferencedIdsDeleted, id)); |
|||
} |
|||
} |
|||
} |
|||
@ -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,158 @@ |
|||
// ==========================================================================
|
|||
// 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.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.ExtractReferenceIds; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries.Steps |
|||
{ |
|||
public sealed class ConvertData : IContentEnricherStep |
|||
{ |
|||
private readonly IAssetUrlGenerator assetUrlGenerator; |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly IContentRepository contentRepository; |
|||
|
|||
public ConvertData(IAssetUrlGenerator assetUrlGenerator, IAssetRepository assetRepository, IContentRepository contentRepository) |
|||
{ |
|||
Guard.NotNull(assetUrlGenerator); |
|||
Guard.NotNull(assetRepository); |
|||
Guard.NotNull(contentRepository); |
|||
|
|||
this.assetUrlGenerator = assetUrlGenerator; |
|||
this.assetRepository = assetRepository; |
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
public async Task EnrichAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
var resolveDataDraft = context.ShouldProvideUnpublished() || context.IsFrontendClient; |
|||
|
|||
var referenceCleaner = await CleanReferencesAsync(context, contents, schemas); |
|||
|
|||
var converters = GenerateConverters(context, referenceCleaner).ToArray(); |
|||
|
|||
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 async Task<ValueConverter?> CleanReferencesAsync(Context context, IEnumerable<ContentEntity> contents, ProvideSchema schemas) |
|||
{ |
|||
if (context.ShouldCleanup()) |
|||
{ |
|||
var ids = new HashSet<Guid>(); |
|||
|
|||
foreach (var group in contents.GroupBy(x => x.SchemaId.Id)) |
|||
{ |
|||
var schema = await schemas(group.Key); |
|||
|
|||
foreach (var content in group) |
|||
{ |
|||
content.Data?.AddReferencedIds(schema.SchemaDef, ids); |
|||
content.DataDraft?.AddReferencedIds(schema.SchemaDef, ids); |
|||
} |
|||
} |
|||
|
|||
if (ids.Count > 0) |
|||
{ |
|||
var taskForAssets = QueryAssetIdsAsync(context, ids); |
|||
var taskForContents = QueryContentIdsAsync(context, ids); |
|||
|
|||
await Task.WhenAll(taskForAssets, taskForContents); |
|||
|
|||
var foundIds = new HashSet<Guid>(taskForAssets.Result.Union(taskForContents.Result)); |
|||
|
|||
return ValueReferencesConverter.CleanReferences(foundIds); |
|||
} |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
private async Task<IEnumerable<Guid>> QueryContentIdsAsync(Context context, HashSet<Guid> ids) |
|||
{ |
|||
var result = await contentRepository.QueryIdsAsync(context.App.Id, ids); |
|||
|
|||
return result.Select(x => x.Id); |
|||
} |
|||
|
|||
private async Task<IEnumerable<Guid>> QueryAssetIdsAsync(Context context, HashSet<Guid> ids) |
|||
{ |
|||
var result = await assetRepository.QueryIdsAsync(context.App.Id, ids); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
private IEnumerable<FieldConverter> GenerateConverters(Context context, ValueConverter? cleanReferences) |
|||
{ |
|||
if (!context.IsFrontendClient) |
|||
{ |
|||
yield return FieldConverters.ExcludeHidden(); |
|||
yield return FieldConverters.ForNestedName2Name(ValueConverters.ExcludeHidden()); |
|||
} |
|||
|
|||
yield return FieldConverters.ExcludeChangedTypes(); |
|||
yield return FieldConverters.ForNestedName2Name(ValueConverters.ExcludeChangedTypes()); |
|||
|
|||
if (cleanReferences != null) |
|||
{ |
|||
yield return FieldConverters.ForValues(cleanReferences); |
|||
yield return FieldConverters.ForNestedName2Name(cleanReferences); |
|||
} |
|||
|
|||
yield return FieldConverters.ResolveInvariant(context.App.LanguagesConfig); |
|||
yield return FieldConverters.ResolveLanguages(context.App.LanguagesConfig); |
|||
|
|||
if (!context.IsFrontendClient) |
|||
{ |
|||
if (context.ShouldResolveLanguages()) |
|||
{ |
|||
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.ShouldResolveFlow(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
// ==========================================================================
|
|||
// 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) |
|||
{ |
|||
var temp = new HashSet<Guid>(); |
|||
|
|||
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) |
|||
.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().WithoutAssetEnrichment(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) |
|||
{ |
|||
content.DataDraft.AddReferencedIds(schema.SchemaDef.ResolvingAssets(), ids); |
|||
} |
|||
} |
|||
|
|||
private static bool ShouldEnrich(Context context) |
|||
{ |
|||
return context.IsFrontendClient && context.ShouldEnrichContent(); |
|||
} |
|||
} |
|||
} |
|||
@ -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) |
|||
.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) |
|||
{ |
|||
content.DataDraft.AddReferencedIds(schema.SchemaDef.ResolvingReferences(), ids); |
|||
} |
|||
} |
|||
|
|||
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.ShouldEnrichContent(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using FakeItEasy; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.TestHelpers |
|||
{ |
|||
public static class AExtensions |
|||
{ |
|||
public static T[] Is<T>(this INegatableArgumentConstraintManager<T[]> that, params T[]? values) |
|||
{ |
|||
return values == null ? that.IsNull() : that.IsSameSequenceAs(values); |
|||
} |
|||
|
|||
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, IEnumerable<T>? values) |
|||
{ |
|||
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Count()); |
|||
} |
|||
|
|||
public static HashSet<T> Is<T>(this INegatableArgumentConstraintManager<HashSet<T>> that, params T[]? values) |
|||
{ |
|||
return values == null ? that.IsNull() : that.Matches(x => x.Intersect(values).Count() == values.Length); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,146 @@ |
|||
// ==========================================================================
|
|||
// 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 FakeItEasy; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Queries.Steps; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents.Queries |
|||
{ |
|||
public class ConvertDataTests |
|||
{ |
|||
private readonly ISchemaEntity schema; |
|||
private readonly IAssetUrlGenerator assetUrlGenerator = A.Fake<IAssetUrlGenerator>(); |
|||
private readonly IAssetRepository assetRepository = A.Fake<IAssetRepository>(); |
|||
private readonly IContentRepository contentRepository = A.Fake<IContentRepository>(); |
|||
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 ConvertData sut; |
|||
|
|||
public ConvertDataTests() |
|||
{ |
|||
requestContext = new Context(Mocks.ApiUser(), Mocks.App(appId)); |
|||
|
|||
var schemaDef = |
|||
new Schema("my-schema") |
|||
.AddReferences(1, "references", Partitioning.Invariant) |
|||
.AddAssets(2, "assets", Partitioning.Invariant) |
|||
.AddArray(3, "array", Partitioning.Invariant, a => a |
|||
.AddAssets(31, "nested")); |
|||
|
|||
schema = Mocks.Schema(appId, schemaId, schemaDef); |
|||
schemaProvider = x => Task.FromResult(schema); |
|||
|
|||
sut = new ConvertData(assetUrlGenerator, assetRepository, contentRepository); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_convert_data_only() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
await sut.EnrichAsync(requestContext, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.NotNull(source.Data); |
|||
Assert.Null(source.DataDraft); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_convert_data_and_data_draft_when_frontend_user() |
|||
{ |
|||
var source = PublishedContent(); |
|||
|
|||
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)); |
|||
|
|||
await sut.EnrichAsync(ctx, Enumerable.Repeat(source, 1), schemaProvider); |
|||
|
|||
Assert.NotNull(source.Data); |
|||
Assert.NotNull(source.DataDraft); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_cleanup_references() |
|||
{ |
|||
var id1 = Guid.NewGuid(); |
|||
var id2 = Guid.NewGuid(); |
|||
|
|||
var source = |
|||
new NamedContentData() |
|||
.AddField("references", |
|||
new ContentFieldData() |
|||
.AddJsonValue(JsonValue.Array(id1, id2))) |
|||
.AddField("assets", |
|||
new ContentFieldData() |
|||
.AddJsonValue(JsonValue.Array(id1))) |
|||
.AddField("array", |
|||
new ContentFieldData() |
|||
.AddJsonValue( |
|||
JsonValue.Array( |
|||
JsonValue.Object() |
|||
.Add("nested", JsonValue.Array(id1, id2))))); |
|||
|
|||
var expected = |
|||
new NamedContentData() |
|||
.AddField("references", |
|||
new ContentFieldData() |
|||
.AddJsonValue(JsonValue.Array(id2))) |
|||
.AddField("assets", |
|||
new ContentFieldData() |
|||
.AddJsonValue(JsonValue.Array())) |
|||
.AddField("array", |
|||
new ContentFieldData() |
|||
.AddJsonValue( |
|||
JsonValue.Array( |
|||
JsonValue.Object() |
|||
.Add("nested", JsonValue.Array(id2))))); |
|||
var content = PublishedContent(); |
|||
|
|||
content.Data = source; |
|||
content.DataDraft = source; |
|||
|
|||
A.CallTo(() => assetRepository.QueryIdsAsync(appId.Id, A<HashSet<Guid>>.That.Is(id1, id2))) |
|||
.Returns(new List<Guid> { id2 }); |
|||
|
|||
A.CallTo(() => contentRepository.QueryIdsAsync(appId.Id, A<HashSet<Guid>>.That.Is(id1, id2))) |
|||
.Returns(new List<(Guid, Guid)> { (id2, id2) }); |
|||
|
|||
var ctx = new Context(Mocks.FrontendUser(), Mocks.App(appId)); |
|||
|
|||
await sut.EnrichAsync(ctx, Enumerable.Repeat(content, 1), schemaProvider); |
|||
|
|||
Assert.Equal(expected, content.Data); |
|||
Assert.Equal(expected, content.DataDraft); |
|||
} |
|||
|
|||
private ContentEntity PublishedContent() |
|||
{ |
|||
return new ContentEntity |
|||
{ |
|||
Status = Status.Published, |
|||
Data = new NamedContentData(), |
|||
DataDraft = new NamedContentData(), |
|||
SchemaId = schemaId |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -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.Data.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.Data.String); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue