mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
26 changed files with 1273 additions and 733 deletions
@ -0,0 +1,74 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ConvertContent |
|||
{ |
|||
public static class ContentConverterFlat |
|||
{ |
|||
public static object ToFlatLanguageModel(this NamedContentData content, LanguagesConfig languagesConfig, IReadOnlyCollection<Language> languagePreferences = null) |
|||
{ |
|||
Guard.NotNull(languagesConfig, nameof(languagesConfig)); |
|||
|
|||
if (languagePreferences == null || languagePreferences.Count == 0) |
|||
{ |
|||
return content; |
|||
} |
|||
|
|||
if (languagePreferences.Count == 1 && languagesConfig.TryGetConfig(languagePreferences.First(), out var languageConfig)) |
|||
{ |
|||
languagePreferences = languagePreferences.Union(languageConfig.LanguageFallbacks).ToList(); |
|||
} |
|||
|
|||
var result = new Dictionary<string, JToken>(); |
|||
|
|||
foreach (var fieldValue in content) |
|||
{ |
|||
var fieldData = fieldValue.Value; |
|||
|
|||
foreach (var language in languagePreferences) |
|||
{ |
|||
if (fieldData.TryGetValue(language, out var value) && value != null) |
|||
{ |
|||
result[fieldValue.Key] = value; |
|||
|
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public static Dictionary<string, object> ToFlatten(this NamedContentData content) |
|||
{ |
|||
var result = new Dictionary<string, object>(); |
|||
|
|||
foreach (var fieldValue in content) |
|||
{ |
|||
var fieldData = fieldValue.Value; |
|||
|
|||
if (fieldData.Count == 1) |
|||
{ |
|||
result[fieldValue.Key] = fieldData.Values.First(); |
|||
} |
|||
else |
|||
{ |
|||
result[fieldValue.Key] = fieldData; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,265 @@ |
|||
// ==========================================================================
|
|||
// 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.Text; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.ValidateContent; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
#pragma warning disable RECS0002 // Convert anonymous method to method group
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.ConvertContent |
|||
{ |
|||
public static class FieldConverters |
|||
{ |
|||
public static FieldConverter ExcludeHidden() |
|||
{ |
|||
return (data, field) => |
|||
{ |
|||
return field.IsHidden ? null : data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter ExcludeChangedTypes() |
|||
{ |
|||
return (data, field) => |
|||
{ |
|||
var isValid = true; |
|||
|
|||
foreach (var value in data.Values) |
|||
{ |
|||
try |
|||
{ |
|||
if (!value.IsNull()) |
|||
{ |
|||
JsonValueConverter.ConvertValue(field, value); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
isValid = false; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (!isValid) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter ResolveInvariant(LanguagesConfig languagesConfig) |
|||
{ |
|||
var codeForInvariant = InvariantPartitioning.Instance.Master.Key; |
|||
var codeForMasterLanguage = languagesConfig.Master.Language.Iso2Code; |
|||
|
|||
return (data, field) => |
|||
{ |
|||
if (field.Partitioning.Equals(Partitioning.Invariant)) |
|||
{ |
|||
var result = new ContentFieldData(); |
|||
|
|||
if (data.TryGetValue(codeForInvariant, out var value)) |
|||
{ |
|||
result[codeForInvariant] = value; |
|||
} |
|||
else if (data.TryGetValue(codeForMasterLanguage, out value)) |
|||
{ |
|||
result[codeForInvariant] = value; |
|||
} |
|||
else if (data.Count > 0) |
|||
{ |
|||
result[codeForInvariant] = data.Values.First(); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter ResolveLanguages(LanguagesConfig languagesConfig) |
|||
{ |
|||
var codeForInvariant = InvariantPartitioning.Instance.Master.Key; |
|||
var codeForMasterLanguage = languagesConfig.Master.Language.Iso2Code; |
|||
|
|||
return (data, field) => |
|||
{ |
|||
if (field.Partitioning.Equals(Partitioning.Language)) |
|||
{ |
|||
var result = new ContentFieldData(); |
|||
|
|||
foreach (var languageConfig in languagesConfig) |
|||
{ |
|||
var languageCode = languageConfig.Key; |
|||
|
|||
if (data.TryGetValue(languageCode, out var value)) |
|||
{ |
|||
result[languageCode] = value; |
|||
} |
|||
else if (languageConfig == languagesConfig.Master && data.TryGetValue(codeForInvariant, out value)) |
|||
{ |
|||
result[languageCode] = value; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter ResolveFallbackLanguages(LanguagesConfig languagesConfig) |
|||
{ |
|||
var master = languagesConfig.Master; |
|||
|
|||
return (data, field) => |
|||
{ |
|||
if (field.Partitioning.Equals(Partitioning.Language)) |
|||
{ |
|||
foreach (var languageConfig in languagesConfig) |
|||
{ |
|||
var languageCode = languageConfig.Key; |
|||
|
|||
if (!data.TryGetValue(languageCode, out var value)) |
|||
{ |
|||
var dataFound = false; |
|||
|
|||
foreach (var fallback in languageConfig.Fallback) |
|||
{ |
|||
if (data.TryGetValue(fallback, out value)) |
|||
{ |
|||
data[languageCode] = value; |
|||
dataFound = true; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (!dataFound && languageConfig != master) |
|||
{ |
|||
if (data.TryGetValue(master.Language, out value)) |
|||
{ |
|||
data[languageCode] = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter FilterLanguages(LanguagesConfig languagesConfig, IEnumerable<Language> languages) |
|||
{ |
|||
if (languages == null) |
|||
{ |
|||
return (data, field) => data; |
|||
} |
|||
|
|||
var languageCodes = |
|||
new HashSet<string>( |
|||
languages.Select(x => x.Iso2Code).Where(x => languagesConfig.Contains(x)), |
|||
StringComparer.OrdinalIgnoreCase); |
|||
|
|||
if (languageCodes.Count == 0) |
|||
{ |
|||
return (data, field) => data; |
|||
} |
|||
|
|||
return (data, field) => |
|||
{ |
|||
if (field.Partitioning.Equals(Partitioning.Language)) |
|||
{ |
|||
var result = new ContentFieldData(); |
|||
|
|||
foreach (var languageCode in languageCodes) |
|||
{ |
|||
if (data.TryGetValue(languageCode, out var value)) |
|||
{ |
|||
result[languageCode] = value; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter DecodeJson() |
|||
{ |
|||
return (data, field) => |
|||
{ |
|||
if (field is JsonField) |
|||
{ |
|||
var result = new ContentFieldData(); |
|||
|
|||
foreach (var partitionValue in data) |
|||
{ |
|||
if (partitionValue.Value.IsNull()) |
|||
{ |
|||
result[partitionValue.Key] = null; |
|||
} |
|||
else |
|||
{ |
|||
var value = Encoding.UTF8.GetString(Convert.FromBase64String(partitionValue.Value.ToString())); |
|||
|
|||
result[partitionValue.Key] = JToken.Parse(value); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
|
|||
public static FieldConverter EncodeJson() |
|||
{ |
|||
return (data, field) => |
|||
{ |
|||
if (field is JsonField) |
|||
{ |
|||
var result = new ContentFieldData(); |
|||
|
|||
foreach (var partitionValue in data) |
|||
{ |
|||
if (partitionValue.Value.IsNull()) |
|||
{ |
|||
result[partitionValue.Key] = null; |
|||
} |
|||
else |
|||
{ |
|||
var value = Convert.ToBase64String(Encoding.UTF8.GetBytes(partitionValue.Value.ToString())); |
|||
|
|||
result[partitionValue.Key] = value; |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
// ==========================================================================
|
|||
// 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 Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ExtractReferenceIds |
|||
{ |
|||
public static class FieldReferencesConverter |
|||
{ |
|||
public static FieldConverter CleanReferences(IEnumerable<Guid> deletedReferencedIds) |
|||
{ |
|||
var ids = new HashSet<Guid>(deletedReferencedIds); |
|||
|
|||
return (data, field) => |
|||
{ |
|||
foreach (var partitionValue in data.Where(x => !x.Value.IsNull()).ToList()) |
|||
{ |
|||
var newValue = field.CleanReferences(partitionValue.Value, ids); |
|||
|
|||
data[partitionValue.Key] = newValue; |
|||
} |
|||
|
|||
return data; |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -1,145 +1,83 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Assets; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Reflection; |
|||
using Squidex.Infrastructure.Security; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class QueryContext |
|||
public sealed class QueryContext : Cloneable<QueryContext> |
|||
{ |
|||
private readonly ConcurrentDictionary<Guid, IContentEntity> cachedContents = new ConcurrentDictionary<Guid, IContentEntity>(); |
|||
private readonly ConcurrentDictionary<Guid, IAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IAssetEntity>(); |
|||
private readonly IContentQueryService contentQuery; |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly IAppEntity app; |
|||
private readonly ClaimsPrincipal user; |
|||
|
|||
public QueryContext( |
|||
IAppEntity app, |
|||
IAssetRepository assetRepository, |
|||
IContentQueryService contentQuery, |
|||
ClaimsPrincipal user) |
|||
{ |
|||
Guard.NotNull(assetRepository, nameof(assetRepository)); |
|||
Guard.NotNull(contentQuery, nameof(contentQuery)); |
|||
Guard.NotNull(app, nameof(app)); |
|||
Guard.NotNull(user, nameof(user)); |
|||
public ClaimsPrincipal User { get; private set; } |
|||
|
|||
this.assetRepository = assetRepository; |
|||
this.contentQuery = contentQuery; |
|||
public IAppEntity App { get; private set; } |
|||
|
|||
this.user = user; |
|||
public IEnumerable<Language> Languages { get; private set; } |
|||
|
|||
this.app = app; |
|||
} |
|||
public string SchemaIdOrName { get; private set; } |
|||
|
|||
public async Task<IAssetEntity> FindAssetAsync(Guid id) |
|||
{ |
|||
var asset = cachedAssets.GetOrDefault(id); |
|||
|
|||
if (asset == null) |
|||
{ |
|||
asset = await assetRepository.FindAssetAsync(id); |
|||
public bool Archived { get; private set; } |
|||
|
|||
if (asset != null) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
} |
|||
public bool Flatten { get; private set; } |
|||
|
|||
return asset; |
|||
private QueryContext() |
|||
{ |
|||
} |
|||
|
|||
public async Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id) |
|||
public static QueryContext Create(IAppEntity app, ClaimsPrincipal user, IEnumerable<string> languageCodes = null) |
|||
{ |
|||
var content = cachedContents.GetOrDefault(id); |
|||
var result = new QueryContext { App = app, User = user }; |
|||
|
|||
if (content == null) |
|||
if (languageCodes != null) |
|||
{ |
|||
content = await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id); |
|||
var languages = new List<Language>(); |
|||
|
|||
if (content != null) |
|||
foreach (var iso2Code in languageCodes) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
if (Language.TryGetLanguage(iso2Code, out var language)) |
|||
{ |
|||
languages.Add(language); |
|||
} |
|||
} |
|||
|
|||
result.Languages = languages; |
|||
} |
|||
|
|||
return content; |
|||
return result; |
|||
} |
|||
|
|||
public async Task<IResultList<IAssetEntity>> QueryAssetsAsync(string query) |
|||
public QueryContext WithArchived(bool archived) |
|||
{ |
|||
var assets = await assetRepository.QueryAsync(app.Id, query); |
|||
|
|||
foreach (var asset in assets) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
|
|||
return assets; |
|||
return Clone(c => c.Archived = archived); |
|||
} |
|||
|
|||
public async Task<IResultList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query) |
|||
public QueryContext WithFlatten(bool flatten) |
|||
{ |
|||
var result = await contentQuery.QueryAsync(app, schemaIdOrName, user, false, query); |
|||
|
|||
foreach (var content in result) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
} |
|||
|
|||
return result; |
|||
return Clone(c => c.Flatten = flatten); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids) |
|||
public QueryContext WithSchemaName(string name) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
var notLoadedAssets = new HashSet<Guid>(ids.Where(id => !cachedAssets.ContainsKey(id))); |
|||
|
|||
if (notLoadedAssets.Count > 0) |
|||
{ |
|||
var assets = await assetRepository.QueryAsync(app.Id, notLoadedAssets); |
|||
|
|||
foreach (var asset in assets) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
} |
|||
|
|||
return ids.Select(cachedAssets.GetOrDefault).Where(x => x != null).ToList(); |
|||
return Clone(c => c.SchemaIdOrName = name); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, ICollection<Guid> ids) |
|||
public QueryContext WithSchemaId(Guid id) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
var notLoadedContents = new HashSet<Guid>(ids.Where(id => !cachedContents.ContainsKey(id))); |
|||
|
|||
if (notLoadedContents.Count > 0) |
|||
{ |
|||
var result = await contentQuery.QueryAsync(app, schemaId.ToString(), user, false, notLoadedContents); |
|||
|
|||
foreach (var content in result) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
} |
|||
} |
|||
return Clone(c => c.SchemaIdOrName = id.ToString()); |
|||
} |
|||
|
|||
return ids.Select(cachedContents.GetOrDefault).Where(x => x != null).ToList(); |
|||
public bool IsFrontendClient |
|||
{ |
|||
get { return User.IsInClient("squidex-frontend"); } |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,136 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Assets; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class QueryExecutionContext |
|||
{ |
|||
private readonly ConcurrentDictionary<Guid, IContentEntity> cachedContents = new ConcurrentDictionary<Guid, IContentEntity>(); |
|||
private readonly ConcurrentDictionary<Guid, IAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IAssetEntity>(); |
|||
private readonly IContentQueryService contentQuery; |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly QueryContext context; |
|||
|
|||
public QueryExecutionContext(QueryContext context, |
|||
IAssetRepository assetRepository, |
|||
IContentQueryService contentQuery) |
|||
{ |
|||
Guard.NotNull(assetRepository, nameof(assetRepository)); |
|||
Guard.NotNull(contentQuery, nameof(contentQuery)); |
|||
Guard.NotNull(context, nameof(context)); |
|||
|
|||
this.assetRepository = assetRepository; |
|||
this.contentQuery = contentQuery; |
|||
this.context = context; |
|||
} |
|||
|
|||
public async Task<IAssetEntity> FindAssetAsync(Guid id) |
|||
{ |
|||
var asset = cachedAssets.GetOrDefault(id); |
|||
|
|||
if (asset == null) |
|||
{ |
|||
asset = await assetRepository.FindAssetAsync(id); |
|||
|
|||
if (asset != null) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
} |
|||
|
|||
return asset; |
|||
} |
|||
|
|||
public async Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id) |
|||
{ |
|||
var content = cachedContents.GetOrDefault(id); |
|||
|
|||
if (content == null) |
|||
{ |
|||
content = await contentQuery.FindContentAsync(context.WithSchemaId(schemaId), id); |
|||
|
|||
if (content != null) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
} |
|||
} |
|||
|
|||
return content; |
|||
} |
|||
|
|||
public async Task<IResultList<IAssetEntity>> QueryAssetsAsync(string query) |
|||
{ |
|||
var assets = await assetRepository.QueryAsync(context.App.Id, query); |
|||
|
|||
foreach (var asset in assets) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
|
|||
return assets; |
|||
} |
|||
|
|||
public async Task<IResultList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query) |
|||
{ |
|||
var result = await contentQuery.QueryAsync(context.WithSchemaName(schemaIdOrName), query); |
|||
|
|||
foreach (var content in result) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
var notLoadedAssets = new HashSet<Guid>(ids.Where(id => !cachedAssets.ContainsKey(id))); |
|||
|
|||
if (notLoadedAssets.Count > 0) |
|||
{ |
|||
var assets = await assetRepository.QueryAsync(context.App.Id, notLoadedAssets); |
|||
|
|||
foreach (var asset in assets) |
|||
{ |
|||
cachedAssets[asset.Id] = asset; |
|||
} |
|||
} |
|||
|
|||
return ids.Select(cachedAssets.GetOrDefault).Where(x => x != null).ToList(); |
|||
} |
|||
|
|||
public async Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, ICollection<Guid> ids) |
|||
{ |
|||
Guard.NotNull(ids, nameof(ids)); |
|||
|
|||
var notLoadedContents = new HashSet<Guid>(ids.Where(id => !cachedContents.ContainsKey(id))); |
|||
|
|||
if (notLoadedContents.Count > 0) |
|||
{ |
|||
var result = await contentQuery.QueryAsync(context.WithSchemaId(schemaId), notLoadedContents); |
|||
|
|||
foreach (var content in result) |
|||
{ |
|||
cachedContents[content.Id] = content; |
|||
} |
|||
} |
|||
|
|||
return ids.Select(cachedContents.GetOrDefault).Where(x => x != null).ToList(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,153 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
#pragma warning disable xUnit2013 // Do not use equality check to check for collection size.
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Operations.ConvertContent |
|||
{ |
|||
public class ContentConversionFlatTests |
|||
{ |
|||
private readonly Schema schema; |
|||
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE); |
|||
|
|||
public ContentConversionFlatTests() |
|||
{ |
|||
schema = |
|||
new Schema("my-schema") |
|||
.AddField(new NumberField(1, "field1", Partitioning.Language)) |
|||
.AddField(new NumberField(2, "field2", Partitioning.Invariant)) |
|||
.AddField(new NumberField(3, "field3", Partitioning.Invariant)) |
|||
.AddField(new AssetsField(5, "assets1", Partitioning.Invariant)) |
|||
.AddField(new AssetsField(6, "assets2", Partitioning.Invariant)) |
|||
.AddField(new JsonField(4, "json", Partitioning.Language)) |
|||
.HideField(3); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_original_when_no_language_preferences_defined() |
|||
{ |
|||
var data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 1)); |
|||
|
|||
Assert.Same(data, data.ToFlatLanguageModel(languagesConfig)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_flatten_value() |
|||
{ |
|||
var data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("de", 1) |
|||
.AddValue("en", 2)) |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddValue("de", null) |
|||
.AddValue("en", 4)) |
|||
.AddField("field3", |
|||
new ContentFieldData() |
|||
.AddValue("en", 6)) |
|||
.AddField("field4", |
|||
new ContentFieldData() |
|||
.AddValue("it", 7)); |
|||
|
|||
var output = data.ToFlatten(); |
|||
|
|||
var expected = new Dictionary<string, object> |
|||
{ |
|||
{ "field1", new ContentFieldData().AddValue("de", 1).AddValue("en", 2) }, |
|||
{ "field2", new ContentFieldData().AddValue("de", null).AddValue("en", 4) }, |
|||
{ "field3", (JValue)6 }, |
|||
{ "field4", (JValue)7 } |
|||
}; |
|||
|
|||
Assert.True(expected.EqualsDictionary(output)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_flat_list_when_single_languages_specified() |
|||
{ |
|||
var data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("de", 1) |
|||
.AddValue("en", 2)) |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddValue("de", null) |
|||
.AddValue("en", 4)) |
|||
.AddField("field3", |
|||
new ContentFieldData() |
|||
.AddValue("en", 6)) |
|||
.AddField("field4", |
|||
new ContentFieldData() |
|||
.AddValue("it", 7)); |
|||
|
|||
var fallbackConfig = |
|||
LanguagesConfig.Build( |
|||
new LanguageConfig(Language.EN), |
|||
new LanguageConfig(Language.DE, false, Language.EN)); |
|||
|
|||
var output = (Dictionary<string, JToken>)data.ToFlatLanguageModel(fallbackConfig, new List<Language> { Language.DE }); |
|||
|
|||
var expected = new Dictionary<string, JToken> |
|||
{ |
|||
{ "field1", 1 }, |
|||
{ "field2", 4 }, |
|||
{ "field3", 6 } |
|||
}; |
|||
|
|||
Assert.True(expected.EqualsDictionary(output)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_flat_list_when_languages_specified() |
|||
{ |
|||
var data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("de", 1) |
|||
.AddValue("en", 2)) |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddValue("de", null) |
|||
.AddValue("en", 4)) |
|||
.AddField("field3", |
|||
new ContentFieldData() |
|||
.AddValue("en", 6)) |
|||
.AddField("field4", |
|||
new ContentFieldData() |
|||
.AddValue("it", 7)); |
|||
|
|||
var output = (Dictionary<string, JToken>)data.ToFlatLanguageModel(languagesConfig, new List<Language> { Language.DE, Language.EN }); |
|||
|
|||
var expected = new Dictionary<string, JToken> |
|||
{ |
|||
{ "field1", 1 }, |
|||
{ "field2", 4 }, |
|||
{ "field3", 6 } |
|||
}; |
|||
|
|||
Assert.True(expected.EqualsDictionary(output)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,325 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Linq; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Operations.ConvertContent |
|||
{ |
|||
public class FieldConvertersTests |
|||
{ |
|||
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE); |
|||
private readonly StringField stringLanguageField = new StringField(1, "1", Partitioning.Language); |
|||
private readonly StringField stringInvariantField = new StringField(1, "1", Partitioning.Invariant); |
|||
private readonly NumberField numberField = new NumberField(1, "1", Partitioning.Invariant); |
|||
|
|||
[Fact] |
|||
public void Should_encode_json_values() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", null) |
|||
.AddValue("de", JToken.FromObject(new { Value = 1 })); |
|||
|
|||
var result = FieldConverters.EncodeJson()(source, new JsonField(1, "1", Partitioning.Invariant)); |
|||
|
|||
Assert.Null(result["en"]); |
|||
Assert.True(result["de"].Type == JTokenType.String); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_encoding_non_json_field() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", null); |
|||
|
|||
var result = FieldConverters.EncodeJson()(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_decode_json_values() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", null) |
|||
.AddValue("de", "e30="); |
|||
|
|||
var result = FieldConverters.DecodeJson()(source, new JsonField(1, "1", Partitioning.Invariant)); |
|||
|
|||
Assert.Null(result["en"]); |
|||
Assert.True(result["de"] is JObject); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_all_values_are_valid() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", null) |
|||
.AddValue("de", 1); |
|||
|
|||
var result = FieldConverters.ExcludeChangedTypes()(source, numberField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_null_values_if_all_value_is_invalid() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN") |
|||
.AddValue("de", 0); |
|||
|
|||
var result = FieldConverters.ExcludeChangedTypes()(source, numberField); |
|||
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_decoding_non_json_field() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", null); |
|||
|
|||
var result = FieldConverters.DecodeJson()(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_field_not_hidden() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.ExcludeHidden()(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_null_values_if_field_hidden() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.ExcludeHidden()(source, stringLanguageField.Hide()); |
|||
|
|||
Assert.Null(result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_resolve_languages_and_cleanup_old_languages() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN") |
|||
.AddValue("it", "IT"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN"); |
|||
|
|||
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, stringLanguageField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_resolve_languages_and_resolve_master_language_from_invariant() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("iv", "A") |
|||
.AddValue("it", "B"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("en", "A"); |
|||
|
|||
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, stringLanguageField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_resolving_languages_from_invariant_field() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.ResolveLanguages(languagesConfig)(source, stringInvariantField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_resolve_invariant_and_use_direct_value() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("iv", "A") |
|||
.AddValue("it", "B"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("iv", "A"); |
|||
|
|||
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, stringInvariantField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_resolve_invariant_and_resolve_invariant_from_master_language() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("de", "DE") |
|||
.AddValue("en", "EN"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("iv", "EN"); |
|||
|
|||
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, stringInvariantField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_resolve_invariant_and_resolve_invariant_from_first_language() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("de", "DE") |
|||
.AddValue("it", "IT"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("iv", "DE"); |
|||
|
|||
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, stringInvariantField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_resolving_invariant_from_language_field() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.ResolveInvariant(languagesConfig)(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_language_from_fallback_if_found() |
|||
{ |
|||
var config_1 = languagesConfig.Set(new LanguageConfig(Language.IT)); |
|||
var config_2 = config_1.Set(new LanguageConfig(Language.ES, false, Language.IT)); |
|||
|
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN") |
|||
.AddValue("it", "IT"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN") |
|||
.AddValue("de", "EN") |
|||
.AddValue("it", "IT") |
|||
.AddValue("es", "IT"); |
|||
|
|||
var result = FieldConverters.ResolveFallbackLanguages(config_2)(source, stringLanguageField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_not_return_value_if_master_is_missing() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("de", "DE"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("de", "DE"); |
|||
|
|||
var result = FieldConverters.ResolveFallbackLanguages(languagesConfig)(source, stringLanguageField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_filter_languages() |
|||
{ |
|||
var source = |
|||
new ContentFieldData() |
|||
.AddValue("en", "EN") |
|||
.AddValue("de", "DE"); |
|||
|
|||
var expected = |
|||
new ContentFieldData() |
|||
.AddValue("de", "DE"); |
|||
|
|||
var result = FieldConverters.FilterLanguages(languagesConfig, new[] { Language.DE })(source, stringLanguageField); |
|||
|
|||
Assert.Equal(expected, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_resolving_fallback_languages_from_invariant_field() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.ResolveFallbackLanguages(languagesConfig)(source, stringInvariantField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_filtered_languages_are_invalid() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.FilterLanguages(languagesConfig, new[] { Language.CA })(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_filtered_languages_is_empty() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.FilterLanguages(languagesConfig, Enumerable.Empty<Language>())(source, stringLanguageField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_return_same_values_if_filtering_languages_from_invariant_field() |
|||
{ |
|||
var source = new ContentFieldData(); |
|||
|
|||
var result = FieldConverters.FilterLanguages(languagesConfig, null)(source, stringInvariantField); |
|||
|
|||
Assert.Same(source, result); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue