Browse Source

More flexible partitioning

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
931b2502f5
  1. 28
      src/Squidex.Core/ContentEnricher.cs
  2. 12
      src/Squidex.Core/ContentExtensions.cs
  3. 69
      src/Squidex.Core/ContentValidator.cs
  4. 30
      src/Squidex.Core/Contents/ContentData.cs
  5. 8
      src/Squidex.Core/Contents/ContentFieldData.cs
  6. 14
      src/Squidex.Core/FieldExtensions.cs
  7. 18
      src/Squidex.Core/IFieldPartitionItem.cs
  8. 19
      src/Squidex.Core/IFieldPartitioning.cs
  9. 65
      src/Squidex.Core/InvariantPartitioning.cs
  10. 18
      src/Squidex.Core/LanguageConfig.cs
  11. 50
      src/Squidex.Core/LanguagesConfig.cs
  12. 45
      src/Squidex.Core/Partitioning.cs
  13. 9
      src/Squidex.Core/Schemas/AssetsField.cs
  14. 9
      src/Squidex.Core/Schemas/BooleanField.cs
  15. 9
      src/Squidex.Core/Schemas/DateTimeField.cs
  16. 70
      src/Squidex.Core/Schemas/Field.cs
  17. 12
      src/Squidex.Core/Schemas/FieldProperties.cs
  18. 81
      src/Squidex.Core/Schemas/FieldRegistry.cs
  19. 4
      src/Squidex.Core/Schemas/Field_Generic.cs
  20. 9
      src/Squidex.Core/Schemas/GeolocationField.cs
  21. 14
      src/Squidex.Core/Schemas/Json/JsonFieldModel.cs
  22. 5
      src/Squidex.Core/Schemas/Json/JsonSchemaModel.cs
  23. 5
      src/Squidex.Core/Schemas/Json/SchemaJsonSerializer.cs
  24. 9
      src/Squidex.Core/Schemas/JsonField.cs
  25. 9
      src/Squidex.Core/Schemas/NumberField.cs
  26. 12
      src/Squidex.Core/Schemas/Schema.cs
  27. 9
      src/Squidex.Core/Schemas/StringField.cs
  28. 2
      src/Squidex.Events/Schemas/FieldAdded.cs
  29. 14
      src/Squidex.Events/Schemas/Utils/SchemaEventDispatcher.cs
  30. 2
      src/Squidex.Infrastructure/Language.cs
  31. 7
      src/Squidex.Read.MongoDb/Apps/MongoAppEntity.cs
  32. 9
      src/Squidex.Read.MongoDb/Contents/MongoContentRepository.cs
  33. 2
      src/Squidex.Read/Apps/IAppEntity.cs
  34. 14
      src/Squidex.Read/Contents/Builders/EdmModelBuilder.cs
  35. 5
      src/Squidex.Read/Contents/Repositories/IContentRepository.cs
  36. 4
      src/Squidex.Write/Contents/ContentCommandHandler.cs
  37. 3
      src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs
  38. 2
      src/Squidex/Controllers/Api/Assets/AssetsController.cs
  39. 2
      src/Squidex/Controllers/Api/Languages/LanguagesController.cs
  40. 7
      src/Squidex/Controllers/Api/Schemas/Models/AddFieldDto.cs
  41. 6
      src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs
  42. 4
      src/Squidex/Controllers/ContentApi/ContentsController.cs
  43. 2
      src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs
  44. 28
      tests/Squidex.Core.Tests/ContentEnrichmentTests.cs
  45. 76
      tests/Squidex.Core.Tests/ContentValidationTests.cs
  46. 13
      tests/Squidex.Core.Tests/Contents/ContentDataTests.cs
  47. 18
      tests/Squidex.Core.Tests/LanguagesConfigTests.cs
  48. 16
      tests/Squidex.Core.Tests/Schemas/AssetsFieldTests.cs
  49. 12
      tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs
  50. 16
      tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs
  51. 32
      tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs
  52. 14
      tests/Squidex.Core.Tests/Schemas/GeolocationFieldTests.cs
  53. 33
      tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs
  54. 8
      tests/Squidex.Core.Tests/Schemas/JsonFieldTests.cs
  55. 16
      tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs
  56. 50
      tests/Squidex.Core.Tests/Schemas/SchemaTests.cs
  57. 18
      tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs
  58. 4
      tests/Squidex.Infrastructure.Tests/LanguageTests.cs
  59. 20
      tests/Squidex.Read.Tests/MongoDb/Contents/ODataQueryTests.cs
  60. 3
      tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs

28
src/Squidex.Core/ContentEnricher.cs

@ -17,16 +17,16 @@ namespace Squidex.Core
public sealed class ContentEnricher
{
private readonly Schema schema;
private readonly LanguagesConfig languagesConfig;
private readonly PartitionResolver partitionResolver;
public ContentEnricher(LanguagesConfig languagesConfig, Schema schema)
public ContentEnricher(Schema schema, PartitionResolver partitionResolver)
{
Guard.NotNull(schema, nameof(schema));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
Guard.NotNull(partitionResolver, nameof(partitionResolver));
this.schema = schema;
this.languagesConfig = languagesConfig;
this.partitionResolver = partitionResolver;
}
public void Enrich(ContentData data)
@ -36,17 +36,11 @@ namespace Squidex.Core
foreach (var field in schema.FieldsByName.Values)
{
var fieldData = data.GetOrCreate(field.Name, k => new ContentFieldData());
var fieldPartition = partitionResolver(field.Paritioning);
if (field.RawProperties.IsLocalizable)
foreach (var partitionItem in fieldPartition)
{
foreach (var languageConfig in languagesConfig)
{
Enrich(field, fieldData, languageConfig.Language);
}
}
else
{
Enrich(field, fieldData, Language.Invariant);
Enrich(field, fieldData, partitionItem);
}
if (fieldData.Count > 0)
@ -56,7 +50,7 @@ namespace Squidex.Core
}
}
private static void Enrich(Field field, ContentFieldData fieldData, Language language)
private static void Enrich(Field field, ContentFieldData fieldData, IFieldPartitionItem partitionItem)
{
Guard.NotNull(fieldData, nameof(fieldData));
@ -67,9 +61,11 @@ namespace Squidex.Core
return;
}
if (!fieldData.TryGetValue(language, out JToken value) || value == null || value.Type == JTokenType.Null)
var key = partitionItem.Key;
if (!fieldData.TryGetValue(key, out JToken value) || value == null || value.Type == JTokenType.Null)
{
fieldData.AddValue(language, defaultValue);
fieldData.AddValue(key, defaultValue);
}
}
}

12
src/Squidex.Core/ContentExtensions.cs

@ -16,18 +16,18 @@ namespace Squidex.Core
{
public static class ContentExtensions
{
public static ContentData Enrich(this ContentData data, Schema schema, LanguagesConfig languagesConfig)
public static ContentData Enrich(this ContentData data, Schema schema, PartitionResolver partitionResolver)
{
var enricher = new ContentEnricher(languagesConfig, schema);
var enricher = new ContentEnricher(schema, partitionResolver);
enricher.Enrich(data);
return data;
}
public static async Task ValidateAsync(this ContentData data, Schema schema, LanguagesConfig languagesConfig, IList<ValidationError> errors)
public static async Task ValidateAsync(this ContentData data, Schema schema, PartitionResolver partitionResolver, IList<ValidationError> errors)
{
var validator = new ContentValidator(schema, languagesConfig);
var validator = new ContentValidator(schema, partitionResolver);
await validator.ValidateAsync(data);
@ -37,9 +37,9 @@ namespace Squidex.Core
}
}
public static async Task ValidatePartialAsync(this ContentData data, Schema schema, LanguagesConfig languagesConfig, IList<ValidationError> errors)
public static async Task ValidatePartialAsync(this ContentData data, Schema schema, PartitionResolver partitionResolver, IList<ValidationError> errors)
{
var validator = new ContentValidator(schema, languagesConfig);
var validator = new ContentValidator(schema, partitionResolver);
await validator.ValidatePartialAsync(data);

69
src/Squidex.Core/ContentValidator.cs

@ -13,12 +13,14 @@ using Squidex.Core.Contents;
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
#pragma warning disable 168
namespace Squidex.Core
{
public sealed class ContentValidator
{
private readonly Schema schema;
private readonly LanguagesConfig languagesConfig;
private readonly PartitionResolver partitionResolver;
private readonly List<ValidationError> errors = new List<ValidationError>();
public IReadOnlyList<ValidationError> Errors
@ -26,14 +28,14 @@ namespace Squidex.Core
get { return errors; }
}
public ContentValidator(Schema schema, LanguagesConfig languagesConfig)
public ContentValidator(Schema schema, PartitionResolver partitionResolver)
{
Guard.NotNull(schema, nameof(schema));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
Guard.NotNull(partitionResolver, nameof(partitionResolver));
this.schema = schema;
this.languagesConfig = languagesConfig;
this.partitionResolver = partitionResolver;
}
public async Task ValidatePartialAsync(ContentData data)
@ -50,35 +52,27 @@ namespace Squidex.Core
}
else
{
if (field.RawProperties.IsLocalizable)
{
await ValidateFieldPartialAsync(field, fieldData.Value, languagesConfig);
}
else
{
await ValidateFieldPartialAsync(field, fieldData.Value, LanguagesConfig.Invariant);
}
await ValidateFieldPartialAsync(field, fieldData.Value);
}
}
}
private async Task ValidateFieldPartialAsync(Field field, ContentFieldData fieldData, LanguagesConfig languages)
private async Task ValidateFieldPartialAsync(Field field, ContentFieldData fieldData)
{
foreach (var languageValue in fieldData)
var partitioning = field.Paritioning;
var partition = partitionResolver(partitioning);
foreach (var partitionValues in fieldData)
{
if (!Language.TryGetLanguage(languageValue.Key, out var language))
{
errors.AddError($"<FIELD> has an invalid language '{languageValue.Key}'", field);
}
else if (!languages.TryGetConfig(language, out var languageConfig))
if (!partition.TryGetItem(partitionValues.Key, out var partitionItem))
{
errors.AddError($"<FIELD> has an unsupported language '{languageValue.Key}'", field);
errors.AddError($"<FIELD> has an unsupported {partitioning.Key} value '{partitionValues.Key}'", field);
}
else
{
var config = languageConfig;
var item = partitionItem;
await field.ValidateAsync(languageValue.Value, config.IsOptional, m => errors.AddError(m, field, config.Language));
await field.ValidateAsync(partitionValues.Value, item.IsOptional, m => errors.AddError(m, field, item));
}
}
}
@ -93,14 +87,7 @@ namespace Squidex.Core
{
var fieldData = data.GetOrCreate(field.Name, k => new ContentFieldData());
if (field.RawProperties.IsLocalizable)
{
await ValidateFieldAsync(field, fieldData, languagesConfig);
}
else
{
await ValidateFieldAsync(field, fieldData, LanguagesConfig.Invariant);
}
await ValidateFieldAsync(field, fieldData);
}
}
@ -115,26 +102,24 @@ namespace Squidex.Core
}
}
private async Task ValidateFieldAsync(Field field, ContentFieldData fieldData, LanguagesConfig languages)
private async Task ValidateFieldAsync(Field field, ContentFieldData fieldData)
{
foreach (var valueLanguage in fieldData.Keys)
var partitioning = field.Paritioning;
var partition = partitionResolver(partitioning);
foreach (var partitionValues in fieldData)
{
if (!Language.TryGetLanguage(valueLanguage, out Language language))
{
errors.AddError($"<FIELD> has an invalid language '{valueLanguage}'", field);
}
else if (!languages.Contains(language))
if (!partition.TryGetItem(partitionValues.Key, out var partitionItem))
{
errors.AddError($"<FIELD> has an unsupported language '{valueLanguage}'", field);
errors.AddError($"<FIELD> has an unsupported {partitioning.Key} value '{partitionValues.Key}'", field);
}
}
foreach (var languageConfig in languages)
foreach (var partitionItem in partition)
{
var config = languageConfig;
var value = fieldData.GetOrCreate(config.Language, k => JValue.CreateNull());
var value = fieldData.GetOrCreate(partitionItem.Key, k => JValue.CreateNull());
await field.ValidateAsync(value, config.IsOptional, m => errors.AddError(m, field, config.Language));
await field.ValidateAsync(value, partitionItem.IsOptional, m => errors.AddError(m, field, partitionItem));
}
}
}

