Browse Source

Performance optimizations. Get rid of slow immutable classes.

pull/335/head
Sebastian Stehle 8 years ago
parent
commit
f2418b30f1
  1. 39
      src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs
  2. 14
      src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs
  3. 30
      src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs
  4. 9
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
  5. 4
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
  6. 10
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs
  7. 8
      src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs
  8. 3
      src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs
  9. 17
      src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs
  10. 32
      src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs
  11. 68
      src/Squidex.Domain.Apps.Core.Model/DictionaryWrapper{TKey,TValue}.cs
  12. 4
      src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
  13. 4
      src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
  14. 29
      src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs
  15. 20
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs
  16. 5
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs
  17. 32
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs
  18. 11
      src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
  19. 4
      src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
  20. 4
      src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
  21. 4
      src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
  22. 4
      src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs
  23. 32
      src/Squidex.Infrastructure/CollectionExtensions.cs
  24. 21
      src/Squidex.Infrastructure/Collections/ArrayDictionary.cs
  25. 163
      src/Squidex.Infrastructure/Collections/ArrayDictionary{TKey,TValue}.cs
  26. 36
      src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs
  27. 154
      src/Squidex.Infrastructure/StringExtensions.cs
  28. 3
      src/Squidex.Infrastructure/ValidationError.cs
  29. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs
  30. 4
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs
  31. 4
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs
  32. 4
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs
  33. 4
      src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs
  34. 2
      src/Squidex/Config/Domain/SerializationServices.cs
  35. 10
      tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs
  36. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs
  37. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs
  38. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs
  39. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs
  40. 4
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs
  41. 6
      tests/Squidex.Domain.Apps.Core.Tests/TestData.cs
  42. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs
  43. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs
  44. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs
  45. 6
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleGrainTests.cs
  46. 10
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs
  47. 10
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs
  48. 31
      tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs

39
src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs

@ -5,49 +5,60 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class AppClients : DictionaryWrapper<string, AppClient>
public sealed class AppClients : ArrayDictionary<string, AppClient>
{
public static readonly AppClients Empty = new AppClients();
private AppClients()
: base(ImmutableDictionary<string, AppClient>.Empty)
{
}
public AppClients(ImmutableDictionary<string, AppClient> inner)
: base(inner)
public AppClients(KeyValuePair<string, AppClient>[] items)
: base(items)
{
}
[Pure]
public AppClients Add(string id, AppClient client)
public AppClients Revoke(string id)
{
Guard.NotNullOrEmpty(id, nameof(id));
Guard.NotNull(client, nameof(client));
return new AppClients(Inner.Add(id, client));
return new AppClients(Without(id));
}
[Pure]
public AppClients Add(string id, string secret)
public AppClients Add(string id, AppClient client)
{
Guard.NotNullOrEmpty(id, nameof(id));
Guard.NotNull(client, nameof(client));
if (ContainsKey(id))
{
throw new ArgumentException("Id already exists.", nameof(id));
}
return new AppClients(Inner.Add(id, new AppClient(id, secret, Role.Editor)));
return new AppClients(With(id, client));
}
[Pure]
public AppClients Revoke(string id)
public AppClients Add(string id, string secret)
{
Guard.NotNullOrEmpty(id, nameof(id));
return new AppClients(Inner.Remove(id));
if (ContainsKey(id))
{
throw new ArgumentException("Id already exists.", nameof(id));
}
return new AppClients(With(id, new AppClient(id, secret, Role.Editor)));
}
[Pure]
@ -60,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this;
}
return new AppClients(Inner.SetItem(id, client.Rename(newName)));
return new AppClients(With(id, client.Rename(newName)));
}
[Pure]
@ -73,7 +84,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this;
}
return new AppClients(Inner.SetItem(id, client.Update(role)));
return new AppClients(With(id, client.Update(role)));
}
}
}

14
src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs

@ -5,23 +5,23 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class AppContributors : DictionaryWrapper<string, string>
public sealed class AppContributors : ArrayDictionary<string, string>
{
public static readonly AppContributors Empty = new AppContributors();
private AppContributors()
: base(ImmutableDictionary<string, string>.Empty)
{
}
public AppContributors(ImmutableDictionary<string, string> inner)
: base(inner)
public AppContributors(KeyValuePair<string, string>[] items)
: base(items)
{
}
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Apps
Guard.NotNullOrEmpty(contributorId, nameof(contributorId));
Guard.NotNullOrEmpty(role, nameof(role));
return new AppContributors(Inner.SetItem(contributorId, role));
return new AppContributors(With(contributorId, role));
}
[Pure]
@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{
Guard.NotNullOrEmpty(contributorId, nameof(contributorId));
return new AppContributors(Inner.Remove(contributorId));
return new AppContributors(Without(contributorId));
}
}
}

30
src/Squidex.Domain.Apps.Core.Model/Apps/AppPatterns.cs

@ -4,39 +4,45 @@
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class AppPatterns : DictionaryWrapper<Guid, AppPattern>
public sealed class AppPatterns : ArrayDictionary<Guid, AppPattern>
{
public static readonly AppPatterns Empty = new AppPatterns();
private AppPatterns()
: base(ImmutableDictionary<Guid, AppPattern>.Empty)
{
}
public AppPatterns(ImmutableDictionary<Guid, AppPattern> inner)
: base(inner)
public AppPatterns(KeyValuePair<Guid, AppPattern>[] items)
: base(items)
{
}
[Pure]
public AppPatterns Add(Guid id, string name, string pattern, string message)
public AppPatterns Remove(Guid id)
{
var newPattern = new AppPattern(name, pattern, message);
return new AppPatterns(Inner.Add(id, newPattern));
return new AppPatterns(Without(id));
}
[Pure]
public AppPatterns Remove(Guid id)
public AppPatterns Add(Guid id, string name, string pattern, string message)
{
return new AppPatterns(Inner.Remove(id));
var newPattern = new AppPattern(name, pattern, message);
if (ContainsKey(id))
{
throw new ArgumentException("Id already exists.", nameof(id));
}
return new AppPatterns(With(id, newPattern));
}
[Pure]
@ -50,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this;
}
return new AppPatterns(Inner.SetItem(id, appPattern.Update(name, pattern, message)));
return new AppPatterns(With(id, appPattern.Update(name, pattern, message)));
}
}
}

9
src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs

@ -7,7 +7,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -31,7 +31,12 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
var json = serializer.Deserialize<Dictionary<string, JsonAppClient>>(reader);
return new AppClients(json.ToImmutableDictionary(x => x.Key, x => x.Value.ToClient()));
return new AppClients(json.Select(Convert).ToArray());
}
private static KeyValuePair<string, AppClient> Convert(KeyValuePair<string, JsonAppClient> kvp)
{
return new KeyValuePair<string, AppClient>(kvp.Key, kvp.Value.ToClient());
}
}
}

4
src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs

@ -7,7 +7,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
var json = serializer.Deserialize<Dictionary<string, string>>(reader);
return new AppContributors(json.ToImmutableDictionary());
return new AppContributors(json.ToArray());
}
}
}

10
src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppPatternsConverter.cs

@ -4,9 +4,10 @@
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@ -30,7 +31,12 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
var json = serializer.Deserialize<Dictionary<Guid, JsonAppPattern>>(reader);
return new AppPatterns(json.ToImmutableDictionary(x => x.Key, x => x.Value.ToPattern()));
return new AppPatterns(json.Select(Convert).ToArray());
}
private static KeyValuePair<Guid, AppPattern> Convert(KeyValuePair<Guid, JsonAppPattern> kvp)
{
return new KeyValuePair<Guid, AppPattern>(kvp.Key, kvp.Value.ToPattern());
}
}
}

8
src/Squidex.Domain.Apps.Core.Model/Apps/Json/RolesConverter.cs

@ -10,7 +10,6 @@ using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Security;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Squidex.Domain.Apps.Core.Apps.Json
@ -33,7 +32,12 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{
var json = serializer.Deserialize<Dictionary<string, string[]>>(reader);
return new Roles(json.ToImmutableDictionary(x => x.Key, x => new Role(x.Key, new PermissionSet(x.Value))));
return new Roles(json.Select(Convert).ToArray());
}
private static KeyValuePair<string, Role> Convert(KeyValuePair<string, string[]> kvp)
{
return new KeyValuePair<string, Role>(kvp.Key, new Role(kvp.Key, new PermissionSet(kvp.Value)));
}
}
}

3
src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs

@ -14,7 +14,6 @@ namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class LanguageConfig : IFieldPartitionItem
{
private static readonly Language[] DefaultFallback = Array.Empty<Language>();
private readonly Language language;
private readonly Language[] languageFallbacks;
@ -57,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Apps
IsOptional = isOptional;
this.language = language;
this.languageFallbacks = fallback ?? DefaultFallback;
this.languageFallbacks = fallback ?? Array.Empty<Language>();
}
}
}

17
src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs

@ -8,10 +8,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using System.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps
{
@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{
public static readonly LanguagesConfig English = Build(Language.EN);
private readonly ImmutableDictionary<Language, LanguageConfig> languages;
private readonly ArrayDictionary<Language, LanguageConfig> languages;
private readonly LanguageConfig master;
public LanguageConfig Master
@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.Apps
get { return languages.Count; }
}
private LanguagesConfig(ImmutableDictionary<Language, LanguageConfig> languages, LanguageConfig master, bool checkMaster = true)
private LanguagesConfig(ArrayDictionary<Language, LanguageConfig> languages, LanguageConfig master, bool checkMaster = true)
{
if (checkMaster)
{
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{
Guard.NotNull(configs, nameof(configs));
return new LanguagesConfig(configs.ToImmutableDictionary(x => x.Language), configs.FirstOrDefault());
return new LanguagesConfig(configs.ToArrayDictionary(x => x.Language), configs.FirstOrDefault());
}
public static LanguagesConfig Build(params LanguageConfig[] configs)
@ -100,7 +100,12 @@ namespace Squidex.Domain.Apps.Core.Apps
{
Guard.NotNull(config, nameof(config));
return new LanguagesConfig(languages.SetItem(config.Language, config), Master?.Language == config.Language ? config : Master);
var newLanguages =
new ArrayDictionary<Language, LanguageConfig>(languages.With(config.Language, config));
var newMaster = Master?.Language == config.Language ? config : Master;
return new LanguagesConfig(newLanguages, newMaster);
}
[Pure]
@ -114,7 +119,7 @@ namespace Squidex.Domain.Apps.Core.Apps
config.Language,
config.IsOptional,
config.LanguageFallbacks.Except(new[] { language })))
.ToImmutableDictionary(x => x.Language);
.ToArrayDictionary(x => x.Language);
var newMaster =
newLanguages.Values.FirstOrDefault(x => x.Language == Master.Language) ??

32
src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs

@ -6,38 +6,44 @@
// ==========================================================================
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using System.Linq;
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class Roles : DictionaryWrapper<string, Role>
public sealed class Roles : ArrayDictionary<string, Role>
{
public static readonly Roles Empty = new Roles();
private Roles()
: base(ImmutableDictionary<string, Role>.Empty)
{
}
public Roles(ImmutableDictionary<string, Role> inner)
: base(inner)
public Roles(KeyValuePair<string, Role>[] items)
: base(items)
{
}
[Pure]
public Roles Add(string name)
public Roles Remove(string name)
{
var newRole = new Role(name);
return new Roles(Inner.Add(name, newRole));
return new Roles(Without(name));
}
[Pure]
public Roles Remove(string name)
public Roles Add(string name)
{
return new Roles(Inner.Remove(name));
var newRole = new Role(name);
if (ContainsKey(name))
{
throw new ArgumentException("Name already exists.", nameof(name));
}
return new Roles(With(name, newRole));
}
[Pure]
@ -51,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this;
}
return new Roles(Inner.SetItem(name, role.Update(permissions)));
return new Roles(With(name, role.Update(permissions)));
}
public static Roles CreateDefaults(string app)
@ -63,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Apps
[Role.Editor] = Role.CreateEditor(app),
[Role.Owner] = Role.CreateOwner(app),
[Role.Reader] = Role.CreateReader(app)
}.ToImmutableDictionary());
}.ToArray());
}
}
}

68
src/Squidex.Domain.Apps.Core.Model/DictionaryWrapper{TKey,TValue}.cs

@ -1,68 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Squidex.Domain.Apps.Core
{
public abstract class DictionaryWrapper<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
private readonly ImmutableDictionary<TKey, TValue> inner;
public TValue this[TKey key]
{
get { return inner[key]; }
}
public IEnumerable<TKey> Keys
{
get { return inner.Keys; }
}
public IEnumerable<TValue> Values
{
get { return inner.Values; }
}
public int Count
{
get { return inner.Count; }
}
protected ImmutableDictionary<TKey, TValue> Inner
{
get { return inner; }
}
protected DictionaryWrapper(ImmutableDictionary<TKey, TValue> inner)
{
this.inner = inner;
}
public bool ContainsKey(TKey key)
{
return inner.ContainsKey(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return inner.TryGetValue(key, out value);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return inner.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return inner.GetEnumerator();
}
}
}

4
src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs

@ -5,15 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
{
[TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger
{
public ImmutableList<ContentChangedTriggerSchema> Schemas { get; set; }
public ReadOnlyCollection<ContentChangedTriggerSchema> Schemas { get; set; }
public bool HandleAll { get; set; }

4
src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs

@ -5,8 +5,8 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas
{
@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
public int? AspectHeight { get; set; }
public ImmutableList<string> AllowedExtensions { get; set; }
public ReadOnlyCollection<string> AllowedExtensions { get; set; }
public override T Accept<T>(IFieldPropertiesVisitor<T> visitor)
{

29
src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using System.Linq;
using Squidex.Infrastructure;
@ -17,10 +16,13 @@ namespace Squidex.Domain.Apps.Core.Schemas
public sealed class FieldCollection<T> : Cloneable<FieldCollection<T>> where T : IField
{
public static readonly FieldCollection<T> Empty = new FieldCollection<T>();
private static readonly Dictionary<long, T> EmptyById = new Dictionary<long, T>();
private static readonly Dictionary<string, T> EmptyByString = new Dictionary<string, T>();
private ImmutableArray<T> fieldsOrdered = ImmutableArray<T>.Empty;
private ImmutableDictionary<long, T> fieldsById;
private ImmutableDictionary<string, T> fieldsByName;
private T[] fieldsOrdered;
private Dictionary<long, T> fieldsById;
private Dictionary<string, T> fieldsByName;
public IReadOnlyList<T> Ordered
{
@ -35,11 +37,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
if (fieldsOrdered.Length == 0)
{
fieldsById = ImmutableDictionary<long, T>.Empty;
fieldsById = EmptyById;
}
else
{
fieldsById = fieldsOrdered.ToImmutableDictionary(x => x.Id);
fieldsById = fieldsOrdered.ToDictionary(x => x.Id);
}
}
@ -55,11 +57,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
if (fieldsOrdered.Length == 0)
{
fieldsByName = ImmutableDictionary<string, T>.Empty;
fieldsByName = EmptyByString;
}
else
{
fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
fieldsByName = fieldsOrdered.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase);
}
}
@ -69,13 +71,14 @@ namespace Squidex.Domain.Apps.Core.Schemas
private FieldCollection()
{
fieldsOrdered = Array.Empty<T>();
}
public FieldCollection(T[] fields)
{
Guard.NotNull(fields, nameof(fields));
fieldsOrdered = ImmutableArray.Create(fields);
fieldsOrdered = fields;
}
protected override void OnCloned()
@ -94,7 +97,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone =>
{
clone.fieldsOrdered = fieldsOrdered.Remove(field);
clone.fieldsOrdered = fieldsOrdered.Where(x => x.Id != fieldId).ToArray();
});
}
@ -110,7 +113,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone =>
{
clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToImmutableArray();
clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToArray();
});
}
@ -126,7 +129,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone =>
{
clone.fieldsOrdered = clone.fieldsOrdered.Add(field);
clone.fieldsOrdered = clone.fieldsOrdered.Union(Enumerable.Repeat(field, 1)).ToArray();
});
}
@ -154,7 +157,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone =>
{
clone.fieldsOrdered = clone.fieldsOrdered.Replace(field, typedField);
clone.fieldsOrdered = clone.fieldsOrdered.Select(x => ReferenceEquals(x, field) ? newField : x).ToArray();
});
}
}

20
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonFieldModel.cs

@ -6,6 +6,10 @@
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Infrastructure;
using System;
using System.Linq;
using P = Squidex.Domain.Apps.Core.Partitioning;
namespace Squidex.Domain.Apps.Core.Schemas.Json
{
@ -34,5 +38,21 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
[JsonProperty]
public JsonNestedFieldModel[] Children { get; set; }
public RootField ToField()
{
var partitioning = P.FromString(Partitioning);
if (Properties is ArrayFieldProperties arrayProperties)
{
var nested = Children?.ToArray(n => n.ToNestedField()) ?? Array.Empty<NestedField>();
return new ArrayField(Id, Name, partitioning, nested, arrayProperties, this);
}
else
{
return Properties.CreateRootField(Id, Name, partitioning, this);
}
}
}
}

5
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs

@ -30,5 +30,10 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
{
get { return false; }
}
public NestedField ToNestedField()
{
return Properties.CreateNestedField(Id, Name, this);
}
}
}

32
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs

@ -14,8 +14,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class JsonSchemaModel
{
private static readonly RootField[] Empty = Array.Empty<RootField>();
[JsonProperty]
public string Name { get; set; }
@ -73,35 +71,9 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
return null;
}
public Schema ToSchema(FieldRegistry registry)
public Schema ToSchema()
{
var fields = Empty;
if (Fields != null)
{
fields = Fields.ToArray(fieldModel =>
{
var parititonKey = new Partitioning(fieldModel.Partitioning);
RootField field;
if (fieldModel.Properties is ArrayFieldProperties arrayProperties && fieldModel.Children?.Length > 0)
{
var nestedFields = fieldModel.Children.ToArray(nestedFieldModel =>
{
return registry.CreateNestedField(nestedFieldModel.Id, nestedFieldModel.Name, nestedFieldModel.Properties, nestedFieldModel);
});
field = new ArrayField(fieldModel.Id, fieldModel.Name, parititonKey, nestedFields, arrayProperties, fieldModel);
}
else
{
field = registry.CreateRootField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties, fieldModel);
}
return field;
});
}
var fields = Fields.ToArray(f => f.ToField()) ?? Array.Empty<RootField>();
return new Schema(Name, fields, Properties, IsPublished);
}

11
src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs

@ -14,15 +14,6 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class SchemaConverter : JsonClassConverter<Schema>
{
private readonly FieldRegistry fieldRegistry;
public SchemaConverter(FieldRegistry fieldRegistry)
{
Guard.NotNull(fieldRegistry, nameof(fieldRegistry));
this.fieldRegistry = fieldRegistry;
}
protected override void WriteValue(JsonWriter writer, Schema value, JsonSerializer serializer)
{
serializer.Serialize(writer, new JsonSchemaModel(value));
@ -30,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
protected override Schema ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
return serializer.Deserialize<JsonSchemaModel>(reader).ToSchema(fieldRegistry);
return serializer.Deserialize<JsonSchemaModel>(reader).ToSchema();
}
}
}

4
src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs

@ -5,15 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas
{
[TypeName("NumberField")]
public sealed class NumberFieldProperties : FieldProperties
{
public ImmutableList<double> AllowedValues { get; set; }
public ReadOnlyCollection<double> AllowedValues { get; set; }
public double? MaxValue { get; set; }

4
src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs

@ -5,15 +5,15 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas
{
[TypeName("StringField")]
public sealed class StringFieldProperties : FieldProperties
{
public ImmutableList<string> AllowedValues { get; set; }
public ReadOnlyCollection<string> AllowedValues { get; set; }
public int? MinLength { get; set; }

4
src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs

@ -6,14 +6,14 @@
// ==========================================================================
using Squidex.Infrastructure;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas
{
[TypeName("TagsField")]
public sealed class TagsFieldProperties : FieldProperties
{
public ImmutableList<string> AllowedValues { get; set; }
public ReadOnlyCollection<string> AllowedValues { get; set; }
public int? MinItems { get; set; }

4
src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
{
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders
public StringFieldBuilder AsDropDown(params string[] values)
{
Properties<StringFieldProperties>().AllowedValues = ImmutableList.Create(values);
Properties<StringFieldProperties>().AllowedValues = ReadOnlyCollection.Create(values);
Properties<StringFieldProperties>().Editor = StringFieldEditor.Dropdown;
return this;

32
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
namespace Squidex.Infrastructure
@ -21,37 +20,6 @@ namespace Squidex.Infrastructure
return enumerable.OrderBy(x => random.Next()).ToList();
}
public static ImmutableDictionary<TKey, TValue> SetItem<TKey, TValue>(this ImmutableDictionary<TKey, TValue> dictionary, TKey key, Func<TValue, TValue> updater)
{
if (dictionary.TryGetValue(key, out var value))
{
var newValue = updater(value);
if (!Equals(newValue, value))
{
return dictionary.SetItem(key, newValue);
}
}
return dictionary;
}
public static bool TryGetValue<TKey, TValue, TBase>(this IReadOnlyDictionary<TKey, TValue> values, TKey key, out TBase item) where TValue : TBase
{
if (values.TryGetValue(key, out var value))
{
item = value;
return true;
}
else
{
item = default(TBase);
return false;
}
}
public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> source)
{
return source ?? Enumerable.Empty<T>();

21
src/Squidex.Infrastructure/Collections/ArrayDictionary.cs

@ -0,0 +1,21 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
namespace Squidex.Infrastructure.Collections
{
public static class ArrayDictionary
{
public static ArrayDictionary<TKey, TValue> ToArrayDictionary<TKey, TValue>(this IEnumerable<TValue> source, Func<TValue, TKey> keyExtractor)
{
return new ArrayDictionary<TKey, TValue>(source.Select(x => new KeyValuePair<TKey, TValue>(keyExtractor(x), x)).ToArray());
}
}
}

163
src/Squidex.Infrastructure/Collections/ArrayDictionary{TKey,TValue}.cs

@ -0,0 +1,163 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Squidex.Infrastructure.Collections
{
public class ArrayDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
private readonly IEqualityComparer<TKey> keyComparer;
private readonly KeyValuePair<TKey, TValue>[] items;
public TValue this[TKey key]
{
get
{
if (!TryGetValue(key, out var value))
{
throw new KeyNotFoundException();
}
return value;
}
}
public IEnumerable<TKey> Keys
{
get { return items.Select(x => x.Key); }
}
public IEnumerable<TValue> Values
{
get { return items.Select(x => x.Value); }
}
public int Count
{
get { return items.Length; }
}
public ArrayDictionary()
: this(EqualityComparer<TKey>.Default, Array.Empty<KeyValuePair<TKey, TValue>>())
{
}
public ArrayDictionary(KeyValuePair<TKey, TValue>[] items)
: this(EqualityComparer<TKey>.Default, items)
{
}
public ArrayDictionary(IEqualityComparer<TKey> keyComparer, KeyValuePair<TKey, TValue>[] items)
{
Guard.NotNull(items, nameof(items));
Guard.NotNull(keyComparer, nameof(keyComparer));
this.items = items;
this.keyComparer = keyComparer;
}
public KeyValuePair<TKey, TValue>[] With(TKey key, TValue value)
{
var result = new List<KeyValuePair<TKey, TValue>>(Math.Max(items.Length, 1));
var wasReplaced = false;
for (var i = 0; i < items.Length; i++)
{
var item = items[i];
if (wasReplaced || !keyComparer.Equals(item.Key, key))
{
result.Add(item);
}
else
{
result.Add(new KeyValuePair<TKey, TValue>(key, value));
wasReplaced = true;
}
}
if (!wasReplaced)
{
result.Add(new KeyValuePair<TKey, TValue>(key, value));
}
return result.ToArray();
}
public KeyValuePair<TKey, TValue>[] Without(TKey key)
{
var result = new List<KeyValuePair<TKey, TValue>>(Math.Max(items.Length, 1));
var wasRemoved = false;
for (var i = 0; i < items.Length; i++)
{
var item = items[i];
if (wasRemoved || !keyComparer.Equals(item.Key, key))
{
result.Add(item);
}
else
{
wasRemoved = true;
}
}
return result.ToArray();
}
public bool ContainsKey(TKey key)
{
for (var i = 0; i < items.Length; i++)
{
if (keyComparer.Equals(items[i].Key, key))
{
return true;
}
}
return false;
}
public bool TryGetValue(TKey key, out TValue value)
{
for (var i = 0; i < items.Length; i++)
{
if (keyComparer.Equals(items[i].Key, key))
{
value = items[i].Value;
return true;
}
}
value = default(TValue);
return false;
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetEnumerable(items).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return items.GetEnumerator();
}
private static IEnumerable<T2> GetEnumerable<T2>(T2[] array)
{
return array;
}
}
}

36
src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Squidex.Infrastructure.Collections
{
public static class ReadOnlyCollection
{
private static class Empties<T>
{
public static readonly ReadOnlyCollection<T> Collection = new ReadOnlyCollection<T>(new List<T>());
}
public static ReadOnlyCollection<T> Create<T>(params T[] items)
{
return new ReadOnlyCollection<T>(items.ToList());
}
public static ReadOnlyCollection<T> Empty<T>()
{
return Empties<T>.Collection;
}
public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(this IEnumerable<T> source)
{
return new ReadOnlyCollection<T>(source.ToList());
}
}
}

154
src/Squidex.Infrastructure/StringExtensions.cs

@ -15,6 +15,7 @@ namespace Squidex.Infrastructure
{
public static class StringExtensions
{
private const char NullChar = (char)0;
private static readonly Regex SlugRegex = new Regex("^[a-z0-9]+(\\-[a-z0-9]+)*$", RegexOptions.Compiled);
private static readonly Regex PropertyNameRegex = new Regex("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$", RegexOptions.Compiled);
private static readonly Dictionary<char, string> LowerCaseDiacritics;
@ -325,36 +326,97 @@ namespace Squidex.Infrastructure
public static string ToPascalCase(this string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var sb = new StringBuilder();
foreach (var part in value.Split(new[] { '-', '_', ' ' }, StringSplitOptions.RemoveEmptyEntries))
var last = NullChar;
var length = 0;
for (var i = 0; i < value.Length; i++)
{
if (part.Length < 2)
var c = value[i];
if (c == '-' || c == '_' || c == ' ')
{
sb.Append(part.ToUpper());
if (last != NullChar)
{
sb.Append(char.ToUpperInvariant(last));
}
last = NullChar;
length = 0;
}
else
{
sb.Append(char.ToUpper(part[0]));
sb.Append(part.Substring(1));
if (length > 1)
{
sb.Append(c);
}
else if (length == 0)
{
last = c;
}
else
{
sb.Append(char.ToUpperInvariant(last));
sb.Append(c);
last = NullChar;
}
length++;
}
}
if (last != NullChar)
{
sb.Append(char.ToUpperInvariant(last));
}
return sb.ToString();
}
public static string ToKebabCase(this string value)
{
if (value.Length == 0)
{
return string.Empty;
}
var sb = new StringBuilder();
foreach (var part in value.Split(new[] { '-', '_', ' ' }, StringSplitOptions.RemoveEmptyEntries))
var length = 0;
for (var i = 0; i < value.Length; i++)
{
if (sb.Length > 0)
var c = value[i];
if (c == '-' || c == '_' || c == ' ')
{
sb.Append("-");
length = 0;
}
else
{
if (length > 0)
{
sb.Append(char.ToLowerInvariant(c));
}
else
{
if (sb.Length > 0)
{
sb.Append('-');
}
sb.Append(char.ToLowerInvariant(c));
}
sb.Append(part.ToLower());
length++;
}
}
return sb.ToString();
@ -362,16 +424,80 @@ namespace Squidex.Infrastructure
public static string ToCamelCase(this string value)
{
value = value.ToPascalCase();
if (value.Length == 0)
{
return string.Empty;
}
if (value.Length < 2)
var sb = new StringBuilder();
var last = NullChar;
var length = 0;
for (var i = 0; i < value.Length; i++)
{
return value.ToLower();
var c = value[i];
if (c == '-' || c == '_' || c == ' ')
{
if (last != NullChar)
{
if (sb.Length > 0)
{
sb.Append(char.ToUpperInvariant(last));
}
else
{
sb.Append(char.ToLowerInvariant(last));
}
}
last = NullChar;
length = 0;
}
else
{
if (length > 1)
{
sb.Append(c);
}
else if (length == 0)
{
last = c;
}
else
{
if (sb.Length > 0)
{
sb.Append(char.ToUpperInvariant(last));
}
else
{
sb.Append(char.ToLowerInvariant(last));
}
sb.Append(c);
last = NullChar;
}
length++;
}
}
else
if (last != NullChar)
{
return char.ToLower(value[0]) + value.Substring(1);
if (sb.Length > 0)
{
sb.Append(char.ToUpperInvariant(last));
}
else
{
sb.Append(char.ToLowerInvariant(last));
}
}
return sb.ToString();
}
public static string Slugify(this string value, ISet<char> preserveHash = null, bool singleCharDiactric = false, char separator = '-')

3
src/Squidex.Infrastructure/ValidationError.cs

@ -14,7 +14,6 @@ namespace Squidex.Infrastructure
[Serializable]
public sealed class ValidationError
{
private static readonly string[] FallbackProperties = Array.Empty<string>();
private readonly string message;
private readonly string[] propertyNames;
@ -34,7 +33,7 @@ namespace Squidex.Infrastructure
this.message = message;
this.propertyNames = propertyNames ?? FallbackProperties;
this.propertyNames = propertyNames ?? Array.Empty<string>();
}
public ValidationError WithPrefix(string prefix)

5
src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedTriggerDto.cs

@ -5,13 +5,12 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
@ -32,7 +31,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
public override RuleTrigger ToTrigger()
{
var schemas = Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchema())).ToImmutableList();
var schemas = Schemas.Select(x => SimpleMapper.Map(x, new ContentChangedTriggerSchema())).ToReadOnlyCollection();
return new ContentChangedTrigger { HandleAll = HandleAll, Schemas = schemas };
}

4
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs

@ -5,9 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -81,7 +81,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedExtensions != null)
{
result.AllowedExtensions = ImmutableList.Create(AllowedExtensions);
result.AllowedExtensions = ReadOnlyCollection.Create(AllowedExtensions);
}
return result;

4
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs

@ -5,11 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -54,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null)
{
result.AllowedValues = ImmutableList.Create(AllowedValues);
result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
}
return result;

4
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs

@ -5,11 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -64,7 +64,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null)
{
result.AllowedValues = ImmutableList.Create(AllowedValues);
result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
}
return result;

4
src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs

@ -5,11 +5,11 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -44,7 +44,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null)
{
result.AllowedValues = ImmutableList.Create(AllowedValues);
result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
}
return result;

2
src/Squidex/Config/Domain/SerializationServices.cs

@ -59,7 +59,7 @@ namespace Squidex.Config.Domain
new RefTokenConverter(),
new RolesConverter(),
new RuleConverter(),
new SchemaConverter(FieldRegistry),
new SchemaConverter(),
new StringEnumConverter());
settings.NullValueHandling = NullValueHandling.Ignore;

10
tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs

@ -35,13 +35,21 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
}
[Fact]
public void Should_throw_exception_if_assigning_clients_with_same_id()
public void Should_throw_exception_if_assigning_client_with_same_id()
{
var clients_1 = clients_0.Add("2", "my-secret");
Assert.Throws<ArgumentException>(() => clients_1.Add("2", "my-secret"));
}
[Fact]
public void Should_throw_exception_if_assigning_client_object_with_same_id()
{
var clients_1 = clients_0.Add("2", "my-secret");
Assert.Throws<ArgumentException>(() => clients_1.Add("2", new AppClient("my-name", "my-secret", "my-role")));
}
[Fact]
public void Should_rename_client()
{

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs

@ -7,7 +7,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.Triggers;
@ -17,6 +16,7 @@ using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing;
using Xunit;
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{
var trigger = new ContentChangedTrigger
{
Schemas = ImmutableList.Create(
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema
{
SendCreate = sendCreate == 1,

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs

@ -7,13 +7,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -251,7 +251,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact]
public async Task Should_add_error_if_image_has_invalid_extension()
{
var sut = Field(new AssetsFieldProperties { AllowedExtensions = ImmutableList.Create("mp4") });
var sut = Field(new AssetsFieldProperties { AllowedExtensions = ReadOnlyCollection.Create("mp4") });
await sut.ValidateAsync(CreateValue(document.AssetId, image.AssetId), errors, ctx);

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs

@ -6,11 +6,11 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact]
public async Task Should_add_errors_if_number_is_not_allowed()
{
var sut = Field(new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) });
var sut = Field(new NumberFieldProperties { AllowedValues = ReadOnlyCollection.Create(10d) });
await sut.ValidateAsync(CreateValue(20), errors);

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs

@ -6,11 +6,11 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact]
public async Task Should_add_errors_if_string_not_allowed()
{
var sut = Field(new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") });
var sut = Field(new StringFieldProperties { AllowedValues = ReadOnlyCollection.Create("Foo") });
await sut.ValidateAsync(CreateValue("Bar"), errors);

4
tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs

@ -6,12 +6,12 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact]
public async Task Should_add_errors_if_value_contains_an_not_allowed_values()
{
var sut = Field(new TagsFieldProperties { AllowedValues = ImmutableList.Create("tag-2", "tag-3") });
var sut = Field(new TagsFieldProperties { AllowedValues = ReadOnlyCollection.Create("tag-2", "tag-3") });
await sut.ValidateAsync(CreateValue("tag-1", "tag-2", null), errors);

6
tests/Squidex.Domain.Apps.Core.Tests/TestData.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
@ -16,6 +15,7 @@ using Squidex.Domain.Apps.Core.Rules.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Json;
using Xunit;
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Core
new RefTokenConverter(),
new RolesConverter(),
new RuleConverter(),
new SchemaConverter(new FieldRegistry(typeNameRegistry)),
new SchemaConverter(),
new StringEnumConverter()),
TypeNameHandling = TypeNameHandling.Auto
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core
.AddReferences(109, "root-references", Partitioning.Invariant,
new ReferencesFieldProperties())
.AddString(110, "root-string1", Partitioning.Invariant,
new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ImmutableList.Create("a", "b") })
new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ReadOnlyCollection.Create("a", "b") })
.AddString(111, "root-string2", Partitioning.Invariant,
new StringFieldProperties { Hints = "My String1" })
.AddTags(112, "root-tags", Partitioning.Language,

4
tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Linq;
using FakeItEasy;
using MongoDB.Bson.Serialization;
@ -20,6 +19,7 @@ using Squidex.Domain.Apps.Entities.MongoDb.Contents;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries;
@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
schemaDef =
new Schema("user")
.AddString(1, "firstName", Partitioning.Language,
new StringFieldProperties { Label = "FirstName", IsRequired = true, AllowedValues = ImmutableList.Create("1", "2") })
new StringFieldProperties { Label = "FirstName", IsRequired = true, AllowedValues = ReadOnlyCollection.Create("1", "2") })
.AddString(2, "lastName", Partitioning.Language,
new StringFieldProperties { Hints = "Last Name", Editor = StringFieldEditor.Input })
.AddBoolean(3, "isAdmin", Partitioning.Invariant,

8
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Rules;
@ -15,6 +14,7 @@ using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit;
#pragma warning disable SA1310 // Field names must not contain underscore
@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
Trigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
},
Action = null
});
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
Trigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
},
Action = new TestAction
{
@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards
{
Trigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
},
Action = new TestAction
{

8
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs

@ -6,11 +6,11 @@
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
var trigger = new ContentChangedTrigger
{
Schemas = ImmutableList.Create(
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema()
)
};
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
{
var trigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
};
var errors = await RuleTriggerValidator.ValidateAsync(appId, trigger, appProvider);
@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards.Triggers
var trigger = new ContentChangedTrigger
{
Schemas = ImmutableList.Create(
Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema()
)
};

6
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleGrainTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Collections.Immutable;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Rules;
@ -16,6 +15,7 @@ using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Log;
using Xunit;
@ -191,7 +191,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
var newTrigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
};
var newAction = new TestAction
@ -206,7 +206,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
var newTrigger = new ContentChangedTrigger
{
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty
Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
};
var newAction = new TestAction

10
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs

@ -6,11 +6,11 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified()
{
var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = ImmutableList.Create(4d) };
var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = ReadOnlyCollection.Create(4d) };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified()
{
var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = ImmutableList.Create(4d) };
var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = ReadOnlyCollection.Create(4d) };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -135,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(NumberFieldEditor.Stars)]
public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(NumberFieldEditor editor)
{
var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create(1.0) };
var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create(1.0) };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(NumberFieldEditor.Dropdown)]
public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(NumberFieldEditor editor)
{
var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create(1.0) };
var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create(1.0) };
var errors = FieldPropertiesValidator.Validate(sut).ToList();

