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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable; using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps 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(); public static readonly AppClients Empty = new AppClients();
private AppClients() private AppClients()
: base(ImmutableDictionary<string, AppClient>.Empty)
{ {
} }
public AppClients(ImmutableDictionary<string, AppClient> inner) public AppClients(KeyValuePair<string, AppClient>[] items)
: base(inner) : base(items)
{ {
} }
[Pure] [Pure]
public AppClients Add(string id, AppClient client) public AppClients Revoke(string id)
{ {
Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNullOrEmpty(id, nameof(id));
Guard.NotNull(client, nameof(client));
return new AppClients(Inner.Add(id, client)); return new AppClients(Without(id));
} }
[Pure] [Pure]
public AppClients Add(string id, string secret) public AppClients Add(string id, AppClient client)
{ {
Guard.NotNullOrEmpty(id, nameof(id)); 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] [Pure]
public AppClients Revoke(string id) public AppClients Add(string id, string secret)
{ {
Guard.NotNullOrEmpty(id, nameof(id)); 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] [Pure]
@ -60,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this; return this;
} }
return new AppClients(Inner.SetItem(id, client.Rename(newName))); return new AppClients(With(id, client.Rename(newName)));
} }
[Pure] [Pure]
@ -73,7 +84,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this; 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps 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(); public static readonly AppContributors Empty = new AppContributors();
private AppContributors() private AppContributors()
: base(ImmutableDictionary<string, string>.Empty)
{ {
} }
public AppContributors(ImmutableDictionary<string, string> inner) public AppContributors(KeyValuePair<string, string>[] items)
: base(inner) : base(items)
{ {
} }
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Apps
Guard.NotNullOrEmpty(contributorId, nameof(contributorId)); Guard.NotNullOrEmpty(contributorId, nameof(contributorId));
Guard.NotNullOrEmpty(role, nameof(role)); Guard.NotNullOrEmpty(role, nameof(role));
return new AppContributors(Inner.SetItem(contributorId, role)); return new AppContributors(With(contributorId, role));
} }
[Pure] [Pure]
@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
Guard.NotNullOrEmpty(contributorId, nameof(contributorId)); 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) // Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Immutable; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps 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(); public static readonly AppPatterns Empty = new AppPatterns();
private AppPatterns() private AppPatterns()
: base(ImmutableDictionary<Guid, AppPattern>.Empty)
{ {
} }
public AppPatterns(ImmutableDictionary<Guid, AppPattern> inner) public AppPatterns(KeyValuePair<Guid, AppPattern>[] items)
: base(inner) : base(items)
{ {
} }
[Pure] [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(Without(id));
return new AppPatterns(Inner.Add(id, newPattern));
} }
[Pure] [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] [Pure]
@ -50,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this; 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
@ -31,7 +31,12 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{ {
var json = serializer.Deserialize<Dictionary<string, JsonAppClient>>(reader); 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{ {
var json = serializer.Deserialize<Dictionary<string, string>>(reader); 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) // Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
@ -30,7 +31,12 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
{ {
var json = serializer.Deserialize<Dictionary<Guid, JsonAppPattern>>(reader); 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 Squidex.Infrastructure.Security;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace Squidex.Domain.Apps.Core.Apps.Json 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); 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 public sealed class LanguageConfig : IFieldPartitionItem
{ {
private static readonly Language[] DefaultFallback = Array.Empty<Language>();
private readonly Language language; private readonly Language language;
private readonly Language[] languageFallbacks; private readonly Language[] languageFallbacks;
@ -57,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Apps
IsOptional = isOptional; IsOptional = isOptional;
this.language = language; 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;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Apps namespace Squidex.Domain.Apps.Core.Apps
{ {
@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
public static readonly LanguagesConfig English = Build(Language.EN); public static readonly LanguagesConfig English = Build(Language.EN);
private readonly ImmutableDictionary<Language, LanguageConfig> languages; private readonly ArrayDictionary<Language, LanguageConfig> languages;
private readonly LanguageConfig master; private readonly LanguageConfig master;
public LanguageConfig Master public LanguageConfig Master
@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.Apps
get { return languages.Count; } 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) if (checkMaster)
{ {
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
Guard.NotNull(configs, nameof(configs)); 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) public static LanguagesConfig Build(params LanguageConfig[] configs)
@ -100,7 +100,12 @@ namespace Squidex.Domain.Apps.Core.Apps
{ {
Guard.NotNull(config, nameof(config)); 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] [Pure]
@ -114,7 +119,7 @@ namespace Squidex.Domain.Apps.Core.Apps
config.Language, config.Language,
config.IsOptional, config.IsOptional,
config.LanguageFallbacks.Except(new[] { language }))) config.LanguageFallbacks.Except(new[] { language })))
.ToImmutableDictionary(x => x.Language); .ToArrayDictionary(x => x.Language);
var newMaster = var newMaster =
newLanguages.Values.FirstOrDefault(x => x.Language == Master.Language) ?? 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;
using Squidex.Infrastructure.Collections;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Linq;
namespace Squidex.Domain.Apps.Core.Apps 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(); public static readonly Roles Empty = new Roles();
private Roles() private Roles()
: base(ImmutableDictionary<string, Role>.Empty)
{ {
} }
public Roles(ImmutableDictionary<string, Role> inner) public Roles(KeyValuePair<string, Role>[] items)
: base(inner) : base(items)
{ {
} }
[Pure] [Pure]
public Roles Add(string name) public Roles Remove(string name)
{ {
var newRole = new Role(name); return new Roles(Without(name));
return new Roles(Inner.Add(name, newRole));
} }
[Pure] [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] [Pure]
@ -51,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Apps
return this; 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) public static Roles CreateDefaults(string app)
@ -63,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Apps
[Role.Editor] = Role.CreateEditor(app), [Role.Editor] = Role.CreateEditor(app),
[Role.Owner] = Role.CreateOwner(app), [Role.Owner] = Role.CreateOwner(app),
[Role.Reader] = Role.CreateReader(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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Rules.Triggers namespace Squidex.Domain.Apps.Core.Rules.Triggers
{ {
[TypeName(nameof(ContentChangedTrigger))] [TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger public sealed class ContentChangedTrigger : RuleTrigger
{ {
public ImmutableList<ContentChangedTriggerSchema> Schemas { get; set; } public ReadOnlyCollection<ContentChangedTriggerSchema> Schemas { get; set; }
public bool HandleAll { 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
public int? AspectHeight { get; set; } 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) 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Linq; using System.Linq;
using Squidex.Infrastructure; 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 sealed class FieldCollection<T> : Cloneable<FieldCollection<T>> where T : IField
{ {
public static readonly FieldCollection<T> Empty = new FieldCollection<T>(); 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 T[] fieldsOrdered;
private ImmutableDictionary<long, T> fieldsById; private Dictionary<long, T> fieldsById;
private ImmutableDictionary<string, T> fieldsByName; private Dictionary<string, T> fieldsByName;
public IReadOnlyList<T> Ordered public IReadOnlyList<T> Ordered
{ {
@ -35,11 +37,11 @@ namespace Squidex.Domain.Apps.Core.Schemas
{ {
if (fieldsOrdered.Length == 0) if (fieldsOrdered.Length == 0)
{ {
fieldsById = ImmutableDictionary<long, T>.Empty; fieldsById = EmptyById;
} }
else 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) if (fieldsOrdered.Length == 0)
{ {
fieldsByName = ImmutableDictionary<string, T>.Empty; fieldsByName = EmptyByString;
} }
else 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() private FieldCollection()
{ {
fieldsOrdered = Array.Empty<T>();
} }
public FieldCollection(T[] fields) public FieldCollection(T[] fields)
{ {
Guard.NotNull(fields, nameof(fields)); Guard.NotNull(fields, nameof(fields));
fieldsOrdered = ImmutableArray.Create(fields); fieldsOrdered = fields;
} }
protected override void OnCloned() protected override void OnCloned()
@ -94,7 +97,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
return Clone(clone => 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 => 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 => 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 => 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 Newtonsoft.Json;
using Squidex.Infrastructure;
using System;
using System.Linq;
using P = Squidex.Domain.Apps.Core.Partitioning;
namespace Squidex.Domain.Apps.Core.Schemas.Json namespace Squidex.Domain.Apps.Core.Schemas.Json
{ {
@ -34,5 +38,21 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
[JsonProperty] [JsonProperty]
public JsonNestedFieldModel[] Children { get; set; } 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; } 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 public sealed class JsonSchemaModel
{ {
private static readonly RootField[] Empty = Array.Empty<RootField>();
[JsonProperty] [JsonProperty]
public string Name { get; set; } public string Name { get; set; }
@ -73,35 +71,9 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
return null; return null;
} }
public Schema ToSchema(FieldRegistry registry) public Schema ToSchema()
{ {
var fields = Empty; var fields = Fields.ToArray(f => f.ToField()) ?? Array.Empty<RootField>();
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;
});
}
return new Schema(Name, fields, Properties, IsPublished); 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> 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) protected override void WriteValue(JsonWriter writer, Schema value, JsonSerializer serializer)
{ {
serializer.Serialize(writer, new JsonSchemaModel(value)); 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) 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
[TypeName("NumberField")] [TypeName("NumberField")]
public sealed class NumberFieldProperties : FieldProperties public sealed class NumberFieldProperties : FieldProperties
{ {
public ImmutableList<double> AllowedValues { get; set; } public ReadOnlyCollection<double> AllowedValues { get; set; }
public double? MaxValue { 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
[TypeName("StringField")] [TypeName("StringField")]
public sealed class StringFieldProperties : FieldProperties public sealed class StringFieldProperties : FieldProperties
{ {
public ImmutableList<string> AllowedValues { get; set; } public ReadOnlyCollection<string> AllowedValues { get; set; }
public int? MinLength { 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 Squidex.Infrastructure;
using System.Collections.Immutable; using System.Collections.ObjectModel;
namespace Squidex.Domain.Apps.Core.Schemas namespace Squidex.Domain.Apps.Core.Schemas
{ {
[TypeName("TagsField")] [TypeName("TagsField")]
public sealed class TagsFieldProperties : FieldProperties public sealed class TagsFieldProperties : FieldProperties
{ {
public ImmutableList<string> AllowedValues { get; set; } public ReadOnlyCollection<string> AllowedValues { get; set; }
public int? MinItems { 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders 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) public StringFieldBuilder AsDropDown(params string[] values)
{ {
Properties<StringFieldProperties>().AllowedValues = ImmutableList.Create(values); Properties<StringFieldProperties>().AllowedValues = ReadOnlyCollection.Create(values);
Properties<StringFieldProperties>().Editor = StringFieldEditor.Dropdown; Properties<StringFieldProperties>().Editor = StringFieldEditor.Dropdown;
return this; return this;

32
src/Squidex.Infrastructure/CollectionExtensions.cs

@ -7,7 +7,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
@ -21,37 +20,6 @@ namespace Squidex.Infrastructure
return enumerable.OrderBy(x => random.Next()).ToList(); 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) public static IEnumerable<T> OrEmpty<T>(this IEnumerable<T> source)
{ {
return source ?? Enumerable.Empty<T>(); 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 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 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 Regex PropertyNameRegex = new Regex("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$", RegexOptions.Compiled);
private static readonly Dictionary<char, string> LowerCaseDiacritics; private static readonly Dictionary<char, string> LowerCaseDiacritics;
@ -325,36 +326,97 @@ namespace Squidex.Infrastructure
public static string ToPascalCase(this string value) public static string ToPascalCase(this string value)
{ {
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
var sb = new StringBuilder(); 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 else
{ {
sb.Append(char.ToUpper(part[0])); if (length > 1)
sb.Append(part.Substring(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(); return sb.ToString();
} }
public static string ToKebabCase(this string value) public static string ToKebabCase(this string value)
{ {
if (value.Length == 0)
{
return string.Empty;
}
var sb = new StringBuilder(); 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(); return sb.ToString();
@ -362,16 +424,80 @@ namespace Squidex.Infrastructure
public static string ToCamelCase(this string value) 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 = '-') 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] [Serializable]
public sealed class ValidationError public sealed class ValidationError
{ {
private static readonly string[] FallbackProperties = Array.Empty<string>();
private readonly string message; private readonly string message;
private readonly string[] propertyNames; private readonly string[] propertyNames;
@ -34,7 +33,7 @@ namespace Squidex.Infrastructure
this.message = message; this.message = message;
this.propertyNames = propertyNames ?? FallbackProperties; this.propertyNames = propertyNames ?? Array.Empty<string>();
} }
public ValidationError WithPrefix(string prefix) 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
@ -32,7 +31,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers
public override RuleTrigger ToTrigger() 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 }; 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -81,7 +81,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedExtensions != null) if (AllowedExtensions != null)
{ {
result.AllowedExtensions = ImmutableList.Create(AllowedExtensions); result.AllowedExtensions = ReadOnlyCollection.Create(AllowedExtensions);
} }
return result; 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -54,7 +54,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null) if (AllowedValues != null)
{ {
result.AllowedValues = ImmutableList.Create(AllowedValues); result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
} }
return result; 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -64,7 +64,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null) if (AllowedValues != null)
{ {
result.AllowedValues = ImmutableList.Create(AllowedValues); result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
} }
return result; 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
using NJsonSchema.Annotations; using NJsonSchema.Annotations;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
@ -44,7 +44,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields
if (AllowedValues != null) if (AllowedValues != null)
{ {
result.AllowedValues = ImmutableList.Create(AllowedValues); result.AllowedValues = ReadOnlyCollection.Create(AllowedValues);
} }
return result; return result;

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

@ -59,7 +59,7 @@ namespace Squidex.Config.Domain
new RefTokenConverter(), new RefTokenConverter(),
new RolesConverter(), new RolesConverter(),
new RuleConverter(), new RuleConverter(),
new SchemaConverter(FieldRegistry), new SchemaConverter(),
new StringEnumConverter()); new StringEnumConverter());
settings.NullValueHandling = NullValueHandling.Ignore; 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] [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"); var clients_1 = clients_0.Add("2", "my-secret");
Assert.Throws<ArgumentException>(() => clients_1.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] [Fact]
public void Should_rename_client() 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.Triggers; 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.Rules;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Xunit; using Xunit;
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers
{ {
var trigger = new ContentChangedTrigger var trigger = new ContentChangedTrigger
{ {
Schemas = ImmutableList.Create( Schemas = ReadOnlyCollection.Create(
new ContentChangedTriggerSchema new ContentChangedTriggerSchema
{ {
SendCreate = sendCreate == 1, SendCreate = sendCreate == 1,

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

@ -7,13 +7,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -251,7 +251,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_error_if_image_has_invalid_extension() 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); 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.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_number_is_not_allowed() 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); 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.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_string_not_allowed() 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); 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.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core.Operations.ValidateContent namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
@ -106,7 +106,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent
[Fact] [Fact]
public async Task Should_add_errors_if_value_contains_an_not_allowed_values() 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); 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;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json; 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;
using Squidex.Domain.Apps.Core.Schemas.Json; using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Xunit; using Xunit;
@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Core
new RefTokenConverter(), new RefTokenConverter(),
new RolesConverter(), new RolesConverter(),
new RuleConverter(), new RuleConverter(),
new SchemaConverter(new FieldRegistry(typeNameRegistry)), new SchemaConverter(),
new StringEnumConverter()), new StringEnumConverter()),
TypeNameHandling = TypeNameHandling.Auto TypeNameHandling = TypeNameHandling.Auto
@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Core
.AddReferences(109, "root-references", Partitioning.Invariant, .AddReferences(109, "root-references", Partitioning.Invariant,
new ReferencesFieldProperties()) new ReferencesFieldProperties())
.AddString(110, "root-string1", Partitioning.Invariant, .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, .AddString(111, "root-string2", Partitioning.Invariant,
new StringFieldProperties { Hints = "My String1" }) new StringFieldProperties { Hints = "My String1" })
.AddTags(112, "root-tags", Partitioning.Language, .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;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using FakeItEasy; using FakeItEasy;
using MongoDB.Bson.Serialization; 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.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.MongoDb.Queries;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
@ -46,7 +46,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb
schemaDef = schemaDef =
new Schema("user") new Schema("user")
.AddString(1, "firstName", Partitioning.Language, .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, .AddString(2, "lastName", Partitioning.Language,
new StringFieldProperties { Hints = "Last Name", Editor = StringFieldEditor.Input }) new StringFieldProperties { Hints = "Last Name", Editor = StringFieldEditor.Input })
.AddBoolean(3, "isAdmin", Partitioning.Invariant, .AddBoolean(3, "isAdmin", Partitioning.Invariant,

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

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

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

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

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Rules; 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.Entities.TestHelpers;
using Squidex.Domain.Apps.Events.Rules; using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Xunit; using Xunit;
@ -191,7 +191,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
var newTrigger = new ContentChangedTrigger var newTrigger = new ContentChangedTrigger
{ {
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
}; };
var newAction = new TestAction var newAction = new TestAction
@ -206,7 +206,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
var newTrigger = new ContentChangedTrigger var newTrigger = new ContentChangedTrigger
{ {
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty Schemas = ReadOnlyCollection.Empty<ContentChangedTriggerSchema>()
}; };
var newAction = new TestAction 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.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified() 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified() 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -135,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(NumberFieldEditor.Stars)] [InlineData(NumberFieldEditor.Stars)]
public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(NumberFieldEditor editor) 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(NumberFieldEditor.Dropdown)] [InlineData(NumberFieldEditor.Dropdown)]
public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(NumberFieldEditor editor) 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(); 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.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified() 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -48,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified() 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(StringFieldEditor.TextArea)] [InlineData(StringFieldEditor.TextArea)]
public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(StringFieldEditor editor) 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -125,7 +125,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards.FieldProperties
[InlineData(StringFieldEditor.Slug)] [InlineData(StringFieldEditor.Slug)]
public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(StringFieldEditor editor) 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(); var errors = FieldPropertiesValidator.Validate(sut).ToList();

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit; using Xunit;
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
@ -275,35 +274,5 @@ namespace Squidex.Infrastructure
Assert.Equal(source, target); 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