30
src/Squidex.Core/Contents/ContentData.cs

@ -71,9 +71,9 @@ namespace Squidex.Core.Contents
{
var resultValue = new ContentFieldData();
foreach (var languageValue in fieldValue.Value.Where(x => x.Value != null && x.Value.Type != JTokenType.Null))
foreach (var partitionValue in fieldValue.Value.Where(x => x.Value != null && x.Value.Type != JTokenType.Null))
{
resultValue[languageValue.Key] = languageValue.Value;
resultValue[partitionValue.Key] = partitionValue.Value;
}
if (resultValue.Count > 0)
@ -104,17 +104,17 @@ namespace Squidex.Core.Contents
{
var encodedValue = new ContentFieldData();
foreach (var languageValue in fieldValue.Value)
foreach (var partitionValue in fieldValue.Value)
{
if (languageValue.Value == null || languageValue.Value.Type == JTokenType.Null)
if (partitionValue.Value == null || partitionValue.Value.Type == JTokenType.Null)
{
encodedValue[languageValue.Key] = null;
encodedValue[partitionValue.Key] = null;
}
else
{
var value = Convert.ToBase64String(Encoding.UTF8.GetBytes(languageValue.Value.ToString()));
var value = Convert.ToBase64String(Encoding.UTF8.GetBytes(partitionValue.Value.ToString()));
encodedValue[languageValue.Key] = value;
encodedValue[partitionValue.Key] = value;
}
}
@ -146,17 +146,17 @@ namespace Squidex.Core.Contents
{
var encodedValue = new ContentFieldData();
foreach (var languageValue in fieldValue.Value)
foreach (var partitionValue in fieldValue.Value)
{
if (languageValue.Value == null || languageValue.Value.Type == JTokenType.Null)
if (partitionValue.Value == null || partitionValue.Value.Type == JTokenType.Null)
{
encodedValue[languageValue.Key] = null;
encodedValue[partitionValue.Key] = null;
}
else
{
var value = Encoding.UTF8.GetString(Convert.FromBase64String(languageValue.Value.ToString()));
var value = Encoding.UTF8.GetString(Convert.FromBase64String(partitionValue.Value.ToString()));
encodedValue[languageValue.Key] = JToken.Parse(value);
encodedValue[partitionValue.Key] = JToken.Parse(value);
}
}
@ -177,7 +177,7 @@ namespace Squidex.Core.Contents
Guard.NotNull(schema, nameof(schema));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
var codeForInvariant = Language.Invariant.Iso2Code;
var codeForInvariant = InvariantPartitioning.Instance.Master.Key;
var codeForMasterLanguage = languagesConfig.Master.Language.Iso2Code;
var result = new ContentData();
@ -192,11 +192,11 @@ namespace Squidex.Core.Contents
var fieldResult = new ContentFieldData();
var fieldValues = fieldValue.Value;
if (field.RawProperties.IsLocalizable)
if (field.Paritioning.Equals(Partitioning.Language))
{
foreach (var languageConfig in languagesConfig)
{
string languageCode = languageConfig.Language;
var languageCode = languageConfig.Key;
if (fieldValues.TryGetValue(languageCode, out JToken value))
{

8
src/Squidex.Core/Contents/ContentFieldData.cs

@ -22,16 +22,16 @@ namespace Squidex.Core.Contents
public ContentFieldData SetValue(JToken value)
{
this[Language.Invariant] = value;
this[InvariantPartitioning.Instance.Master.Key] = value;
return this;
}
public ContentFieldData AddValue(string language, JToken value)
public ContentFieldData AddValue(string key, JToken value)
{
Guard.NotNullOrEmpty(language, nameof(language));
Guard.NotNullOrEmpty(key, nameof(key));
this[language] = value;
this[key] = value;
return this;
}

14
src/Squidex.Core/FieldExtensions.cs

@ -18,21 +18,21 @@ namespace Squidex.Core
{
public static class FieldExtensions
{
public static void AddError(this ICollection<ValidationError> errors, string message, Field field, Language language = null)
public static void AddError(this ICollection<ValidationError> errors, string message, Field field, IFieldPartitionItem partitionItem = null)
{
AddError(errors, message, !string.IsNullOrWhiteSpace(field.RawProperties.Label) ? field.RawProperties.Label : field.Name, field.Name, language);
AddError(errors, message, !string.IsNullOrWhiteSpace(field.RawProperties.Label) ? field.RawProperties.Label : field.Name, field.Name, partitionItem);
}
public static void AddError(this ICollection<ValidationError> errors, string message, string fieldName, Language language = null)
public static void AddError(this ICollection<ValidationError> errors, string message, string fieldName, IFieldPartitionItem partitionItem = null)
{
AddError(errors, message, fieldName, fieldName, language);
AddError(errors, message, fieldName, fieldName, partitionItem);
}
public static void AddError(this ICollection<ValidationError> errors, string message, string displayName, string fieldName, Language language = null)
public static void AddError(this ICollection<ValidationError> errors, string message, string displayName, string fieldName, IFieldPartitionItem partitionItem = null)
{
if (language != null && language != Language.Invariant)
if (partitionItem != null && partitionItem != InvariantPartitioning.Instance.Master)
{
displayName += $" ({language.Iso2Code})";
displayName += $" ({partitionItem.Key})";
}
errors.Add(new ValidationError(message.Replace("<FIELD>", displayName), fieldName));

18
src/Squidex.Core/Schemas/IRegisteredField.cs → src/Squidex.Core/IFieldPartitionItem.cs

@ -1,19 +1,23 @@
// ==========================================================================
// IRegisteredField.cs
// IFieldPartitionItem.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
namespace Squidex.Core.Schemas
namespace Squidex.Core
{
public interface IRegisteredField
public interface IFieldPartitionItem
{
Type PropertiesType { get; }
string Key { get; }
Field CreateField(long id, string name, FieldProperties properties);
string Name { get; }
bool IsOptional { get; }
IEnumerable<string> Fallback { get; }
}
}
}

19
src/Squidex.Core/IFieldPartitioning.cs

@ -0,0 +1,19 @@
// ==========================================================================
// IFieldPartitioning.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
namespace Squidex.Core
{
public interface IFieldPartitioning : IReadOnlyCollection<IFieldPartitionItem>
{
IFieldPartitionItem Master { get; }
bool TryGetItem(string key, out IFieldPartitionItem item);
}
}

65
src/Squidex.Core/InvariantPartitioning.cs

@ -0,0 +1,65 @@
// ==========================================================================
// InvariantPartitioning.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Squidex.Core
{
public sealed class InvariantPartitioning : IFieldPartitioning
{
public static readonly InvariantPartitioning Instance = new InvariantPartitioning();
private readonly InvariantItem invariantItem = new InvariantItem();
private sealed class InvariantItem : IFieldPartitionItem
{
public string Key { get; } = "iv";
public string Name { get; } = "Invariant";
public bool IsOptional { get; } = false;
public IEnumerable<string> Fallback { get; } = Enumerable.Empty<string>();
}
public int Count
{
get { return 1; }
}
public IFieldPartitionItem Master
{
get { return invariantItem; }
}
private InvariantPartitioning()
{
}
public bool TryGetItem(string key, out IFieldPartitionItem item)
{
var isFound = string.Equals(key, "iv", StringComparison.OrdinalIgnoreCase);
item = isFound ? invariantItem : null;
return isFound;
}
IEnumerator<IFieldPartitionItem> IEnumerable<IFieldPartitionItem>.GetEnumerator()
{
yield return invariantItem;
}
IEnumerator IEnumerable.GetEnumerator()
{
yield return invariantItem;
}
}
}

18
src/Squidex.Core/LanguageConfig.cs

@ -9,10 +9,11 @@
using System.Collections.Generic;
using Squidex.Infrastructure;
using System.Collections.Immutable;
using System.Linq;
namespace Squidex.Core
{
public sealed class LanguageConfig
public sealed class LanguageConfig : IFieldPartitionItem
{
public bool IsOptional { get; }
@ -20,6 +21,21 @@ namespace Squidex.Core
public ImmutableList<Language> Fallback { get; }
public string Key
{
get { return Language.Iso2Code; }
}
public string Name
{
get { return Language.EnglishName; }
}
IEnumerable<string> IFieldPartitionItem.Fallback
{
get { return Fallback.Select(x => x.Iso2Code); }
}
public LanguageConfig(Language language, bool isOptional, params Language[] fallback)
: this(language, isOptional, fallback?.ToImmutableList())
{

50
src/Squidex.Core/LanguagesConfig.cs

@ -17,25 +17,34 @@ using Squidex.Infrastructure;
namespace Squidex.Core
{
public sealed class LanguagesConfig : IEnumerable<LanguageConfig>
public sealed class LanguagesConfig : IFieldPartitioning
{
private readonly ImmutableDictionary<Language, LanguageConfig> languages;
private readonly LanguageConfig master;
public static readonly LanguagesConfig Empty = Create();
public static readonly LanguagesConfig Invariant = Create(Language.Invariant);
public LanguageConfig Master
{
get { return master; }
}
public int Count
{
get { return languages.Count; }
}
IFieldPartitionItem IFieldPartitioning.Master
{
get { return Master; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return languages.Values.GetEnumerator();
}
IEnumerator<LanguageConfig> IEnumerable<LanguageConfig>.GetEnumerator()
IEnumerator<IFieldPartitionItem> IEnumerable<IFieldPartitionItem>.GetEnumerator()
{
return languages.Values.GetEnumerator();
}
@ -119,14 +128,28 @@ namespace Squidex.Core
return new LanguagesConfig(newLanguages, master);
}
public bool TryGetConfig(Language language, out LanguageConfig value)
public bool Contains(Language language)
{
return languages.TryGetValue(language, out value);
return language != null && languages.ContainsKey(language);
}
public bool Contains(Language language)
public bool TryGetConfig(Language language, out LanguageConfig config)
{
return language != null && languages.ContainsKey(language);
return languages.TryGetValue(language, out config);
}
public bool TryGetItem(string key, out IFieldPartitionItem item)
{
if (Language.IsValidLanguage(key) && languages.TryGetValue(key, out var value))
{
item = value;
return true;
}
item = null;
return false;
}
private static ImmutableDictionary<Language, LanguageConfig> ValidateLanguages(ImmutableDictionary<Language, LanguageConfig> languages)
@ -181,5 +204,18 @@ namespace Squidex.Core
throw new ValidationException(message(), error);
}
}
public PartitionResolver ToResolver()
{
return partitioning =>
{
if (partitioning.Equals(Partitioning.Invariant))
{
return InvariantPartitioning.Instance;
}
return this;
};
}
}
}

45
src/Squidex.Core/Partitioning.cs

@ -0,0 +1,45 @@
// ==========================================================================
// Partitioning.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Core
{
public delegate IFieldPartitioning PartitionResolver(Partitioning key);
public sealed class Partitioning : IEquatable<Partitioning>
{
public static readonly Partitioning Invariant = new Partitioning("invariant");
public static readonly Partitioning Language = new Partitioning("language");
public string Key { get; }
public Partitioning(string key)
{
Guard.NotNullOrEmpty(key, nameof(key));
Key = key;
}
public override bool Equals(object obj)
{
return Equals(obj as Partitioning);
}
public bool Equals(Partitioning other)
{
return string.Equals(other?.Key, Key, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode()
{
return Key.GetHashCode();
}
}
}

9
src/Squidex.Core/Schemas/AssetsField.cs

@ -19,8 +19,13 @@ namespace Squidex.Core.Schemas
{
private readonly IAssetTester assetTester;
public AssetsField(long id, string name, AssetsFieldProperties properties, IAssetTester assetTester)
: base(id, name, properties)
public AssetsField(long id, string name, Partitioning partitioning, IAssetTester assetTester)
: this(id, name, partitioning, new AssetsFieldProperties(), assetTester)
{
}
public AssetsField(long id, string name, Partitioning partitioning, AssetsFieldProperties properties, IAssetTester assetTester)
: base(id, name, partitioning, properties)
{
this.assetTester = assetTester;
}

9
src/Squidex.Core/Schemas/BooleanField.cs

@ -18,8 +18,13 @@ namespace Squidex.Core.Schemas
{
public sealed class BooleanField : Field<BooleanFieldProperties>
{
public BooleanField(long id, string name, BooleanFieldProperties properties)
: base(id, name, properties)
public BooleanField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new BooleanFieldProperties())
{
}
public BooleanField(long id, string name, Partitioning partitioning, BooleanFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

9
src/Squidex.Core/Schemas/DateTimeField.cs

@ -26,8 +26,13 @@ namespace Squidex.Core.Schemas
[TypeName("DateTimeField")]
public sealed class DateTimeField : Field<DateTimeFieldProperties>
{
public DateTimeField(long id, string name, DateTimeFieldProperties properties)
: base(id, name, properties)
public DateTimeField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new DateTimeFieldProperties())
{
}
public DateTimeField(long id, string name, Partitioning partitioning, DateTimeFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

70
src/Squidex.Core/Schemas/Field.cs

@ -24,19 +24,20 @@ namespace Squidex.Core.Schemas
public abstract class Field : CloneableBase
{
private readonly Lazy<List<IValidator>> validators;
private readonly long id;
private string name;
private readonly long fieldId;
private readonly Partitioning partitioning;
private string fieldName;
private bool isDisabled;
private bool isHidden;
public long Id
{
get { return id; }
get { return fieldId; }
}
public string Name
{
get { return name; }
get { return fieldName; }
}
public bool IsHidden
@ -49,6 +50,11 @@ namespace Squidex.Core.Schemas
get { return isDisabled; }
}
public Partitioning Paritioning
{
get { return partitioning; }
}
public IReadOnlyList<IValidator> Validators
{
get { return validators.Value; }
@ -56,14 +62,16 @@ namespace Squidex.Core.Schemas
public abstract FieldProperties RawProperties { get; }
protected Field(long id, string name)
protected Field(long id, string name, Partitioning partitioning)
{
Guard.ValidPropertyName(name, nameof(name));
Guard.GreaterThan(id, 0, nameof(id));
Guard.NotNull(partitioning, nameof(partitioning));
this.id = id;
fieldId = id;
fieldName = name;
this.name = name;
this.partitioning = partitioning;
validators = new Lazy<List<IValidator>>(() => new List<IValidator>(CreateValidators()));
}
@ -98,22 +106,17 @@ namespace Squidex.Core.Schemas
{
var error = new ValidationError("Name must be a valid slug", "Name");
throw new ValidationException($"Cannot rename the field '{name}' ({id})", error);
throw new ValidationException($"Cannot rename the field '{fieldName}' ({fieldId})", error);
}
return Clone<Field>(clone => clone.name = newName);
return Clone<Field>(clone => clone.fieldName = newName);
}
public void AddToEdmType(EdmStructuredType edmType, LanguagesConfig languagesConfig, string schemaName, Func<EdmComplexType, EdmComplexType> typeResolver)
public void AddToEdmType(EdmStructuredType edmType, PartitionResolver partitionResolver, string schemaName, Func<EdmComplexType, EdmComplexType> typeResolver)
{
Guard.NotNull(edmType, nameof(edmType));
Guard.NotNull(typeResolver, nameof(typeResolver));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
if (!RawProperties.IsLocalizable)
{
languagesConfig = LanguagesConfig.Invariant;
}
Guard.NotNull(partitionResolver, nameof(partitionResolver));
var edmValueType = CreateEdmType();
@ -122,42 +125,39 @@ namespace Squidex.Core.Schemas
return;
}
var languageType = typeResolver(new EdmComplexType("Squidex", $"{schemaName}{Name.ToPascalCase()}Property"));
var partitionType = typeResolver(new EdmComplexType("Squidex", $"{schemaName}{Name.ToPascalCase()}Property"));
var partition = partitionResolver(partitioning);
foreach (var languageConfig in languagesConfig)
foreach (var partitionItem in partition)
{
languageType.AddStructuralProperty(languageConfig.Language, edmValueType);
partitionType.AddStructuralProperty(partitionItem.Key, edmValueType);
}
edmType.AddStructuralProperty(Name, new EdmComplexTypeReference(languageType, false));
edmType.AddStructuralProperty(Name, new EdmComplexTypeReference(partitionType, false));
}
public void AddToJsonSchema(JsonSchema4 schema, LanguagesConfig languagesConfig, string schemaName, Func<string, JsonSchema4, JsonSchema4> schemaResolver)
public void AddToJsonSchema(JsonSchema4 schema, PartitionResolver partitionResolver, string schemaName, Func<string, JsonSchema4, JsonSchema4> schemaResolver)
{
Guard.NotNull(schema, nameof(schema));
Guard.NotNull(schemaResolver, nameof(schemaResolver));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
if (!RawProperties.IsLocalizable)
{
languagesConfig = LanguagesConfig.Invariant;
}
Guard.NotNull(partitionResolver, nameof(partitionResolver));
var languagesProperty = CreateProperty();
var languagesObject = new JsonSchema4 { Type = JsonObjectType.Object, AllowAdditionalProperties = false };
var partitionProperty = CreateProperty();
var partitionObject = new JsonSchema4 { Type = JsonObjectType.Object, AllowAdditionalProperties = false };
var partition = partitionResolver(partitioning);
foreach (var languageConfig in languagesConfig)
foreach (var partitionItem in partition)
{
var languageProperty = new JsonProperty { Description = languageConfig.Language.EnglishName, IsRequired = RawProperties.IsRequired };
var partitionItemProperty = new JsonProperty { Description = partitionItem.Name, IsRequired = RawProperties.IsRequired };
PrepareJsonSchema(languageProperty, schemaResolver);
PrepareJsonSchema(partitionItemProperty, schemaResolver);
languagesObject.Properties.Add(languageConfig.Language, languageProperty);
partitionObject.Properties.Add(partitionItem.Key, partitionItemProperty);
}
languagesProperty.SchemaReference = schemaResolver($"{schemaName}{Name.ToPascalCase()}Property", languagesObject);
partitionProperty.SchemaReference = schemaResolver($"{schemaName}{Name.ToPascalCase()}Property", partitionObject);
schema.Properties.Add(Name, languagesProperty);
schema.Properties.Add(Name, partitionProperty);
}
public JsonProperty CreateProperty()

12
src/Squidex.Core/Schemas/FieldProperties.cs

@ -15,7 +15,6 @@ namespace Squidex.Core.Schemas
public abstract class FieldProperties : NamedElementPropertiesBase, IValidatable
{
private bool isRequired;
private bool isLocalizable;
private bool isListField;
private string placeholder;
@ -30,17 +29,6 @@ namespace Squidex.Core.Schemas
}
}
public bool IsLocalizable
{
get { return isLocalizable; }
set
{
ThrowIfFrozen();
isLocalizable = value;
}
}
public bool IsListField
{
get { return isListField; }

81
src/Squidex.Core/Schemas/FieldRegistry.cs

@ -12,41 +12,32 @@ using Squidex.Infrastructure;
namespace Squidex.Core.Schemas
{
public delegate Field FactoryFunction(long id, string name, FieldProperties properties);
public sealed class FieldRegistry
{
private delegate Field FactoryFunction(long id, string name, Partitioning partitioning, FieldProperties properties);
private readonly TypeNameRegistry typeNameRegistry;
private readonly Dictionary<string, IRegisteredField> fieldsByTypeName = new Dictionary<string, IRegisteredField>();
private readonly Dictionary<Type, IRegisteredField> fieldsByPropertyType = new Dictionary<Type, IRegisteredField>();
private readonly Dictionary<Type, Registered> fieldsByPropertyType = new Dictionary<Type, Registered>();
private sealed class Registered : IRegisteredField
private sealed class Registered
{
private readonly FactoryFunction fieldFactory;
private readonly Type propertiesType;
private readonly string typeName;
public Type PropertiesType
{
get { return propertiesType; }
}
public string TypeName
public Registered(FactoryFunction fieldFactory, Type propertiesType)
{
get { return typeName; }
}
public Registered(FactoryFunction fieldFactory, Type propertiesType, TypeNameRegistry typeNameRegistry)
{
typeName = typeNameRegistry.GetName(propertiesType);
this.fieldFactory = fieldFactory;
this.propertiesType = propertiesType;
}
Field IRegisteredField.CreateField(long id, string name, FieldProperties properties)
public Field CreateField(long id, string name, Partitioning partitioning, FieldProperties properties)
{
return fieldFactory(id, name, properties);
return fieldFactory(id, name, partitioning, properties);
}
}
@ -58,25 +49,32 @@ namespace Squidex.Core.Schemas
this.typeNameRegistry = typeNameRegistry;
Add<BooleanFieldProperties>(
(id, name, p) => new BooleanField(id, name, (BooleanFieldProperties)p));
(id, name, partitioning, properties) =>
new BooleanField(id, name, partitioning, (BooleanFieldProperties)properties));
Add<NumberFieldProperties>(
(id, name, p) => new NumberField(id, name, (NumberFieldProperties)p));
(id, name, partitioning, properties) =>
new NumberField(id, name, partitioning, (NumberFieldProperties)properties));
Add<StringFieldProperties>(
(id, name, p) => new StringField(id, name, (StringFieldProperties)p));
(id, name, partitioning, properties) =>
new StringField(id, name, partitioning, (StringFieldProperties)properties));
Add<DateTimeFieldProperties>(
(id, name, p) => new DateTimeField(id, name, (DateTimeFieldProperties)p));
(id, name, partitioning, properties) =>
new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties));
Add<JsonFieldProperties>(
(id, name, p) => new JsonField(id, name, (JsonFieldProperties)p));
(id, name, partitioning, properties) =>
new JsonField(id, name, partitioning, (JsonFieldProperties)properties));
Add<AssetsFieldProperties>(
(id, name, p) => new AssetsField(id, name, (AssetsFieldProperties)p, assetTester));
(id, name, partitioning, properties) =>
new AssetsField(id, name, partitioning, (AssetsFieldProperties)properties, assetTester));
Add<GeolocationFieldProperties>(
(id, name, p) => new GeolocationField(id, name, (GeolocationFieldProperties)p));
(id, name, partitioning, properties) =>
new GeolocationField(id, name, partitioning, (GeolocationFieldProperties)properties));
}
private void Add<TFieldProperties>(FactoryFunction fieldFactory)
@ -85,13 +83,12 @@ namespace Squidex.Core.Schemas
typeNameRegistry.Map(typeof(TFieldProperties));
var registered = new Registered(fieldFactory, typeof(TFieldProperties), typeNameRegistry);
fieldsByTypeName[registered.TypeName] = registered;
var registered = new Registered(fieldFactory, typeof(TFieldProperties));
fieldsByPropertyType[registered.PropertiesType] = registered;
}
public Field CreateField(long id, string name, FieldProperties properties)
public Field CreateField(long id, string name, Partitioning partitioning, FieldProperties properties)
{
var registered = fieldsByPropertyType.GetOrDefault(properties.GetType());
@ -100,35 +97,7 @@ namespace Squidex.Core.Schemas
throw new InvalidOperationException($"The field property '{properties.GetType()}' is not supported.");
}
return registered.CreateField(id, name, properties);
}
public IRegisteredField FindByPropertiesType(Type type)
{
Guard.NotNull(type, nameof(type));
var registered = fieldsByPropertyType.GetOrDefault(type);
if (registered == null)
{
throw new InvalidOperationException($"The field property '{type}' is not supported.");
}
return registered;
}
public IRegisteredField FindByTypeName(string typeName)
{
Guard.NotNullOrEmpty(typeName, nameof(typeName));
var registered = fieldsByTypeName.GetOrDefault(typeName);
if (registered == null)
{
throw new DomainException($"A field with type '{typeName} is not known.");
}
return registered;
return registered.CreateField(id, name, partitioning, properties);
}
}
}

4
src/Squidex.Core/Schemas/Field_Generic.cs

@ -25,8 +25,8 @@ namespace Squidex.Core.Schemas
get { return properties; }
}
protected Field(long id, string name, T properties)
: base(id, name)
protected Field(long id, string name, Partitioning partitioning, T properties)
: base(id, name, partitioning)
{
Guard.NotNull(properties, nameof(properties));

9
src/Squidex.Core/Schemas/GeolocationField.cs

@ -18,8 +18,13 @@ namespace Squidex.Core.Schemas
{
public sealed class GeolocationField : Field<GeolocationFieldProperties>
{
public GeolocationField(long id, string name, GeolocationFieldProperties properties)
: base(id, name, properties)
public GeolocationField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new GeolocationFieldProperties())
{
}
public GeolocationField(long id, string name, Partitioning partitioning, GeolocationFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

14
src/Squidex.Core/Schemas/Json/JsonFieldModel.cs

@ -6,18 +6,28 @@
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json;
namespace Squidex.Core.Schemas.Json
{
public sealed class JsonFieldModel
{
public string Name { get; set; }
[JsonProperty]
public long Id { get; set; }
[JsonProperty]
public bool IsHidden { get; set; }
[JsonProperty]
public bool IsDisabled { get; set; }
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Partitioning { get; set; }
[JsonProperty]
public FieldProperties Properties { get; set; }
}
}

5
src/Squidex.Core/Schemas/Json/JsonSchemaModel.cs

@ -7,17 +7,22 @@
// ==========================================================================
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Squidex.Core.Schemas.Json
{
public sealed class JsonSchemaModel
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public bool IsPublished { get; set; }
[JsonProperty]
public SchemaProperties Properties { get; set; }
[JsonProperty]
public List<JsonFieldModel> Fields { get; set; }
}
}

5
src/Squidex.Core/Schemas/Json/SchemaJsonSerializer.cs

@ -43,6 +43,7 @@ namespace Squidex.Core.Schemas.Json
Name = x.Name,
IsHidden = x.IsHidden,
IsDisabled = x.IsDisabled,
Partitioning = x.Paritioning.Key,
Properties = x.RawProperties
}).ToList();
@ -56,7 +57,9 @@ namespace Squidex.Core.Schemas.Json
var fields =
model.Fields.Select(fieldModel =>
{
var field = fieldRegistry.CreateField(fieldModel.Id, fieldModel.Name, fieldModel.Properties);
var parititonKey = new Partitioning(fieldModel.Partitioning);
var field = fieldRegistry.CreateField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties);
if (fieldModel.IsDisabled)
{

9
src/Squidex.Core/Schemas/JsonField.cs

@ -17,8 +17,13 @@ namespace Squidex.Core.Schemas
{
public sealed class JsonField : Field<JsonFieldProperties>
{
public JsonField(long id, string name, JsonFieldProperties properties)
: base(id, name, properties)
public JsonField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new JsonFieldProperties())
{
}
public JsonField(long id, string name, Partitioning partitioning, JsonFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

9
src/Squidex.Core/Schemas/NumberField.cs

@ -19,8 +19,13 @@ namespace Squidex.Core.Schemas
{
public sealed class NumberField : Field<NumberFieldProperties>
{
public NumberField(long id, string name, NumberFieldProperties properties)
: base(id, name, properties)
public NumberField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new NumberFieldProperties())
{
}
public NumberField(long id, string name, Partitioning partitioning, NumberFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

12
src/Squidex.Core/Schemas/Schema.cs

@ -202,10 +202,10 @@ namespace Squidex.Core.Schemas
return new Schema(name, isPublished, properties, newFields);
}
public EdmComplexType BuildEdmType(LanguagesConfig languagesConfig, Func<EdmComplexType, EdmComplexType> typeResolver)
public EdmComplexType BuildEdmType(PartitionResolver partitionResolver, Func<EdmComplexType, EdmComplexType> typeResolver)
{
Guard.NotNull(typeResolver, nameof(typeResolver));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
Guard.NotNull(partitionResolver, nameof(partitionResolver));
var schemaName = Name.ToPascalCase();
@ -213,16 +213,16 @@ namespace Squidex.Core.Schemas
foreach (var field in fieldsByName.Values.Where(x => !x.IsHidden))
{
field.AddToEdmType(edmType, languagesConfig, schemaName, typeResolver);
field.AddToEdmType(edmType, partitionResolver, schemaName, typeResolver);
}
return edmType;
}
public JsonSchema4 BuildJsonSchema(LanguagesConfig languagesConfig, Func<string, JsonSchema4, JsonSchema4> schemaResolver)
public JsonSchema4 BuildJsonSchema(PartitionResolver partitionResolver, Func<string, JsonSchema4, JsonSchema4> schemaResolver)
{
Guard.NotNull(schemaResolver, nameof(schemaResolver));
Guard.NotNull(languagesConfig, nameof(languagesConfig));
Guard.NotNull(partitionResolver, nameof(partitionResolver));
var schemaName = Name.ToPascalCase();
@ -230,7 +230,7 @@ namespace Squidex.Core.Schemas
foreach (var field in fieldsByName.Values.Where(x => !x.IsHidden))
{
field.AddToJsonSchema(schema, languagesConfig, schemaName, schemaResolver);
field.AddToJsonSchema(schema, partitionResolver, schemaName, schemaResolver);
}
return schema;

9
src/Squidex.Core/Schemas/StringField.cs

@ -20,8 +20,13 @@ namespace Squidex.Core.Schemas
{
public sealed class StringField : Field<StringFieldProperties>
{
public StringField(long id, string name, StringFieldProperties properties)
: base(id, name, properties)
public StringField(long id, string name, Partitioning partitioning)
: this(id, name, partitioning, new StringFieldProperties())
{
}
public StringField(long id, string name, Partitioning partitioning, StringFieldProperties properties)
: base(id, name, partitioning, properties)
{
}

2
src/Squidex.Events/Schemas/FieldAdded.cs

@ -16,6 +16,8 @@ namespace Squidex.Events.Schemas
{
public string Name { get; set; }
public bool IsLocalizable { get; set; }
public FieldProperties Properties { get; set; }
}
}

14
src/Squidex.Events/Schemas/Utils/SchemaEventDispatcher.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using Squidex.Core;
using Squidex.Core.Schemas;
// ReSharper disable UnusedParameter.Global
@ -14,14 +15,19 @@ namespace Squidex.Events.Schemas.Utils
{
public static class SchemaEventDispatcher
{
public static Schema Dispatch(SchemaCreated @event)
public static Schema Dispatch(FieldAdded @event, Schema schema, FieldRegistry registry)
{
return Schema.Create(@event.Name, @event.Properties);
var partitioning =
@event.IsLocalizable ?
Partitioning.Language :
Partitioning.Invariant;
return schema.AddOrUpdateField(registry.CreateField(@event.FieldId.Id, @event.Name, partitioning, @event.Properties));
}
public static Schema Dispatch(FieldAdded @event, Schema schema, FieldRegistry registry)
public static Schema Dispatch(SchemaCreated @event)
{
return schema.AddOrUpdateField(registry.CreateField(@event.FieldId.Id, @event.Name, @event.Properties));
return Schema.Create(@event.Name, @event.Properties);
}
public static Schema Dispatch(FieldUpdated @event, Schema schema)

2
src/Squidex.Infrastructure/Language.cs

@ -21,8 +21,6 @@ namespace Squidex.Infrastructure
private readonly string englishName;
private static readonly Dictionary<string, Language> AllLanguagesField = new Dictionary<string, Language>(StringComparer.OrdinalIgnoreCase);
public static readonly Language Invariant = AddLanguage("iv", "Invariant");
private static Language AddLanguage(string iso2Code, string englishName)
{
var language = new Language(iso2Code, englishName);

7
src/Squidex.Read.MongoDb/Apps/MongoAppEntity.cs

@ -48,6 +48,11 @@ namespace Squidex.Read.MongoDb.Apps
[BsonElement]
public Dictionary<string, MongoAppContributorEntity> Contributors { get; set; } = new Dictionary<string, MongoAppContributorEntity>();
public PartitionResolver PartitionResolver
{
get { return LanguagesConfig.ToResolver(); }
}
public LanguagesConfig LanguagesConfig
{
get { return languagesConfig ?? (languagesConfig = CreateLanguagesConfig()); }
@ -70,7 +75,7 @@ namespace Squidex.Read.MongoDb.Apps
if (languagesConfig != newConfig)
{
languagesConfig = newConfig;
Languages = newConfig.Select(FromLanguageConfig).ToList();
Languages = newConfig.OfType<LanguageConfig>().Select(FromLanguageConfig).ToList();
MasterLanguage = newConfig.Master.Language;
}

9
src/Squidex.Read.MongoDb/Contents/MongoContentRepository.cs

@ -15,6 +15,7 @@ using MongoDB.Driver;
using Squidex.Core;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Read.Apps;
using Squidex.Read.Contents;
using Squidex.Read.Contents.Builders;
using Squidex.Read.Contents.Repositories;
@ -50,7 +51,7 @@ namespace Squidex.Read.MongoDb.Contents
this.modelBuilder = modelBuilder;
}
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, LanguagesConfig languagesConfig)
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity)
{
List<IContentEntity> result = null;
@ -59,7 +60,7 @@ namespace Squidex.Read.MongoDb.Contents
IFindFluent<MongoContentEntity, MongoContentEntity> cursor;
try
{
var model = modelBuilder.BuildEdmModel(schemaEntity, languagesConfig);
var model = modelBuilder.BuildEdmModel(schemaEntity, appEntity);
var parser = model.ParseQuery(odataQuery);
@ -91,7 +92,7 @@ namespace Squidex.Read.MongoDb.Contents
return result;
}
public async Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, LanguagesConfig languagesConfig)
public async Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity)
{
var result = 0L;
@ -100,7 +101,7 @@ namespace Squidex.Read.MongoDb.Contents
IFindFluent<MongoContentEntity, MongoContentEntity> cursor;
try
{
var model = modelBuilder.BuildEdmModel(schemaEntity, languagesConfig);
var model = modelBuilder.BuildEdmModel(schemaEntity, appEntity);
var parser = model.ParseQuery(odataQuery);

2
src/Squidex.Read/Apps/IAppEntity.cs

@ -20,5 +20,7 @@ namespace Squidex.Read.Apps
IReadOnlyCollection<IAppClientEntity> Clients { get; }
IReadOnlyCollection<IAppContributorEntity> Contributors { get; }
PartitionResolver PartitionResolver { get; }
}
}

14
src/Squidex.Read/Contents/Builders/EdmModelBuilder.cs

@ -14,6 +14,7 @@ using Microsoft.OData.Edm.Library;
using Squidex.Core;
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Read.Apps;
using Squidex.Read.Schemas;
using Squidex.Read.Utils;
@ -26,32 +27,29 @@ namespace Squidex.Read.Contents.Builders
{
}
public IEdmModel BuildEdmModel(ISchemaEntityWithSchema schemaEntity, LanguagesConfig languagesConfig)
public IEdmModel BuildEdmModel(ISchemaEntityWithSchema schemaEntity, IAppEntity app)
{
Guard.NotNull(languagesConfig, nameof(languagesConfig));
Guard.NotNull(schemaEntity, nameof(schemaEntity));
var isoCodes = string.Join(",", languagesConfig.Select(x => x.Language.Iso2Code).OrderBy(x => x));
var cacheKey = $"{schemaEntity.Id}_{schemaEntity.Version}_{isoCodes}";
var cacheKey = $"{schemaEntity.Id}_{schemaEntity.Version}_{app.Id}_{app.Version}";
var result = Cache.GetOrCreate<IEdmModel>(cacheKey, entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(60);
return BuildEdmModel(schemaEntity.Schema, languagesConfig);
return BuildEdmModel(schemaEntity.Schema, app.PartitionResolver);
});
return result;
}
private static EdmModel BuildEdmModel(Schema schema, LanguagesConfig languagesConfig)
private static EdmModel BuildEdmModel(Schema schema, PartitionResolver partitionResolver)
{
var model = new EdmModel();
var container = new EdmEntityContainer("Squidex", "Container");
var schemaType = schema.BuildEdmType(languagesConfig, x =>
var schemaType = schema.BuildEdmType(partitionResolver, x =>
{
model.AddElement(x);

5
src/Squidex.Read/Contents/Repositories/IContentRepository.cs

@ -10,14 +10,15 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Core;
using Squidex.Read.Apps;
namespace Squidex.Read.Contents.Repositories
{
public interface IContentRepository
{
Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, LanguagesConfig languagesConfig);
Task<IReadOnlyList<IContentEntity>> QueryAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity);
Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, LanguagesConfig languagesConfig);
Task<long> CountAsync(Guid schemaId, bool nonPublished, string odataQuery, IAppEntity appEntity);
Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id);
}

4
src/Squidex.Write/Contents/ContentCommandHandler.cs

@ -99,7 +99,7 @@ namespace Squidex.Write.Contents
var schemaObject = taskForSchema.Result.Schema;
var schemaErrors = new List<ValidationError>();
await command.Data.ValidateAsync(schemaObject, taskForApp.Result.LanguagesConfig, schemaErrors);
await command.Data.ValidateAsync(schemaObject, taskForApp.Result.PartitionResolver, schemaErrors);
if (schemaErrors.Count > 0)
{
@ -108,7 +108,7 @@ namespace Squidex.Write.Contents
if (enrich)
{
command.Data.Enrich(schemaObject, taskForApp.Result.LanguagesConfig);
command.Data.Enrich(schemaObject, taskForApp.Result.PartitionResolver);
}
}
}

3
src/Squidex/Controllers/Api/Apps/AppLanguagesController.cs

@ -17,6 +17,7 @@ using NSwag.Annotations;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Reflection;
using Squidex.Controllers.Api.Apps.Models;
using Squidex.Core;
using Squidex.Core.Identity;
using Squidex.Infrastructure;
using Squidex.Pipeline;
@ -62,7 +63,7 @@ namespace Squidex.Controllers.Api.Apps
return NotFound();
}
var model = entity.LanguagesConfig.Select(x =>
var model = entity.LanguagesConfig.OfType<LanguageConfig>().Select(x =>
SimpleMapper.Map(x.Language,
new AppLanguageDto
{

2
src/Squidex/Controllers/Api/Assets/AssetsController.cs

@ -92,7 +92,7 @@ namespace Squidex.Controllers.Api.Assets
}
var taskForAssets = assetRepository.QueryAsync(AppId, mimeTypeList, idsList, query, take, skip);
var taskForCount = assetRepository.CountAsync(AppId, mimeTypeList, idsList, query);
var taskForCount = assetRepository.CountAsync(AppId, mimeTypeList, idsList, query);
await Task.WhenAll(taskForAssets, taskForCount);

2
src/Squidex/Controllers/Api/Languages/LanguagesController.cs

@ -38,7 +38,7 @@ namespace Squidex.Controllers.Api.Languages
[ProducesResponseType(typeof(string[]), 200)]
public IActionResult GetLanguages()
{
var response = Language.AllLanguages.Where(x => x != Language.Invariant).Select(x => SimpleMapper.Map(x, new LanguageDto())).ToList();
var response = Language.AllLanguages.Select(x => SimpleMapper.Map(x, new LanguageDto())).ToList();
return Ok(response);
}

7
src/Squidex/Controllers/Api/Schemas/Models/AddFieldDto.cs

@ -8,8 +8,6 @@
using System.ComponentModel.DataAnnotations;
// ReSharper disable ConvertIfStatementToReturnStatement
namespace Squidex.Controllers.Api.Schemas.Models
{
public sealed class AddFieldDto
@ -21,6 +19,11 @@ namespace Squidex.Controllers.Api.Schemas.Models
[RegularExpression("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$")]
public string Name { get; set; }
/// <summary>
/// Determines if the field is localizable.
/// </summary>
public bool IsLocalizable { get; set; }
/// <summary>
/// The field properties.
/// </summary>

6
src/Squidex/Controllers/Api/Schemas/Models/FieldPropertiesDto.cs

@ -51,11 +51,11 @@ namespace Squidex.Controllers.Api.Schemas.Models
/// Determines if the field should be displayed in lists.
/// </summary>
public bool IsListField { get; set; }
/// <summary>
/// Determines if the field is localizable.
/// Gets the partitioning of the language, e.g. invariant or language.
/// </summary>
public bool IsLocalizable { get; set; }
public string Partitioning { get; set; }
public abstract FieldProperties ToProperties();
}

4
src/Squidex/Controllers/ContentApi/ContentsController.cs

@ -55,8 +55,8 @@ namespace Squidex.Controllers.ContentApi
var query = Request.QueryString.ToString();
var taskForContents = contentRepository.QueryAsync(schemaEntity.Id, nonPublished, query, App.LanguagesConfig);
var taskForCount = contentRepository.CountAsync(schemaEntity.Id, nonPublished, query, App.LanguagesConfig);
var taskForContents = contentRepository.QueryAsync(schemaEntity.Id, nonPublished, query, App);
var taskForCount = contentRepository.CountAsync(schemaEntity.Id, nonPublished, query, App);
await Task.WhenAll(taskForContents, taskForCount);

2
src/Squidex/Controllers/ContentApi/Generator/SchemasSwaggerGenerator.cs

@ -178,7 +178,7 @@ namespace Squidex.Controllers.ContentApi.Generator
Name = schemaName, Description = $"API to managed {schemaName} contents."
});
var dataSchema = AppendSchema($"{schemaIdentifier}Dto", schema.BuildJsonSchema(app.LanguagesConfig, AppendSchema));
var dataSchema = AppendSchema($"{schemaIdentifier}Dto", schema.BuildJsonSchema(app.PartitionResolver, AppendSchema));
var schemaOperations = new List<SwaggerOperations>
{

28
tests/Squidex.Core.Tests/ContentEnrichmentTests.cs

@ -27,36 +27,36 @@ namespace Squidex.Core
var schema =
Schema.Create("my-schema", new SchemaProperties())
.AddOrUpdateField(new JsonField(1, "my-json",
.AddOrUpdateField(new JsonField(1, "my-json", Partitioning.Invariant,
new JsonFieldProperties()))
.AddOrUpdateField(new StringField(2, "my-string",
new StringFieldProperties { DefaultValue = "EN-String", IsLocalizable = true }))
.AddOrUpdateField(new NumberField(3, "my-number",
.AddOrUpdateField(new StringField(2, "my-string", Partitioning.Language,
new StringFieldProperties { DefaultValue = "en-string" }))
.AddOrUpdateField(new NumberField(3, "my-number", Partitioning.Invariant,
new NumberFieldProperties { DefaultValue = 123 }))
.AddOrUpdateField(new BooleanField(4, "my-boolean",
.AddOrUpdateField(new AssetsField(4, "my-assets", Partitioning.Invariant,
new AssetsFieldProperties(), new Mock<IAssetTester>().Object))
.AddOrUpdateField(new BooleanField(5, "my-boolean", Partitioning.Invariant,
new BooleanFieldProperties { DefaultValue = true }))
.AddOrUpdateField(new DateTimeField(5, "my-datetime",
.AddOrUpdateField(new DateTimeField(6, "my-datetime", Partitioning.Invariant,
new DateTimeFieldProperties { DefaultValue = now }))
.AddOrUpdateField(new GeolocationField(6, "my-geolocation",
new GeolocationFieldProperties()))
.AddOrUpdateField(new AssetsField(7, "my-assets",
new AssetsFieldProperties(), new Mock<IAssetTester>().Object));
.AddOrUpdateField(new GeolocationField(7, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()));
var data =
new ContentData()
.AddField("my-string",
new ContentFieldData()
.AddValue("de", "DE-String"))
.AddValue("de", "de-string"))
.AddField("my-number",
new ContentFieldData()
.AddValue("iv", 456));
data.Enrich(schema, languagesConfig);
data.Enrich(schema, languagesConfig.ToResolver());
Assert.Equal(456, (int)data["my-number"]["iv"]);
Assert.Equal("DE-String", (string)data["my-string"]["de"]);
Assert.Equal("EN-String", (string)data["my-string"]["en"]);
Assert.Equal("de-string", (string)data["my-string"]["de"]);
Assert.Equal("en-string", (string)data["my-string"]["en"]);
Assert.Equal(now, InstantPattern.General.Parse((string)data["my-datetime"]["iv"]).Value);

76
tests/Squidex.Core.Tests/ContentValidationTests.cs

@ -30,7 +30,7 @@ namespace Squidex.Core
.AddField("unknown",
new ContentFieldData());
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -42,7 +42,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_validating_data_with_invalid_field()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { MaxValue = 100 }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant, new NumberFieldProperties { MaxValue = 100 }));
var data =
new ContentData()
@ -50,7 +50,7 @@ namespace Squidex.Core
new ContentFieldData()
.SetValue(1000));
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -62,7 +62,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_non_localizable_data_field_contains_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties()));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant));
var data =
new ContentData()
@ -71,25 +71,25 @@ namespace Squidex.Core
.AddValue("es", 1)
.AddValue("it", 1));
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an unsupported language 'es'", "my-field"),
new ValidationError("my-field has an unsupported language 'it'", "my-field")
new ValidationError("my-field has an unsupported invariant value 'es'", "my-field"),
new ValidationError("my-field has an unsupported invariant value 'it'", "my-field")
});
}
[Fact]
public async Task Should_add_error_if_validating_data_with_invalid_localizable_field()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true, IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language, new NumberFieldProperties { IsRequired = true }));
var data =
new ContentData();
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -102,12 +102,12 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_required_data_field_is_not_in_bag()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant, new NumberFieldProperties { IsRequired = true }));
var data =
new ContentData();
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -119,7 +119,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_data_contains_invalid_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language));
var data =
new ContentData()
@ -128,12 +128,12 @@ namespace Squidex.Core
.AddValue("de", 1)
.AddValue("xx", 1));
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an invalid language 'xx'", "my-field")
new ValidationError("my-field has an unsupported language value 'xx'", "my-field")
});
}
@ -143,7 +143,7 @@ namespace Squidex.Core
var optionalConfig =
LanguagesConfig.Create(Language.ES, Language.IT).Update(Language.IT, true, false, null);
schema = schema.AddOrUpdateField(new StringField(1, "my-field", new StringFieldProperties { IsLocalizable = true, IsRequired = true }));
schema = schema.AddOrUpdateField(new StringField(1, "my-field", Partitioning.Language, new StringFieldProperties { IsRequired = true }));
var data =
new ContentData()
@ -151,7 +151,7 @@ namespace Squidex.Core
new ContentFieldData()
.AddValue("es", "value"));
await data.ValidateAsync(schema, optionalConfig, errors);
await data.ValidateAsync(schema, optionalConfig.ToResolver(), errors);
Assert.Equal(0, errors.Count);
}
@ -159,7 +159,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_data_contains_unsupported_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language));
var data =
new ContentData()
@ -168,13 +168,13 @@ namespace Squidex.Core
.AddValue("es", 1)
.AddValue("it", 1));
await data.ValidateAsync(schema, languagesConfig, errors);
await data.ValidateAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an unsupported language 'es'", "my-field"),
new ValidationError("my-field has an unsupported language 'it'", "my-field")
new ValidationError("my-field has an unsupported language value 'es'", "my-field"),
new ValidationError("my-field has an unsupported language value 'it'", "my-field")
});
}
@ -186,7 +186,7 @@ namespace Squidex.Core
.AddField("unknown",
new ContentFieldData());
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -198,7 +198,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_validating_partial_data_with_invalid_field()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { MaxValue = 100 }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant, new NumberFieldProperties { MaxValue = 100 }));
var data =
new ContentData()
@ -206,7 +206,7 @@ namespace Squidex.Core
new ContentFieldData()
.SetValue(1000));
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
@ -218,7 +218,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_non_localizable_partial_data_field_contains_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties()));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant));
var data =
new ContentData()
@ -227,25 +227,25 @@ namespace Squidex.Core
.AddValue("es", 1)
.AddValue("it", 1));
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an unsupported language 'es'", "my-field"),
new ValidationError("my-field has an unsupported language 'it'", "my-field")
new ValidationError("my-field has an unsupported invariant value 'es'", "my-field"),
new ValidationError("my-field has an unsupported invariant value 'it'", "my-field")
});
}
[Fact]
public async Task Should_not_add_error_if_validating_partial_data_with_invalid_localizable_field()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true, IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language, new NumberFieldProperties { IsRequired = true }));
var data =
new ContentData();
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
Assert.Equal(0, errors.Count);
}
@ -253,12 +253,12 @@ namespace Squidex.Core
[Fact]
public async Task Should_not_add_error_if_required_partial_data_field_is_not_in_bag()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsRequired = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant, new NumberFieldProperties { IsRequired = true }));
var data =
new ContentData();
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
Assert.Equal(0, errors.Count);
}
@ -266,7 +266,7 @@ namespace Squidex.Core
[Fact]
public async Task Should_add_error_if_partial_data_contains_invalid_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language));
var data =
new ContentData()
@ -275,19 +275,19 @@ namespace Squidex.Core
.AddValue("de", 1)
.AddValue("xx", 1));
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an invalid language 'xx'", "my-field")
new ValidationError("my-field has an unsupported language value 'xx'", "my-field")
});
}
[Fact]
public async Task Should_add_error_if_partial_data_contains_unsupported_language()
{
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", new NumberFieldProperties { IsLocalizable = true }));
schema = schema.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Language));
var data =
new ContentData()
@ -296,13 +296,13 @@ namespace Squidex.Core
.AddValue("es", 1)
.AddValue("it", 1));
await data.ValidatePartialAsync(schema, languagesConfig, errors);
await data.ValidatePartialAsync(schema, languagesConfig.ToResolver(), errors);
errors.ShouldBeEquivalentTo(
new List<ValidationError>
{
new ValidationError("my-field has an unsupported language 'es'", "my-field"),
new ValidationError("my-field has an unsupported language 'it'", "my-field")
new ValidationError("my-field has an unsupported language value 'es'", "my-field"),
new ValidationError("my-field has an unsupported language value 'it'", "my-field")
});
}
}