10
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs

@ -6,11 +6,11 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified()
{
var sut = new StringFieldProperties { MinLength = 10, AllowedValues = ImmutableList.Create("4") };
var sut = new StringFieldProperties { MinLength = 10, AllowedValues = ReadOnlyCollection.Create("4") };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified()
{
var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = ImmutableList.Create("4") };
var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = ReadOnlyCollection.Create("4") };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(StringFieldEditor.TextArea)]
public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(StringFieldEditor editor)
{
var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create("Value") };
var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create("Value") };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -125,7 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(StringFieldEditor.Slug)]
public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(StringFieldEditor editor)
{
var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create("Value") };
var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create("Value") };
var errors = FieldPropertiesValidator.Validate(sut).ToList();

31
tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit;
namespace Squidex.Infrastructure
@ -275,35 +274,5 @@ namespace Squidex.Infrastructure
Assert.Equal(source, target);
}
[Fact]
public void Should_return_same_dictionary_if_item_to_replace_not_found()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(1, x => x);
Assert.Same(dict_0, dict_1);
}
[Fact]
public void Should_return_same_dictionary_if_replaced_item_is_same()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(1, 1);
var dict_2 = dict_1.SetItem(1, x => x);
Assert.Same(dict_1, dict_2);
}
[Fact]
public void Should_return_new_dictionary_if_updated_item_is_different()
{
var dict_0 = ImmutableDictionary<int, int>.Empty;
var dict_1 = dict_0.SetItem(2, 2);
var dict_2 = dict_1.SetItem(2, x => 2 * x);
Assert.NotSame(dict_1, dict_2);
Assert.Equal(4, dict_2[2]);
}
}
}
Loading…
Cancel
Save