13
tests/Squidex.Core.Tests/Contents/ContentDataTests.cs

@ -18,15 +18,10 @@ namespace Squidex.Core.Contents
{
private readonly Schema schema =
Schema.Create("schema", new SchemaProperties())
.AddOrUpdateField(new NumberField(1, "field1",
new NumberFieldProperties { IsLocalizable = true }))
.AddOrUpdateField(new NumberField(2, "field2",
new NumberFieldProperties { IsLocalizable = false }))
.AddOrUpdateField(new NumberField(3, "field3",
new NumberFieldProperties { IsLocalizable = false }))
.AddOrUpdateField(new JsonField(4, "json",
new JsonFieldProperties { IsLocalizable = true }))
.HideField(3);
.AddOrUpdateField(new NumberField(1, "field1", Partitioning.Language))
.AddOrUpdateField(new NumberField(2, "field2", Partitioning.Invariant))
.AddOrUpdateField(new NumberField(3, "field3", Partitioning.Invariant).Hide())
.AddOrUpdateField(new JsonField(4, "json", Partitioning.Language));
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Create(Language.EN, Language.DE);
[Fact]

18
tests/Squidex.Core.Tests/LanguagesConfigTests.cs

@ -22,7 +22,7 @@ namespace Squidex.Core
{
var config = LanguagesConfig.Create(Language.DE);
config.ToList().ShouldBeEquivalentTo(
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(
new List<LanguageConfig>
{
new LanguageConfig(Language.DE)
@ -36,7 +36,7 @@ namespace Squidex.Core
{
var config = LanguagesConfig.Create(Language.DE, Language.EN, Language.IT);
config.ToList().ShouldBeEquivalentTo(
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(
new List<LanguageConfig>
{
new LanguageConfig(Language.DE),
@ -54,11 +54,11 @@ namespace Squidex.Core
{
new LanguageConfig(Language.DE),
new LanguageConfig(Language.EN),
new LanguageConfig(Language.IT),
new LanguageConfig(Language.IT)
};
var config = LanguagesConfig.Create(configs);
config.ToList().ShouldBeEquivalentTo(configs);
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(configs);
Assert.Equal(configs[0], config.Master);
}
@ -68,7 +68,7 @@ namespace Squidex.Core
{
var config = LanguagesConfig.Create(Language.DE).Add(Language.IT);
config.ToList().ShouldBeEquivalentTo(
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(
new List<LanguageConfig>
{
new LanguageConfig(Language.DE),
@ -140,7 +140,7 @@ namespace Squidex.Core
.Update(Language.RU, false, false, new[] { Language.DE, Language.IT })
.Remove(Language.IT);
config.ToList().ShouldBeEquivalentTo(
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(
new List<LanguageConfig>
{
new LanguageConfig(Language.DE, false, Language.RU),
@ -169,7 +169,7 @@ namespace Squidex.Core
{
var config = LanguagesConfig.Create(Language.DE).Add(Language.IT).Update(Language.IT, true, false, new[] { Language.DE });
config.ToList().ShouldBeEquivalentTo(
config.OfType<LanguageConfig>().ToList().ShouldBeEquivalentTo(
new List<LanguageConfig>
{
new LanguageConfig(Language.DE),
@ -222,8 +222,10 @@ namespace Squidex.Core
{
var config = LanguagesConfig.Create();
Assert.Equal(0, config.Count);
Assert.NotNull(((IEnumerable)config).GetEnumerator());
Assert.NotNull(((IEnumerable<LanguageConfig>)config).GetEnumerator());
Assert.NotNull(((IEnumerable<IFieldPartitionItem>)config).GetEnumerator());
}
}
}

16
tests/Squidex.Core.Tests/Schemas/AssetsFieldTests.cs

@ -25,7 +25,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
Assert.Equal("my-asset", sut.Name);
}
@ -33,7 +33,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
Assert.NotEqual(sut, sut.Enable());
}
@ -45,7 +45,7 @@ namespace Squidex.Core.Schemas
assetTester.Setup(x => x.IsValidAsync(assetId)).Returns(TaskHelper.True);
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
await sut.ValidateAsync(CreateValue(assetId), false, errors);
@ -55,7 +55,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_assets_are_null_and_valid()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -65,7 +65,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_assets_are_required_and_null()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties { IsRequired = true }, assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { IsRequired = true }, assetTester.Object);
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -76,7 +76,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_assets_are_required_and_empty()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties { IsRequired = true }, assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, new AssetsFieldProperties { IsRequired = true }, assetTester.Object);
await sut.ValidateAsync(CreateValue(), false, errors);
@ -87,7 +87,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_value_is_not_valid()
{
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
await sut.ValidateAsync("invalid", false, errors);
@ -102,7 +102,7 @@ namespace Squidex.Core.Schemas
assetTester.Setup(x => x.IsValidAsync(assetId)).Returns(TaskHelper.False);
var sut = new AssetsField(1, "my-asset", new AssetsFieldProperties(), assetTester.Object);
var sut = new AssetsField(1, "my-asset", Partitioning.Invariant, assetTester.Object);
await sut.ValidateAsync(CreateValue(assetId), false, errors);

12
tests/Squidex.Core.Tests/Schemas/BooleanFieldTests.cs

@ -21,7 +21,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties());
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant);
Assert.Equal("my-bolean", sut.Name);
}
@ -29,7 +29,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new BooleanField(1, "name", new BooleanFieldProperties());
var sut = new BooleanField(1, "name", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -37,7 +37,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_null_boolean_is_valid()
{
var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties());
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -47,7 +47,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_boolean_is_valid()
{
var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties());
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(true), false, errors);
@ -57,7 +57,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_boolean_is_required()
{
var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties { IsRequired = true });
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant, new BooleanFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -68,7 +68,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_value_is_not_valid()
{
var sut = new BooleanField(1, "my-bolean", new BooleanFieldProperties());
var sut = new BooleanField(1, "my-bolean", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue("Invalid"), false, errors);

16
tests/Squidex.Core.Tests/Schemas/DateTimeFieldTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties());
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant);
Assert.Equal("my-datetime", sut.Name);
}
@ -31,7 +31,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties());
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -39,7 +39,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_datetime_is_valid()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties());
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -49,7 +49,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_datetime_is_required()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { IsRequired = true });
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -60,7 +60,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_datetime_is_less_than_min()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { MinValue = FutureDays(10) });
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { MinValue = FutureDays(10) });
await sut.ValidateAsync(CreateValue(FutureDays(0)), false, errors);
@ -71,7 +71,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_datetime_is_greater_than_max()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties { MaxValue = FutureDays(10) });
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties { MaxValue = FutureDays(10) });
await sut.ValidateAsync(CreateValue(FutureDays(20)), false, errors);
@ -82,7 +82,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_value_is_not_valid()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties());
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue("Invalid"), false, errors);
@ -93,7 +93,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_value_is_another_type()
{
var sut = new DateTimeField(1, "my-datetime", new DateTimeFieldProperties());
var sut = new DateTimeField(1, "my-datetime", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(123), false, errors);

32
tests/Squidex.Core.Tests/Schemas/FieldRegistryTests.cs

@ -35,7 +35,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_throw_if_creating_field_and_field_is_not_registered()
{
Assert.Throws<InvalidOperationException>(() => sut.CreateField(1, "name", new InvalidProperties()));
Assert.Throws<InvalidOperationException>(() => sut.CreateField(1, "name", Partitioning.Invariant, new InvalidProperties()));
}
[Fact]
@ -43,37 +43,9 @@ namespace Squidex.Core.Schemas
{
var properties = new NumberFieldProperties();
var field = sut.CreateField(1, "name", properties);
var field = sut.CreateField(1, "name", Partitioning.Invariant, properties);
Assert.Equal(properties, field.RawProperties);
}
[Fact]
public void Should_throw_if_getting_by_type_and_field_is_not_registered()
{
Assert.Throws<InvalidOperationException>(() => sut.FindByPropertiesType(typeof(InvalidProperties)));
}
[Fact]
public void Should_find_registration_by_properties_type()
{
var registry = sut.FindByPropertiesType(typeof(NumberFieldProperties));
Assert.Equal(typeof(NumberFieldProperties), registry.PropertiesType);
}
[Fact]
public void Should_throw_if_getting_by_name_and_field_is_not_registered()
{
Assert.Throws<DomainException>(() => sut.FindByTypeName("invalid"));
}
[Fact]
public void Should_find_registration_by_name()
{
var registry = sut.FindByTypeName("NumberField");
Assert.Equal(typeof(NumberFieldProperties), registry.PropertiesType);
}
}
}

14
tests/Squidex.Core.Tests/Schemas/GeolocationFieldTests.cs

@ -21,7 +21,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties());
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant);
Assert.Equal("my-geolocation", sut.Name);
}
@ -29,7 +29,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties());
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -37,7 +37,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_geolocation_is_valid_null()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties());
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), false, errors);
@ -47,7 +47,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_geolocation_is_valid()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties());
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant);
var geolocation = new JObject(
new JProperty("latitude", 0),
@ -61,7 +61,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_geolocation_has_invalid_properties()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties { IsRequired = true });
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject(
new JProperty("latitude", 200),
@ -76,7 +76,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_geolocation_has_too_many_properties()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties { IsRequired = true });
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true });
var geolocation = new JObject(
new JProperty("invalid", 0),
@ -92,7 +92,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_geolocation_is_required()
{
var sut = new GeolocationField(1, "my-geolocation", new GeolocationFieldProperties { IsRequired = true });
var sut = new GeolocationField(1, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), false, errors);

33
tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs

@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Immutable;
using FluentAssertions;
using Moq;
using Newtonsoft.Json;
@ -33,21 +34,25 @@ namespace Squidex.Core.Schemas.Json
public void Should_serialize_and_deserialize_schema()
{
var schema =
Schema.Create("my-schema", new SchemaProperties())
.AddOrUpdateField(new StringField(1, "my-string",
new StringFieldProperties { Label = "My-String", Pattern = "[0-9]{3}" }))
.AddOrUpdateField(new NumberField(2, "my-number",
new NumberFieldProperties { Hints = "My-Number" }))
.AddOrUpdateField(new BooleanField(3, "my-boolean",
new BooleanFieldProperties())).HideField(3)
.AddOrUpdateField(new JsonField(4, "my-json",
new JsonFieldProperties())).DisableField(4)
.AddOrUpdateField(new DateTimeField(5, "my-datetime",
new DateTimeFieldProperties()))
.AddOrUpdateField(new GeolocationField(6, "my-geolocation",
new GeolocationFieldProperties()))
.AddOrUpdateField(new AssetsField(7, "my-asset",
Schema.Create("user", new SchemaProperties { Hints = "The User" })
.AddOrUpdateField(new JsonField(1, "my-json", Partitioning.Invariant,
new JsonFieldProperties()))
.AddOrUpdateField(new AssetsField(2, "my-assets", Partitioning.Invariant,
new AssetsFieldProperties(), new Mock<IAssetTester>().Object))
.AddOrUpdateField(new StringField(3, "my-string1", Partitioning.Language,
new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ImmutableList.Create("a", "b") }))
.AddOrUpdateField(new StringField(4, "my-string2", Partitioning.Invariant,
new StringFieldProperties { Hints = "My String1" }))
.AddOrUpdateField(new NumberField(5, "my-number", Partitioning.Invariant,
new NumberFieldProperties { MinValue = 1, MaxValue = 10 }))
.AddOrUpdateField(new BooleanField(6, "my-boolean", Partitioning.Invariant,
new BooleanFieldProperties()))
.AddOrUpdateField(new DateTimeField(7, "my-datetime", Partitioning.Invariant,
new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime }))
.AddOrUpdateField(new DateTimeField(8, "my-date", Partitioning.Invariant,
new DateTimeFieldProperties { Editor = DateTimeFieldEditor.Date }))
.AddOrUpdateField(new GeolocationField(9, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()))
.Publish();
var deserialized = sut.Deserialize(sut.Serialize(schema));

8
tests/Squidex.Core.Tests/Schemas/JsonFieldTests.cs

@ -21,7 +21,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new JsonField(1, "my-json", new JsonFieldProperties());
var sut = new JsonField(1, "my-json", Partitioning.Invariant);
Assert.Equal("my-json", sut.Name);
}
@ -29,7 +29,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new JsonField(1, "my-json", new JsonFieldProperties());
var sut = new JsonField(1, "my-json", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -37,7 +37,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_json_is_valid()
{
var sut = new JsonField(1, "my-json", new JsonFieldProperties());
var sut = new JsonField(1, "my-json", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(new JValue(1)), false, errors);
@ -47,7 +47,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_json_is_required()
{
var sut = new JsonField(1, "my-json", new JsonFieldProperties { IsRequired = true });
var sut = new JsonField(1, "my-json", Partitioning.Invariant, new JsonFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(JValue.CreateNull()), false, errors);

16
tests/Squidex.Core.Tests/Schemas/NumberFieldTests.cs

@ -22,7 +22,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties());
var sut = new NumberField(1, "my-number", Partitioning.Invariant);
Assert.Equal("my-number", sut.Name);
}
@ -30,7 +30,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties());
var sut = new NumberField(1, "my-number", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -38,7 +38,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_number_is_valid()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties());
var sut = new NumberField(1, "my-number", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -48,7 +48,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_required()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties { IsRequired = true });
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -59,7 +59,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_less_than_min()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties { MinValue = 10 });
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { MinValue = 10 });
await sut.ValidateAsync(CreateValue(5), false, errors);
@ -70,7 +70,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_greater_than_max()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties { MaxValue = 10 });
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { MaxValue = 10 });
await sut.ValidateAsync(CreateValue(20), false, errors);
@ -81,7 +81,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_not_allowed()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) });
var sut = new NumberField(1, "my-number", Partitioning.Invariant, new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) });
await sut.ValidateAsync(CreateValue(20), false, errors);
@ -92,7 +92,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_value_is_not_valid()
{
var sut = new NumberField(1, "my-number", new NumberFieldProperties());
var sut = new NumberField(1, "my-number", Partitioning.Invariant);
await sut.ValidateAsync(CreateValue("Invalid"), false, errors);

50
tests/Squidex.Core.Tests/Schemas/SchemaTests.cs

@ -75,7 +75,7 @@ namespace Squidex.Core.Schemas
{
AddField();
Assert.Throws<ValidationException>(() => sut.AddOrUpdateField(new NumberField(2, "my-field", new NumberFieldProperties())));
Assert.Throws<ValidationException>(() => sut.AddOrUpdateField(new NumberField(2, "my-field", Partitioning.Invariant)));
}
[Fact]
@ -163,7 +163,7 @@ namespace Squidex.Core.Schemas
{
AddField();
sut = sut.AddOrUpdateField(new NumberField(2, "other-field", new NumberFieldProperties()));
sut = sut.AddOrUpdateField(new NumberField(2, "other-field", Partitioning.Invariant));
Assert.Throws<ValidationException>(() => sut.RenameField(2, "my-field"));
}
@ -256,9 +256,9 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_reorder_fields()
{
var field1 = new StringField(1, "1", new StringFieldProperties());
var field2 = new StringField(2, "2", new StringFieldProperties());
var field3 = new StringField(3, "3", new StringFieldProperties());
var field1 = new StringField(1, "1", Partitioning.Invariant);
var field2 = new StringField(2, "2", Partitioning.Invariant);
var field3 = new StringField(3, "3", Partitioning.Invariant);
sut = sut.AddOrUpdateField(field1);
sut = sut.AddOrUpdateField(field2);
@ -271,8 +271,8 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_throw_if_not_all_fields_are_covered_for_reordering()
{
var field1 = new StringField(1, "1", new StringFieldProperties());
var field2 = new StringField(2, "2", new StringFieldProperties());
var field1 = new StringField(1, "1", Partitioning.Invariant);
var field2 = new StringField(2, "2", Partitioning.Invariant);
sut = sut.AddOrUpdateField(field1);
sut = sut.AddOrUpdateField(field2);
@ -283,8 +283,8 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_throw_if_field_to_reorder_does_not_exist()
{
var field1 = new StringField(1, "1", new StringFieldProperties());
var field2 = new StringField(2, "2", new StringFieldProperties());
var field1 = new StringField(1, "1", Partitioning.Invariant);
var field2 = new StringField(2, "2", Partitioning.Invariant);
sut = sut.AddOrUpdateField(field1);
sut = sut.AddOrUpdateField(field2);
@ -297,7 +297,7 @@ namespace Squidex.Core.Schemas
{
var languagesConfig = LanguagesConfig.Create(Language.DE, Language.EN);
var jsonSchema = BuildMixedSchema().BuildJsonSchema(languagesConfig, (n, s) => new JsonSchema4 { SchemaReference = s });
var jsonSchema = BuildMixedSchema().BuildJsonSchema(languagesConfig.ToResolver(), (n, s) => new JsonSchema4 { SchemaReference = s });
Assert.NotNull(jsonSchema);
}
@ -307,42 +307,40 @@ namespace Squidex.Core.Schemas
{
var languagesConfig = LanguagesConfig.Create(Language.DE, Language.EN);
var edmModel = BuildMixedSchema().BuildEdmType(languagesConfig, x => x);
var edmModel = BuildMixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x);
Assert.NotNull(edmModel);
}
private static Schema BuildMixedSchema()
{
var allowedValues = new[] { "1", "2" }.ToImmutableList();
var schema =
Schema.Create("user", new SchemaProperties { Hints = "The User" })
.AddOrUpdateField(new JsonField(1, "my-json",
.AddOrUpdateField(new JsonField(1, "my-json", Partitioning.Invariant,
new JsonFieldProperties()))
.AddOrUpdateField(new StringField(2, "my-string1",
new StringFieldProperties { Label = "My String1", IsLocalizable = true, IsRequired = true, AllowedValues = allowedValues }))
.AddOrUpdateField(new StringField(3, "my-string2",
.AddOrUpdateField(new AssetsField(2, "my-assets", Partitioning.Invariant,
new AssetsFieldProperties(), new Mock<IAssetTester>().Object))
.AddOrUpdateField(new StringField(3, "my-string1", Partitioning.Language,
new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ImmutableList.Create("a", "b") }))
.AddOrUpdateField(new StringField(4, "my-string2", Partitioning.Invariant,
new StringFieldProperties { Hints = "My String1" }))
.AddOrUpdateField(new NumberField(4, "my-number",
.AddOrUpdateField(new NumberField(5, "my-number", Partitioning.Invariant,
new NumberFieldProperties { MinValue = 1, MaxValue = 10 }))
.AddOrUpdateField(new BooleanField(5, "my-boolean",
.AddOrUpdateField(new BooleanField(6, "my-boolean", Partitioning.Invariant,
new BooleanFieldProperties()))
.AddOrUpdateField(new DateTimeField(6, "my-datetime",
.AddOrUpdateField(new DateTimeField(7, "my-datetime", Partitioning.Invariant,
new DateTimeFieldProperties { Editor = DateTimeFieldEditor.DateTime }))
.AddOrUpdateField(new DateTimeField(7, "my-date",
.AddOrUpdateField(new DateTimeField(8, "my-date", Partitioning.Invariant,
new DateTimeFieldProperties { Editor = DateTimeFieldEditor.Date }))
.AddOrUpdateField(new GeolocationField(8, "my-geolocation",
new GeolocationFieldProperties()))
.AddOrUpdateField(new AssetsField(9, "my-assets",
new AssetsFieldProperties(), new Mock<IAssetTester>().Object));
.AddOrUpdateField(new GeolocationField(9, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()));
return schema;
}
private NumberField AddField()
{
var field = new NumberField(1, "my-field", new NumberFieldProperties());
var field = new NumberField(1, "my-field", Partitioning.Invariant);
sut = sut.AddOrUpdateField(field);

18
tests/Squidex.Core.Tests/Schemas/StringFieldTests.cs

@ -22,7 +22,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_instantiate_field()
{
var sut = new StringField(1, "my-string", new StringFieldProperties());
var sut = new StringField(1, "my-string", Partitioning.Invariant);
Assert.Equal("my-string", sut.Name);
}
@ -30,7 +30,7 @@ namespace Squidex.Core.Schemas
[Fact]
public void Should_clone_object()
{
var sut = new StringField(1, "my-string", new StringFieldProperties());
var sut = new StringField(1, "my-string", Partitioning.Invariant);
Assert.NotEqual(sut, sut.Enable());
}
@ -38,7 +38,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_not_add_error_if_string_is_valid()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { Label = "<FIELD>" });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Label = "<FIELD>" });
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -48,7 +48,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_string_is_required()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { IsRequired = true });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { IsRequired = true });
await sut.ValidateAsync(CreateValue(null), false, errors);
@ -59,7 +59,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_string_is_shorter_than_min_length()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { MinLength = 10 });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { MinLength = 10 });
await sut.ValidateAsync(CreateValue("123"), false, errors);
@ -70,7 +70,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_string_is_longer_than_max_length()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { MaxLength = 5 });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { MaxLength = 5 });
await sut.ValidateAsync(CreateValue("12345678"), false, errors);
@ -81,7 +81,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_string_not_allowed()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") });
await sut.ValidateAsync(CreateValue("Bar"), false, errors);
@ -92,7 +92,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_not_valid_pattern()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { Pattern = "[0-9]{3}" });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Pattern = "[0-9]{3}" });
await sut.ValidateAsync(CreateValue("abc"), false, errors);
@ -103,7 +103,7 @@ namespace Squidex.Core.Schemas
[Fact]
public async Task Should_add_errors_if_number_is_not_valid_pattern_with_message()
{
var sut = new StringField(1, "my-string", new StringFieldProperties { Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message" });
var sut = new StringField(1, "my-string", Partitioning.Invariant, new StringFieldProperties { Pattern = "[0-9]{3}", PatternMessage = "Custom Error Message" });
await sut.ValidateAsync(CreateValue("abc"), false, errors);

4
tests/Squidex.Infrastructure.Tests/LanguageTests.cs

@ -97,10 +97,10 @@ namespace Squidex.Infrastructure
}
[Theory]
[InlineData("iv", "iv")]
[InlineData("en", "en")]
[InlineData("EN", "en")]
[InlineData("en ", "en")]
[InlineData("EN", "en")]
[InlineData("EN ", "en")]
public void Should_parse_valid_languages(string input, string languageCode)
{
var language = Language.ParseOrNull(input);

20
tests/Squidex.Read.Tests/MongoDb/Contents/ODataQueryTests.cs

@ -18,6 +18,7 @@ using Squidex.Core;
using Squidex.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Read.Apps;
using Squidex.Read.Contents.Builders;
using Squidex.Read.MongoDb.Contents.Visitors;
using Squidex.Read.Schemas;
@ -31,15 +32,15 @@ namespace Squidex.Read.MongoDb.Contents
{
private readonly Schema schema =
Schema.Create("user", new SchemaProperties { Hints = "The User" })
.AddOrUpdateField(new StringField(1, "firstName",
new StringFieldProperties { Label = "FirstName", IsLocalizable = true, IsRequired = true, AllowedValues = new[] { "1", "2" }.ToImmutableList() }))
.AddOrUpdateField(new StringField(2, "lastName",
.AddOrUpdateField(new StringField(1, "firstName", Partitioning.Language,
new StringFieldProperties { Label = "FirstName", IsRequired = true, AllowedValues = new[] { "1", "2" }.ToImmutableList() }))
.AddOrUpdateField(new StringField(2, "lastName", Partitioning.Language,
new StringFieldProperties { Hints = "Last Name", Editor = StringFieldEditor.Input }))
.AddOrUpdateField(new BooleanField(3, "isAdmin",
.AddOrUpdateField(new BooleanField(3, "isAdmin", Partitioning.Invariant,
new BooleanFieldProperties()))
.AddOrUpdateField(new NumberField(4, "age",
.AddOrUpdateField(new NumberField(4, "age", Partitioning.Invariant,
new NumberFieldProperties { MinValue = 1, MaxValue = 10 }))
.AddOrUpdateField(new DateTimeField(5, "birthday",
.AddOrUpdateField(new DateTimeField(5, "birthday", Partitioning.Invariant,
new DateTimeFieldProperties()));
private readonly IBsonSerializerRegistry registry = BsonSerializer.SerializerRegistry;
@ -61,7 +62,12 @@ namespace Squidex.Read.MongoDb.Contents
schemaEntity.Setup(x => x.Version).Returns(3);
schemaEntity.Setup(x => x.Schema).Returns(schema);
edmModel = builder.BuildEdmModel(schemaEntity.Object, languagesConfig);
var appEntity = new Mock<IAppEntity>();
appEntity.Setup(x => x.Id).Returns(Guid.NewGuid());
appEntity.Setup(x => x.Version).Returns(3);
appEntity.Setup(x => x.PartitionResolver).Returns(languagesConfig.ToResolver());
edmModel = builder.BuildEdmModel(schemaEntity.Object, appEntity.Object);
}
[Fact]

3
tests/Squidex.Write.Tests/Contents/ContentCommandHandlerTests.cs

@ -42,7 +42,7 @@ namespace Squidex.Write.Contents
{
var schema =
Schema.Create("my-schema", new SchemaProperties())
.AddOrUpdateField(new NumberField(1, "my-field",
.AddOrUpdateField(new NumberField(1, "my-field", Partitioning.Invariant,
new NumberFieldProperties { IsRequired = true }));
content = new ContentDomainObject(contentId, -1);
@ -50,6 +50,7 @@ namespace Squidex.Write.Contents
sut = new ContentCommandHandler(Handler, appProvider.Object, schemaProvider.Object);
appEntity.Setup(x => x.LanguagesConfig).Returns(languagesConfig);
appEntity.Setup(x => x.PartitionResolver).Returns(languagesConfig.ToResolver());
appProvider.Setup(x => x.FindAppByIdAsync(AppId)).Returns(Task.FromResult(appEntity.Object));
schemaEntity.Setup(x => x.Schema).Returns(schema);

Loading…
Cancel
Save