Browse Source

Some progress with refactoring.

pull/152/head
Sebastian Stehle 8 years ago
parent
commit
7b9770dff1
  1. 15
      Squidex.sln
  2. 27
      src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs
  3. 72
      src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs
  4. 91
      src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
  5. 2
      src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
  6. 89
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs
  7. 43
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityClient.cs
  8. 21
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityContributor.cs
  9. 28
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityLanguage.cs
  10. 6
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs
  11. 41
      src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs
  12. 2
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs
  13. 1
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  14. 2
      src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs
  15. 39
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs
  16. 12
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs
  17. 109
      src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
  18. 3
      src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj
  19. 8
      src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
  20. 2
      src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs
  21. 3
      src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj
  22. 56
      src/Squidex.Domain.Apps.Write/Apps/AppClient.cs
  23. 66
      src/Squidex.Domain.Apps.Write/Apps/AppClients.cs
  24. 78
      src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs
  25. 23
      src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs
  26. 12
      src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs
  27. 7
      src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs
  28. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs
  29. 2
      src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs
  30. 6
      src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs
  31. 19
      src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs
  32. 69
      src/Squidex.Domain.Apps.Write/Contents/Guards/GuardContent.cs
  33. 33
      src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs
  34. 55
      src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs
  35. 33
      src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs
  36. 11
      src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs
  37. 12
      src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs
  38. 12
      src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs
  39. 11
      src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs
  40. 130
      src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchema.cs
  41. 160
      src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchemaField.cs
  42. 106
      src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaFieldGuard.cs
  43. 44
      src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaGuard.cs
  44. 121
      src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs
  45. 85
      src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs
  46. 3
      src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj
  47. 25
      src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs
  48. 63
      src/Squidex.Domain.Apps.Write/Webhooks/Guards/GuardWebhook.cs
  49. 39
      src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs
  50. 6
      src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs
  51. 4
      src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonConverter.cs
  52. 38
      src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonSerializer.cs
  53. 17
      src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs
  54. 42
      src/Squidex.Infrastructure/Validate.cs
  55. 1
      src/Squidex/Squidex.csproj
  56. 3
      tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj
  57. 330
      tests/Squidex.Domain.Apps.Write.Tests/Apps/AppCommandMiddlewareTests.cs
  58. 597
      tests/Squidex.Domain.Apps.Write.Tests/Apps/AppDomainObjectTests.cs
  59. 51
      tests/Squidex.Domain.Apps.Write.Tests/Apps/AppEventTests.cs
  60. 139
      tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandMiddlewareTests.cs
  61. 235
      tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetDomainObjectTests.cs
  62. 240
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs
  63. 323
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentDomainObjectTests.cs
  64. 70
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs
  65. 139
      tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs
  66. 4
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs
  67. 4
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs
  68. 234
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs
  69. 197
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaTests.cs
  70. 169
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaFieldGuardTests.cs
  71. 65
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaGuardTests.cs
  72. 22
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs
  73. 189
      tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs
  74. 5
      tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj
  75. 130
      tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs
  76. 179
      tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookDomainObjectTests.cs

15
Squidex.sln

@ -8,8 +8,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastru
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "domain", "domain", "{4C6B06C2-6D77-4E0E-AE32-D7050236433A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core", "src\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj", "{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure", "src\Squidex.Infrastructure\Squidex.Infrastructure.csproj", "{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Events", "src\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}"
@ -65,9 +63,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
stylecop.json = stylecop.json
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Core.Model", "src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj", "{F0A83301-50A5-40EA-A1A2-07C7858F5A3F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Model", "src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj", "{F0A83301-50A5-40EA-A1A2-07C7858F5A3F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -87,14 +85,6 @@ Global
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|Any CPU.Build.0 = Release|Any CPU
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|x64.ActiveCfg = Release|Any CPU
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|x86.ActiveCfg = Release|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|x64.ActiveCfg = Debug|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|x86.ActiveCfg = Debug|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.Build.0 = Release|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|x64.ActiveCfg = Release|Any CPU
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|x86.ActiveCfg = Release|Any CPU
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -332,7 +322,6 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0} = {C9809D59-6665-471E-AD87-5AC624C65892}
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A85201C6-6AF8-4B63-8365-08F741050438} = {C9809D59-6665-471E-AD87-5AC624C65892}

27
src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs

@ -0,0 +1,27 @@
// ==========================================================================
// PartitioningExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
namespace Squidex.Domain.Apps.Core
{
public static class PartitioningExtensions
{
private static readonly HashSet<string> AllowedPartitions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
Partitioning.Language.Key,
Partitioning.Invariant.Key
};
public static bool IsValidPartitioning(this string value)
{
return value == null || AllowedPartitions.Contains(value);
}
}
}

72
src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs

@ -0,0 +1,72 @@
// ==========================================================================
// MongoAppRepository_EventHandling.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Events.Apps.Utils
{
public static class AppEventDispatcher
{
public static void Apply(this AppContributors contributors, AppContributorRemoved @event)
{
contributors.Remove(@event.ContributorId);
}
public static void Apply(this AppContributors contributors, AppContributorAssigned @event)
{
contributors.Assign(@event.ContributorId, @event.Permission);
}
public static void Apply(this LanguagesConfig languagesConfig, AppLanguageAdded @event)
{
languagesConfig.Set(new LanguageConfig(@event.Language));
}
public static void Apply(this LanguagesConfig languagesConfig, AppLanguageRemoved @event)
{
languagesConfig.Remove(@event.Language);
}
public static void Apply(this AppClients clients, AppClientAttached @event)
{
clients.Add(@event.Id, @event.Secret);
}
public static void Apply(this AppClients clients, AppClientRevoked @event)
{
clients.Revoke(@event.Id);
}
public static void Apply(this AppClients clients, AppClientRenamed @event)
{
if (clients.Clients.TryGetValue(@event.Id, out var client))
{
client.Rename(@event.Name);
}
}
public static void Apply(this AppClients clients, AppClientUpdated @event)
{
if (clients.Clients.TryGetValue(@event.Id, out var client))
{
client.Update(@event.Permission);
}
}
public static void Apply(this LanguagesConfig languagesConfig, AppLanguageUpdated @event)
{
languagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, @event.Fallback));
if (@event.IsMaster)
{
languagesConfig.MakeMaster(@event.Language);
}
}
}
}

91
src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs

@ -14,9 +14,14 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
{
public static class SchemaEventDispatcher
{
public static Schema Dispatch(SchemaCreated @event, FieldRegistry registry)
public static Schema Create(SchemaCreated @event, FieldRegistry registry)
{
var schema = Schema.Create(@event.Name, @event.Properties);
var schema = new Schema(@event.Name);
if (@event.Properties != null)
{
schema.Update(@event.Properties);
}
if (@event.Fields != null)
{
@ -33,20 +38,20 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
if (eventField.IsHidden)
{
field = field.Hide();
field.Hide();
}
if (eventField.IsDisabled)
{
field = field.Disable();
field.Disable();
}
if (eventField.IsLocked)
{
field = field.Lock();
field.Lock();
}
schema = schema.AddField(field);
schema.AddField(field);
fieldId++;
}
@ -55,7 +60,7 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
return schema;
}
public static Schema Dispatch(FieldAdded @event, Schema schema, FieldRegistry registry)
public static void Apply(this Schema schema, FieldAdded @event, FieldRegistry registry)
{
var partitioning =
string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ?
@ -65,69 +70,81 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
var fieldId = @event.FieldId.Id;
var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties);
if (schema.FieldsById.ContainsKey(fieldId))
{
return schema.UpdateField(fieldId, f => field);
}
else
{
return schema.AddField(field);
}
schema.DeleteField(fieldId);
schema.AddField(field);
}
public static Schema Dispatch(FieldUpdated @event, Schema schema)
public static void Apply(this Schema schema, FieldUpdated @event)
{
return schema.UpdateField(@event.FieldId.Id, @event.Properties);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Update(@event.Properties);
}
}
public static Schema Dispatch(FieldLocked @event, Schema schema)
public static void Apply(this Schema schema, FieldLocked @event)
{
return schema.LockField(@event.FieldId.Id);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Lock();
}
}
public static Schema Dispatch(FieldHidden @event, Schema schema)
public static void Apply(this Schema schema, FieldHidden @event)
{
return schema.HideField(@event.FieldId.Id);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Hide();
}
}
public static Schema Dispatch(FieldShown @event, Schema schema)
public static void Apply(this Schema schema, FieldShown @event)
{
return schema.ShowField(@event.FieldId.Id);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Show();
}
}
public static Schema Dispatch(FieldDisabled @event, Schema schema)
public static void Apply(this Schema schema, FieldDisabled @event)
{
return schema.DisableField(@event.FieldId.Id);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Disable();
}
}
public static Schema Dispatch(FieldEnabled @event, Schema schema)
public static void Apply(this Schema schema, FieldEnabled @event)
{
return schema.EnableField(@event.FieldId.Id);
if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
{
field.Enable();
}
}
public static Schema Dispatch(SchemaUpdated @event, Schema schema)
public static void Apply(this Schema schema, SchemaUpdated @event)
{
return schema.Update(@event.Properties);
schema.Update(@event.Properties);
}
public static Schema Dispatch(SchemaFieldsReordered @event, Schema schema)
public static void Apply(this Schema schema, SchemaFieldsReordered @event)
{
return schema.ReorderFields(@event.FieldIds);
schema.ReorderFields(@event.FieldIds);
}
public static Schema Dispatch(FieldDeleted @event, Schema schema)
public static void Apply(this Schema schema, FieldDeleted @event)
{
return schema.DeleteField(@event.FieldId.Id);
schema.DeleteField(@event.FieldId.Id);
}
public static Schema Dispatch(SchemaPublished @event, Schema schema)
public static void Apply(this Schema schema, SchemaPublished @event)
{
return schema.Publish();
schema.Publish();
}
public static Schema Dispatch(SchemaUnpublished @event, Schema schema)
public static void Apply(this Schema schema, SchemaUnpublished @event)
{
return schema.Unpublish();
schema.Unpublish();
}
}
}

2
src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj

@ -7,7 +7,7 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>

89
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs

@ -6,11 +6,9 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
@ -19,10 +17,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
public sealed class MongoAppEntity : MongoEntity, IAppEntity
{
private readonly IReadOnlyDictionary<string, IAppClientEntity> clientWrapper;
private readonly IReadOnlyDictionary<string, IAppContributorEntity> contributorWrapper;
private LanguagesConfig languagesConfig;
[BsonRequired]
[BsonElement]
public string Name { get; set; }
@ -39,93 +33,28 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
[BsonElement]
public string PlanOwner { get; set; }
[BsonRequired]
[BsonElement]
public string MasterLanguage { get; set; }
[BsonRequired]
[BsonIgnoreIfDefault]
[BsonElement]
public List<string> ContributorIds { get; set; }
public string[] ContributorIds { get; set; }
[BsonRequired]
[BsonElement]
public List<MongoAppEntityLanguage> Languages { get; set; }
[BsonSerializer(typeof(JsonBsonSerializer))]
public AppClients Clients { get; set; } = new AppClients();
[BsonRequired]
[BsonElement]
public Dictionary<string, MongoAppEntityClient> Clients { get; set; }
[BsonSerializer(typeof(JsonBsonSerializer))]
public AppContributors Contributors { get; set; } = new AppContributors();
[BsonRequired]
[BsonElement]
public Dictionary<string, MongoAppEntityContributor> Contributors { get; set; }
[BsonSerializer(typeof(JsonBsonSerializer))]
public LanguagesConfig LanguagesConfig { get; } = LanguagesConfig.Build(Language.EN);
public PartitionResolver PartitionResolver
{
get { return LanguagesConfig.ToResolver(); }
}
public LanguagesConfig LanguagesConfig
{
get { return languagesConfig ?? (languagesConfig = CreateLanguagesConfig()); }
}
IReadOnlyDictionary<string, IAppClientEntity> IAppEntity.Clients
{
get { return clientWrapper; }
}
IReadOnlyDictionary<string, IAppContributorEntity> IAppEntity.Contributors
{
get { return contributorWrapper; }
}
public MongoAppEntity()
{
clientWrapper = new DictionaryWrapper<string, IAppClientEntity, MongoAppEntityClient>(() => Clients);
contributorWrapper = new DictionaryWrapper<string, IAppContributorEntity, MongoAppEntityContributor>(() => Contributors);
}
public void ChangePlan(string planId, RefToken planOwner)
{
PlanId = planId;
PlanOwner = planOwner.Identifier;
}
public void UpdateLanguages(Func<LanguagesConfig, LanguagesConfig> updater)
{
var newConfig = updater(LanguagesConfig);
if (languagesConfig != newConfig)
{
languagesConfig = newConfig;
Languages = newConfig.OfType<LanguageConfig>().Select(FromLanguageConfig).ToList();
MasterLanguage = newConfig.Master.Language;
}
}
private LanguagesConfig CreateLanguagesConfig()
{
languagesConfig = LanguagesConfig.Create(Languages?.Select(ToLanguageConfig).ToList() ?? new List<LanguageConfig>());
if (MasterLanguage != null)
{
languagesConfig = languagesConfig.MakeMaster(MasterLanguage);
}
return languagesConfig;
}
private static MongoAppEntityLanguage FromLanguageConfig(LanguageConfig l)
{
return new MongoAppEntityLanguage { Iso2Code = l.Language, IsOptional = l.IsOptional, Fallback = l.LanguageFallbacks.Select(x => x.Iso2Code).ToList() };
}
private static LanguageConfig ToLanguageConfig(MongoAppEntityLanguage l)
{
return new LanguageConfig(l.Iso2Code, l.IsOptional, l.Fallback?.Select<string, Language>(f => f));
}
}
}

43
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityClient.cs

@ -1,43 +0,0 @@
// ==========================================================================
// MongoAppEntityClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps;
namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
public sealed class MongoAppEntityClient : IAppClientEntity
{
[BsonRequired]
[BsonElement]
public string Id { get; set; }
[BsonRequired]
[BsonElement]
public string Secret { get; set; }
[BsonRequired]
[BsonElement]
public string Name { get; set; }
[BsonRequired]
[BsonElement]
public AppClientPermission Permission { get; set; }
string IAppClientEntity.Name
{
get { return !string.IsNullOrWhiteSpace(Name) ? Name : Id; }
}
public MongoAppEntityClient()
{
Permission = AppClientPermission.Editor;
}
}
}

21
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityContributor.cs

@ -1,21 +0,0 @@
// ==========================================================================
// MongoAppEntityContributor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps;
namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
public sealed class MongoAppEntityContributor : IAppContributorEntity
{
[BsonRequired]
[BsonElement]
public AppContributorPermission Permission { get; set; }
}
}

28
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityLanguage.cs

@ -1,28 +0,0 @@
// ==========================================================================
// MongoAppEntityLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using MongoDB.Bson.Serialization.Attributes;
namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
public sealed class MongoAppEntityLanguage
{
[BsonRequired]
[BsonElement]
public string Iso2Code { get; set; }
[BsonRequired]
[BsonElement]
public bool IsOptional { get; set; }
[BsonRequired]
[BsonElement]
public List<string> Fallback { get; set; }
}
}

6
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs

@ -8,8 +8,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Infrastructure.CQRS.Events;
@ -19,9 +22,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
public partial class MongoAppRepository : MongoRepositoryBase<MongoAppEntity>, IAppRepository, IEventConsumer
{
public MongoAppRepository(IMongoDatabase database)
public MongoAppRepository(IMongoDatabase database, JsonSerializer serializer)
: base(database)
{
BsonSerializer.RegisterSerializer(new JsonBsonSerializer(serializer));
}
protected override string CollectionName()

41
src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs

@ -7,13 +7,12 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Utils;
using Squidex.Domain.Apps.Read.MongoDb.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection;
@ -41,10 +40,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return Collection.CreateAsync(@event, headers, a =>
{
a.Clients = new Dictionary<string, MongoAppEntityClient>();
a.Contributors = new Dictionary<string, MongoAppEntityContributor>();
a.ContributorIds = new List<string>();
SimpleMapper.Map(@event, a);
});
}
protected Task On(AppPlanChanged @event, EnvelopeHeaders headers)
{
return UpdateAppAsync(@event, headers, a =>
{
SimpleMapper.Map(@event, a);
});
}
@ -53,7 +56,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Clients[@event.Id] = SimpleMapper.Map(@event, new MongoAppEntityClient());
a.Clients.Apply(@event);
});
}
@ -61,7 +64,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Clients.Remove(@event.Id);
a.Clients.Apply(@event);
});
}
@ -69,7 +72,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Clients[@event.Id].Name = @event.Name;
a.Clients.Apply(@event);
});
}
@ -77,7 +80,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Clients[@event.Id].Permission = @event.Permission;
a.Clients.Apply(@event);
});
}
@ -85,7 +88,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Contributors.Remove(@event.ContributorId);
a.Contributors.Apply(@event);
});
}
@ -93,7 +96,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.Contributors[@event.ContributorId] = new MongoAppEntityContributor { Permission = @event.Permission };
a.Contributors.Apply(@event);
});
}
@ -101,7 +104,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.UpdateLanguages(c => c.Add(@event.Language));
a.LanguagesConfig.Apply(@event);
});
}
@ -109,7 +112,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.UpdateLanguages(c => c.Remove(@event.Language));
a.LanguagesConfig.Apply(@event);
});
}
@ -117,15 +120,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
return UpdateAppAsync(@event, headers, a =>
{
a.UpdateLanguages(c => c.Update(@event.Language, @event.IsOptional, @event.IsMaster, @event.Fallback));
});
}
protected Task On(AppPlanChanged @event, EnvelopeHeaders headers)
{
return UpdateAppAsync(@event, headers, a =>
{
a.ChangePlan(@event.PlanId, @event.Actor);
a.LanguagesConfig.Apply(@event);
});
}
@ -135,7 +130,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{
updater(a);
a.ContributorIds = a.Contributors.Keys.ToList();
a.ContributorIds = a.Contributors.Contributors.Keys.ToArray();
});
}
}

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs

@ -14,6 +14,8 @@ using MongoDB.Bson;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.MongoDb;

1
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -9,6 +9,7 @@
using System;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents;

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs

@ -11,8 +11,8 @@ using System.Collections.Immutable;
using System.Linq;
using Microsoft.OData.UriParser;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Edm;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
{

39
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs

@ -9,8 +9,6 @@
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
@ -20,16 +18,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{
public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity
{
private Lazy<Schema> schema;
[BsonRequired]
[BsonElement]
public string Name { get; set; }
[BsonRequired]
[BsonElement]
public BsonDocument SchemaDocument { get; set; }
[BsonRequired]
[BsonElement]
public long Version { get; set; }
@ -74,32 +66,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonElement]
public string ScriptChange { get; set; }
Schema ISchemaEntity.SchemaDef
{
get { return schema.Value; }
}
public void SerializeSchema(Schema newSchema, JsonSerializer serializer)
{
SchemaDocument = JObject.FromObject(newSchema, serializer).ToBson();
schema = new Lazy<Schema>(() => newSchema);
IsPublished = newSchema.IsPublished;
}
public void UpdateSchema(JsonSerializer serializer, Func<Schema, Schema> updater)
{
DeserializeSchema(serializer);
SerializeSchema(updater(schema.Value), serializer);
}
public void DeserializeSchema(JsonSerializer serializer)
{
if (schema == null)
{
schema = new Lazy<Schema>(() => schema != null ? SchemaDocument.ToJson().ToObject<Schema>(serializer) : null);
}
}
[BsonRequired]
[BsonElement]
[BsonSerializer(typeof(JsonBsonSerializer))]
public Schema SchemaDef { get; set; }
}
}

12
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas;
@ -23,17 +24,16 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{
public partial class MongoSchemaRepository : MongoRepositoryBase<MongoSchemaEntity>, ISchemaRepository, IEventConsumer
{
private readonly JsonSerializer serializer;
private readonly FieldRegistry registry;
public MongoSchemaRepository(IMongoDatabase database, JsonSerializer serializer, FieldRegistry registry)
: base(database)
{
Guard.NotNull(registry, nameof(registry));
Guard.NotNull(serializer, nameof(serializer));
this.registry = registry;
this.serializer = serializer;
BsonSerializer.RegisterSerializer(new JsonBsonSerializer(serializer));
}
protected override string CollectionName()
@ -54,8 +54,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.AppId == appId && !s.IsDeleted)
.ToListAsync();
schemaEntities.ForEach(x => x.DeserializeSchema(serializer));
return schemaEntities.OfType<ISchemaEntity>().ToList();
}
@ -65,8 +63,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name)
.FirstOrDefaultAsync();
schemaEntity?.DeserializeSchema(serializer);
return schemaEntity;
}
@ -76,8 +72,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
await Collection.Find(s => s.Id == schemaId)
.FirstOrDefaultAsync();
schemaEntity?.DeserializeSchema(serializer);
return schemaEntity;
}
}

109
src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Old;
@ -41,104 +40,150 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
protected Task On(SchemaCreated @event, EnvelopeHeaders headers)
{
var schema = SchemaEventDispatcher.Dispatch(@event, registry);
return Collection.CreateAsync(@event, headers, s =>
{
s.SchemaDef = SchemaEventDispatcher.Create(@event, registry);
return Collection.CreateAsync(@event, headers, s => { UpdateSchema(s, schema); SimpleMapper.Map(@event, s); });
SimpleMapper.Map(@event, s);
});
}
protected Task On(FieldDeleted @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldLocked @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldHidden @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldShown @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldDisabled @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldEnabled @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldUpdated @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(SchemaFieldsReordered @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(SchemaUpdated @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(SchemaPublished @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(SchemaUnpublished @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event);
});
}
protected Task On(FieldAdded @event, EnvelopeHeaders headers)
{
return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s, registry));
return UpdateSchemaAsync(@event, headers, s =>
{
s.SchemaDef.Apply(@event, registry);
});
}
protected Task On(ScriptsConfigured @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => SimpleMapper.Map(@event, e));
return Collection.UpdateAsync(@event, headers, s =>
{
SimpleMapper.Map(@event, s);
});
}
protected Task On(SchemaDeleted @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true);
return Collection.UpdateAsync(@event, headers, s =>
{
s.IsDeleted = true;
});
}
private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func<Schema, Schema> updater)
protected Task On(WebhookAdded @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater));
return Collection.UpdateAsync(@event, headers, s =>
{
/* NOOP */
});
}
private void UpdateSchema(MongoSchemaEntity entity, Func<Schema, Schema> updater)
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
entity.UpdateSchema(serializer, updater);
return Collection.UpdateAsync(@event, headers, s =>
{
/* NOOP */
});
}
private void UpdateSchema(MongoSchemaEntity entity, Schema schema)
private Task UpdateSchemaAsync(SquidexEvent @event, EnvelopeHeaders headers, Action<MongoSchemaEntity> updater)
{
entity.SerializeSchema(schema, serializer);
}
return Collection.UpdateAsync(@event, headers, s =>
{
updater(s);
protected Task On(WebhookAdded @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => { });
}
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => { });
s.IsPublished = s.SchemaDef.IsPublished;
});
}
}
}

3
src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj

@ -7,7 +7,8 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj" />

8
src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs

@ -6,8 +6,8 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Read.Apps
{
@ -19,11 +19,11 @@ namespace Squidex.Domain.Apps.Read.Apps
string PlanOwner { get; }
LanguagesConfig LanguagesConfig { get; }
AppClients Clients { get; }
IReadOnlyDictionary<string, IAppClientEntity> Clients { get; }
AppContributors Contributors { get; }
IReadOnlyDictionary<string, IAppContributorEntity> Contributors { get; }
LanguagesConfig LanguagesConfig { get; }
PartitionResolver PartitionResolver { get; }
}

2
src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs

@ -10,8 +10,8 @@ using System;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Edm;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Utils;

3
src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj

@ -7,7 +7,8 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>

56
src/Squidex.Domain.Apps.Write/Apps/AppClient.cs

@ -1,56 +0,0 @@
// ==========================================================================
// AppClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps
{
public sealed class AppClient
{
private readonly string name;
private readonly string secret;
private readonly AppClientPermission permission;
public AppClient(string secret, string name, AppClientPermission permission)
{
Guard.NotNullOrEmpty(name, nameof(name));
Guard.NotNullOrEmpty(secret, nameof(secret));
Guard.Enum(permission, nameof(permission));
this.name = name;
this.secret = secret;
this.permission = permission;
}
public AppClient Update(AppClientPermission newPermission, Func<string> message)
{
if (permission == newPermission)
{
var error = new ValidationError("Client has already the permission.", "IsReader");
throw new ValidationException(message(), error);
}
return new AppClient(secret, name, newPermission);
}
public AppClient Rename(string newName, Func<string> message)
{
if (string.Equals(name, newName))
{
var error = new ValidationError("Client already has the name.", "Id");
throw new ValidationException(message(), error);
}
return new AppClient(secret, newName, permission);
}
}
}

66
src/Squidex.Domain.Apps.Write/Apps/AppClients.cs

@ -1,66 +0,0 @@
// ==========================================================================
// AppClients.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppClients
{
private readonly Dictionary<string, AppClient> clients = new Dictionary<string, AppClient>();
public void Add(string id, string secret)
{
ThrowIfFound(id, () => "Cannot add client");
clients[id] = new AppClient(secret, id, AppClientPermission.Editor);
}
public void Rename(string clientId, string name)
{
ThrowIfNotFound(clientId);
clients[clientId] = clients[clientId].Rename(name, () => "Cannot rename client");
}
public void Update(string clientId, AppClientPermission permission)
{
ThrowIfNotFound(clientId);
clients[clientId] = clients[clientId].Update(permission, () => "Cannot update client");
}
public void Revoke(string clientId)
{
ThrowIfNotFound(clientId);
clients.Remove(clientId);
}
private void ThrowIfNotFound(string clientId)
{
if (!clients.ContainsKey(clientId))
{
throw new DomainObjectNotFoundException(clientId, "Contributors", typeof(AppDomainObject));
}
}
private void ThrowIfFound(string clientId, Func<string> message)
{
if (clients.ContainsKey(clientId))
{
var error = new ValidationError("Client id is alreay part of the app.", "Id");
throw new ValidationException(message(), error);
}
}
}
}

78
src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs

@ -1,78 +0,0 @@
// ==========================================================================
// AppContributors.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppContributors
{
private readonly Dictionary<string, AppContributorPermission> contributors = new Dictionary<string, AppContributorPermission>();
public int Count
{
get { return contributors.Count; }
}
public void Assign(string contributorId, AppContributorPermission permission)
{
string Message() => "Cannot assign contributor";
ThrowIfFound(contributorId, permission, Message);
ThrowIfNoOwner(c => c[contributorId] = permission, Message);
contributors[contributorId] = permission;
}
public void Remove(string contributorId)
{
string Message() => "Cannot remove contributor";
ThrowIfNotFound(contributorId);
ThrowIfNoOwner(c => c.Remove(contributorId), Message);
contributors.Remove(contributorId);
}
private void ThrowIfNotFound(string contributorId)
{
if (!contributors.ContainsKey(contributorId))
{
throw new DomainObjectNotFoundException(contributorId, "Contributors", typeof(AppDomainObject));
}
}
private void ThrowIfFound(string contributorId, AppContributorPermission permission, Func<string> message)
{
if (contributors.TryGetValue(contributorId, out var currentPermission) && currentPermission == permission)
{
var error = new ValidationError("Contributor is already part of the app with same permissions.", "ContributorId");
throw new ValidationException(message(), error);
}
}
private void ThrowIfNoOwner(Action<Dictionary<string, AppContributorPermission>> change, Func<string> message)
{
var contributorsCopy = new Dictionary<string, AppContributorPermission>(contributors);
change(contributorsCopy);
if (contributorsCopy.All(x => x.Value != AppContributorPermission.Owner))
{
var error = new ValidationError("Contributor is the last owner.", "ContributorId");
throw new ValidationException(message(), error);
}
}
}
}

23
src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs

@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Utils;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
@ -25,7 +26,7 @@ namespace Squidex.Domain.Apps.Write.Apps
private static readonly Language DefaultLanguage = Language.EN;
private readonly AppContributors contributors = new AppContributors();
private readonly AppClients clients = new AppClients();
private LanguagesConfig languagesConfig = LanguagesConfig.Empty;
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(DefaultLanguage);
private string name;
private string planId;
private RefToken planOwner;
@ -42,7 +43,7 @@ namespace Squidex.Domain.Apps.Write.Apps
public int ContributorCount
{
get { return contributors.Count; }
get { return contributors.Contributors.Count; }
}
public AppDomainObject(Guid id, int version)
@ -57,47 +58,47 @@ namespace Squidex.Domain.Apps.Write.Apps
protected void On(AppContributorAssigned @event)
{
contributors.Assign(@event.ContributorId, @event.Permission);
contributors.Apply(@event);
}
protected void On(AppContributorRemoved @event)
{
contributors.Remove(@event.ContributorId);
contributors.Apply(@event);
}
protected void On(AppClientAttached @event)
{
clients.Add(@event.Id, @event.Secret);
clients.Apply(@event);
}
protected void On(AppClientUpdated @event)
{
clients.Update(@event.Id, @event.Permission);
clients.Apply(@event);
}
protected void On(AppClientRenamed @event)
{
clients.Rename(@event.Id, @event.Name);
clients.Apply(@event);
}
protected void On(AppClientRevoked @event)
{
clients.Revoke(@event.Id);
clients.Apply(@event);
}
protected void On(AppLanguageAdded @event)
{
languagesConfig = languagesConfig.Add(@event.Language);
languagesConfig.Apply(@event);
}
protected void On(AppLanguageRemoved @event)
{
languagesConfig = languagesConfig.Remove(@event.Language);
languagesConfig.Apply(@event);
}
protected void On(AppLanguageUpdated @event)
{
languagesConfig = languagesConfig.Update(@event.Language, @event.IsOptional, @event.IsMaster, @event.Fallback);
languagesConfig.Apply(@event);
}
protected void On(AppPlanChanged @event)

12
src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs

@ -6,22 +6,12 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public abstract class ContentDataCommand : ContentCommand, IValidatable
public abstract class ContentDataCommand : ContentCommand
{
public NamedContentData Data { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Data == null)
{
errors.Add(new ValidationError("Data cannot be null.", nameof(Data)));
}
}
}
}

7
src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs

@ -6,17 +6,10 @@
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public sealed class CreateContent : ContentDataCommand
{
public bool Publish { get; set; }
public CreateContent()
{
ContentId = Guid.NewGuid();
}
}
}

2
src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs

@ -6,6 +6,8 @@
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public sealed class PatchContent : ContentDataCommand

2
src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs

@ -6,6 +6,8 @@
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands
{
public sealed class UpdateContent : ContentDataCommand

6
src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs

@ -10,9 +10,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.EnrichContent;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Read.Assets.Repositories;
@ -147,8 +147,6 @@ namespace Squidex.Domain.Apps.Write.Contents
private async Task ValidateAsync((ISchemaEntity Schema, IAppEntity App) schemaAndApp, ContentDataCommand command, Func<string> message, bool partial)
{
Guard.Valid(command, nameof(command), message);
var schemaErrors = new List<ValidationError>();
var appId = command.AppId.Id;

19
src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs

@ -69,8 +69,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Create(CreateContent command)
{
Guard.Valid(command, nameof(command), () => "Cannot create content");
VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new ContentCreated()));
@ -85,8 +83,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Delete(DeleteContent command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ContentDeleted()));
@ -96,10 +92,7 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject ChangeStatus(ChangeContentStatus command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
VerifyCanChangeStatus(command.Status);
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged()));
@ -108,8 +101,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Update(UpdateContent command)
{
Guard.Valid(command, nameof(command), () => "Cannot update content");
VerifyCreatedAndNotDeleted();
if (!command.Data.Equals(Data))
@ -122,8 +113,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Patch(PatchContent command)
{
Guard.Valid(command, nameof(command), () => "Cannot patch content");
VerifyCreatedAndNotDeleted();
var newData = Data.MergeInto(command.Data);
@ -136,14 +125,6 @@ namespace Squidex.Domain.Apps.Write.Contents
return this;
}
private void VerifyCanChangeStatus(Status newStatus)
{
if (!StatusFlow.Exists(newStatus) || !StatusFlow.CanChange(status, newStatus))
{
throw new DomainException($"Content cannot be changed from status {status} to {newStatus}.");
}
}
private void VerifyNotCreated()
{
if (isCreated)

69
src/Squidex.Domain.Apps.Write/Contents/Guards/GuardContent.cs

@ -0,0 +1,69 @@
// ==========================================================================
// GuardContent.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Contents.Guards
{
public static class GuardContent
{
public static void CanCreate(CreateContent command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot created content.", error =>
{
if (command.Data == null)
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
});
}
public static void CanCreate(UpdateContent command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot update content.", error =>
{
if (command.Data == null)
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
});
}
public static void CanCreate(PatchContent command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot patch content.", error =>
{
if (command.Data == null)
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
});
}
public static void CanChangeStatus(Status status, ChangeContentStatus command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot change status.", error =>
{
if (!StatusFlow.Exists(command.Status) || !StatusFlow.CanChange(status, command.Status))
{
error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status)));
}
});
}
}
}

33
src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs

@ -6,47 +6,16 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public sealed class AddField : SchemaAggregateCommand, IValidatable
public sealed class AddField : SchemaAggregateCommand
{
public string Name { get; set; }
public string Partitioning { get; set; }
public FieldProperties Properties { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (!Partitioning.IsValidPartitioning())
{
errors.Add(new ValidationError("Partitioning is not valid.", nameof(Partitioning)));
}
if (!Name.IsPropertyName())
{
errors.Add(new ValidationError("Name must be a valid property name.", nameof(Name)));
}
if (Properties == null)
{
errors.Add(new ValidationError("Properties must be defined.", nameof(Properties)));
}
else
{
var propertyErrors = FieldPropertiesValidator.Validate(Properties);
foreach (var error in propertyErrors)
{
errors.Add(error);
}
}
}
}
}

55
src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs

@ -7,45 +7,19 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using SchemaFields = System.Collections.Generic.List<Squidex.Domain.Apps.Write.Schemas.Commands.CreateSchemaField>;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public sealed class CreateSchema : AppCommand, IValidatable, IAggregateCommand
public sealed class CreateSchema : AppCommand, IAggregateCommand
{
private SchemaProperties properties;
private SchemaFields fields;
public Guid SchemaId { get; set; }
public SchemaProperties Properties
{
get
{
return properties ?? (properties = new SchemaProperties());
}
set
{
properties = value;
}
}
public SchemaFields Fields { get; set; }
public SchemaFields Fields
{
get
{
return fields ?? (fields = new SchemaFields());
}
set
{
fields = value;
}
}
public SchemaProperties Properties { get; set; }
public string Name { get; set; }
@ -58,28 +32,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
SchemaId = Guid.NewGuid();
}
public void Validate(IList<ValidationError> errors)
{
if (!Name.IsSlug())
{
errors.Add(new ValidationError("Name must be a valid slug.", nameof(Name)));
}
if (Fields.Any())
{
var index = 0;
foreach (var field in Fields)
{
field.Validate(index++, errors);
}
if (Fields.Select(x => x.Name).Distinct().Count() != Fields.Count)
{
errors.Add(new ValidationError("Fields cannot have duplicate names.", nameof(Fields)));
}
}
}
}
}

33
src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs

@ -6,11 +6,7 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
@ -27,34 +23,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands
public bool IsDisabled { get; set; }
public FieldProperties Properties { get; set; }
public void Validate(int index, IList<ValidationError> errors)
{
var prefix = $"Fields.{index}";
if (!Partitioning.IsValidPartitioning())
{
errors.Add(new ValidationError("Partitioning is not valid.", $"{prefix}.{nameof(Partitioning)}"));
}
if (!Name.IsPropertyName())
{
errors.Add(new ValidationError("Name must be a valid property name.", $"{prefix}.{nameof(Name)}"));
}
if (Properties == null)
{
errors.Add(new ValidationError("Properties must be defined.", $"{prefix}.{nameof(Properties)}"));
}
else
{
var propertyErrors = FieldPropertiesValidator.Validate(Properties);
foreach (var error in propertyErrors)
{
errors.Add(error);
}
}
}
}
}

11
src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs

@ -7,20 +7,11 @@
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public sealed class ReorderFields : SchemaAggregateCommand, IValidatable
public sealed class ReorderFields : SchemaAggregateCommand
{
public List<long> FieldIds { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (FieldIds == null)
{
errors.Add(new ValidationError("Field ids must be specified.", nameof(FieldIds)));
}
}
}
}

12
src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs

@ -6,22 +6,12 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public sealed class UpdateField : FieldCommand, IValidatable
public sealed class UpdateField : FieldCommand
{
public FieldProperties Properties { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Properties == null)
{
errors.Add(new ValidationError("Properties must be defined.", nameof(Properties)));
}
}
}
}

12
src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs

@ -6,22 +6,12 @@
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands
{
public sealed class UpdateSchema : SchemaAggregateCommand, IValidatable
public sealed class UpdateSchema : SchemaAggregateCommand
{
public SchemaProperties Properties { get; set; }
public void Validate(IList<ValidationError> errors)
{
if (Properties == null)
{
errors.Add(new ValidationError("Properties must be specified.", nameof(Properties)));
}
}
}
}

11
src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs

@ -7,6 +7,7 @@
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
@ -22,7 +23,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
public static IEnumerable<ValidationError> Validate(FieldProperties properties)
{
return properties.Accept(Instance);
return properties?.Accept(Instance) ?? Enumerable.Empty<ValidationError>();
}
public IEnumerable<ValidationError> Visit(AssetsFieldProperties properties)
@ -110,7 +111,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.Editor));
}
if ((properties.Editor == NumberFieldEditor.Radio || properties.Editor == NumberFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Count == 0))
if ((properties.Editor == NumberFieldEditor.Radio || properties.Editor == NumberFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Length == 0))
{
yield return new ValidationError("Radio buttons or dropdown list need allowed values.",
nameof(properties.AllowedValues));
@ -135,7 +136,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.MaxValue));
}
if (properties.AllowedValues != null && properties.AllowedValues.Count > 0 && (properties.MinValue.HasValue || properties.MaxValue.HasValue))
if (properties.AllowedValues != null && properties.AllowedValues.Length > 0 && (properties.MinValue.HasValue || properties.MaxValue.HasValue))
{
yield return new ValidationError("Either allowed values or min and max value can be defined.",
nameof(properties.AllowedValues),
@ -162,7 +163,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.Editor));
}
if ((properties.Editor == StringFieldEditor.Radio || properties.Editor == StringFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Count == 0))
if ((properties.Editor == StringFieldEditor.Radio || properties.Editor == StringFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Length == 0))
{
yield return new ValidationError("Radio buttons or dropdown list need allowed values.",
nameof(properties.AllowedValues));
@ -181,7 +182,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.MaxLength));
}
if (properties.AllowedValues != null && properties.AllowedValues.Count > 0 && (properties.MinLength.HasValue || properties.MaxLength.HasValue))
if (properties.AllowedValues != null && properties.AllowedValues.Length > 0 && (properties.MinLength.HasValue || properties.MaxLength.HasValue))
{
yield return new ValidationError("Either allowed values or min and max length can be defined.",
nameof(properties.AllowedValues),

130
src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchema.cs

@ -0,0 +1,130 @@
// ==========================================================================
// GuardSchema.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public static class GuardSchema
{
public static Task CanCreate(CreateSchema command, ISchemaProvider schemas)
{
Guard.NotNull(command, nameof(command));
return Validate.It(() => "Cannot create schema.", async error =>
{
if (!command.Name.IsSlug())
{
error(new ValidationError("Name must be a valid slug.", nameof(command.Name)));
}
if (await schemas.FindSchemaByNameAsync(command.AppId.Id, command.Name) != null)
{
error(new ValidationError($"A schema with name '{command.Name}' already exists", nameof(command.Name)));
}
if (command.Fields != null && command.Fields.Any())
{
var index = 0;
foreach (var field in command.Fields)
{
var prefix = $"Fields.{index}";
if (!field.Partitioning.IsValidPartitioning())
{
error(new ValidationError("Partitioning is not valid.", $"{prefix}.{nameof(field.Partitioning)}"));
}
if (!field.Name.IsPropertyName())
{
error(new ValidationError("Name must be a valid property name.", $"{prefix}.{nameof(field.Name)}"));
}
if (field.Properties == null)
{
error(new ValidationError("Properties must be defined.", $"{prefix}.{nameof(field.Properties)}"));
}
var propertyErrors = FieldPropertiesValidator.Validate(field.Properties);
foreach (var propertyError in propertyErrors)
{
error(propertyError);
}
}
if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count)
{
error(new ValidationError("Fields cannot have duplicate names.", nameof(command.Fields)));
}
}
});
}
public static void CanReorder(Schema schema, ReorderFields command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot reorder schema fields.", error =>
{
if (command.FieldIds == null)
{
error(new ValidationError("Field ids must be specified.", nameof(command.FieldIds)));
}
if (command.FieldIds.Count != schema.Fields.Count || command.FieldIds.Any(x => !schema.FieldsById.ContainsKey(x)))
{
error(new ValidationError("Ids must cover all fields.", nameof(command.FieldIds)));
}
});
}
public static void CanPublish(Schema schema, PublishSchema command)
{
Guard.NotNull(command, nameof(command));
if (schema.IsPublished)
{
throw new DomainException("Schema is already published.");
}
}
public static void CanUnpublish(Schema schema, UnpublishSchema command)
{
Guard.NotNull(command, nameof(command));
if (!schema.IsPublished)
{
throw new DomainException("Schema is not published.");
}
}
public static void CanUpdate(Schema schema, UpdateSchema command)
{
Guard.NotNull(command, nameof(command));
}
public static void CanConfigureScripts(Schema schema, ConfigureScripts command)
{
Guard.NotNull(command, nameof(command));
}
public static void CanDelete(Schema schema, DeleteSchema command)
{
Guard.NotNull(command, nameof(command));
}
}
}

160
src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchemaField.cs

@ -0,0 +1,160 @@
// ==========================================================================
// SchemaFieldGuard.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public static class GuardSchemaField
{
public static void CanAdd(Schema schema, AddField command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot add a new field.", error =>
{
if (!command.Partitioning.IsValidPartitioning())
{
error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning)));
}
if (!command.Name.IsPropertyName())
{
error(new ValidationError("Name must be a valid property name.", nameof(command.Name)));
}
if (command.Properties == null)
{
error(new ValidationError("Properties must be defined.", nameof(command.Properties)));
}
var propertyErrors = FieldPropertiesValidator.Validate(command.Properties);
foreach (var propertyError in propertyErrors)
{
error(propertyError);
}
if (schema.FieldsByName.ContainsKey(command.Name))
{
error(new ValidationError($"There is already a field with name '{command.Name}'", nameof(command.Name)));
}
});
}
public static void CanUpdate(Schema schema, UpdateField command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot update field.", error =>
{
if (command.Properties == null)
{
error(new ValidationError("Properties must be defined.", nameof(command.Properties)));
}
var propertyErrors = FieldPropertiesValidator.Validate(command.Properties);
foreach (var propertyError in propertyErrors)
{
error(propertyError);
}
});
var field = GetFieldOrThrow(schema, command.FieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is already locked.");
}
}
public static void CanDelete(Schema schema, DeleteField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is locked.");
}
}
public static void CanHide(Schema schema, HideField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId);
if (field.IsHidden)
{
throw new DomainException("Schema field is already hidden.");
}
}
public static void CanShow(Schema schema, ShowField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId);
if (!field.IsHidden)
{
throw new DomainException("Schema field is already visible.");
}
}
public static void CanDisable(Schema schema, DisableField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId);
if (field.IsDisabled)
{
throw new DomainException("Schema field is already disabled.");
}
}
public static void CanEnable(Schema schema, EnableField command)
{
var field = GetFieldOrThrow(schema, command.FieldId);
if (!field.IsDisabled)
{
throw new DomainException("Schema field is already enabled.");
}
}
public static void CanLock(Schema schema, LockField command)
{
Guard.NotNull(command, nameof(command));
var field = GetFieldOrThrow(schema, command.FieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is already locked.");
}
}
private static Field GetFieldOrThrow(Schema schema, long fieldId)
{
if (!schema.FieldsById.TryGetValue(fieldId, out var field))
{
throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field));
}
return field;
}
}
}

106
src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaFieldGuard.cs

@ -1,106 +0,0 @@
// ==========================================================================
// SchemaFieldGuard.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public static class SchemaFieldGuard
{
public static void GuardCanAdd(Schema schema, string name)
{
if (schema.FieldsByName.ContainsKey(name))
{
var error = new ValidationError($"There is already a field with name '{name}'", "Name");
throw new ValidationException("Cannot add a new field.", error);
}
}
public static void GuardCanDelete(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is locked.");
}
}
public static void GuardCanHide(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (field.IsHidden)
{
throw new DomainException("Schema field is already hidden.");
}
}
public static void GuardCanShow(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (!field.IsHidden)
{
throw new DomainException("Schema field is already visible.");
}
}
public static void GuardCanDisable(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (field.IsDisabled)
{
throw new DomainException("Schema field is already disabled.");
}
}
public static void GuardCanEnable(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (!field.IsDisabled)
{
throw new DomainException("Schema field is already enabled.");
}
}
public static void GuardCanLock(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is already locked.");
}
}
public static void GuardCanUpdate(Schema schema, long fieldId)
{
var field = GetFieldOrThrow(schema, fieldId);
if (field.IsLocked)
{
throw new DomainException("Schema field is already locked.");
}
}
private static Field GetFieldOrThrow(Schema schema, long fieldId)
{
if (!schema.FieldsById.TryGetValue(fieldId, out var field))
{
throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field));
}
return field;
}
}
}

44
src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaGuard.cs

@ -1,44 +0,0 @@
// ==========================================================================
// SchemaGuard.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public static class SchemaGuard
{
public static void GuardCanReorder(Schema schema, List<long> fieldIds)
{
if (fieldIds.Count != schema.Fields.Count || fieldIds.Any(x => !schema.FieldsById.ContainsKey(x)))
{
var error = new ValidationError("Ids must cover all fields.", "FieldIds");
throw new ValidationException("Cannot reorder schema fields.", error);
}
}
public static void GuardCanPublish(Schema schema)
{
if (schema.IsPublished)
{
throw new DomainException("Schema is already published.");
}
}
public static void GuardCanUnpublish(Schema schema)
{
if (!schema.IsPublished)
{
throw new DomainException("Schema is not published.");
}
}
}
}

121
src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs

@ -11,6 +11,7 @@ using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Dispatching;
@ -31,19 +32,12 @@ namespace Squidex.Domain.Apps.Write.Schemas
this.schemas = schemas;
}
protected async Task On(CreateSchema command, CommandContext context)
protected Task On(CreateSchema command, CommandContext context)
{
if (await schemas.FindSchemaByNameAsync(command.AppId.Id, command.Name) != null)
return handler.CreateAsync<SchemaDomainObject>(context, async s =>
{
var error =
new ValidationError($"A schema with name '{command.Name}' already exists", "Name",
nameof(CreateSchema.Name));
await GuardSchema.CanCreate(command, schemas);
throw new ValidationException("Cannot create a new schema.", error);
}
await handler.CreateAsync<SchemaDomainObject>(context, s =>
{
s.Create(command);
context.Complete(EntityCreatedResult.Create(s.Id, s.Version));
@ -54,75 +48,142 @@ namespace Squidex.Domain.Apps.Write.Schemas
{
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanAdd(s.Schema, command);
s.Add(command);
context.Complete(EntityCreatedResult.Create(s.Schema.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version));
});
}
protected Task On(DeleteSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Delete(command));
}
protected Task On(DeleteField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.DeleteField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDelete(s.Schema, command);
s.DeleteField(command);
});
}
protected Task On(LockField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.LockField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanLock(s.Schema, command);
s.LockField(command);
});
}
protected Task On(HideField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.HideField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanHide(s.Schema, command);
s.HideField(command);
});
}
protected Task On(ShowField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.ShowField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanShow(s.Schema, command);
s.ShowField(command);
});
}
protected Task On(DisableField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.DisableField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDisable(s.Schema, command);
s.DisableField(command);
});
}
protected Task On(EnableField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.EnableField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanEnable(s.Schema, command);
s.EnableField(command);
});
}
protected Task On(ReorderFields command, CommandContext context)
protected Task On(UpdateField command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Reorder(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanUpdate(s.Schema, command);
s.UpdateField(command);
});
}
protected Task On(UpdateSchema command, CommandContext context)
protected Task On(ReorderFields command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Update(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanReorder(s.Schema, command);
s.Reorder(command);
});
}
protected Task On(UpdateField command, CommandContext context)
protected Task On(UpdateSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.UpdateField(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUpdate(s.Schema, command);
s.Update(command);
});
}
protected Task On(PublishSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Publish(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanPublish(s.Schema, command);
s.Publish(command);
});
}
protected Task On(UnpublishSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.Unpublish(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUnpublish(s.Schema, command);
s.Unpublish(command);
});
}
protected Task On(ConfigureScripts command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s => s.ConfigureScripts(command));
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanConfigureScripts(s.Schema, command);
s.ConfigureScripts(command);
});
}
protected Task On(DeleteSchema command, CommandContext context)
{
return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanDelete(s.Schema, command);
s.Delete(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)

85
src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs

@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Utils;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Events;
@ -46,73 +45,73 @@ namespace Squidex.Domain.Apps.Write.Schemas
this.registry = registry;
}
public void On(FieldAdded @event)
protected void On(SchemaCreated @event)
{
totalFields++;
totalFields += @event.Fields?.Count ?? 0;
schema = SchemaEventDispatcher.Dispatch(@event, schema, registry);
schema = SchemaEventDispatcher.Create(@event, registry);
}
protected void On(SchemaCreated @event)
public void On(FieldAdded @event)
{
totalFields += @event.Fields?.Count ?? 0;
totalFields++;
schema = SchemaEventDispatcher.Dispatch(@event, registry);
schema.Apply(@event, registry);
}
protected void On(FieldUpdated @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldLocked @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldHidden @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldShown @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldDisabled @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldEnabled @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(SchemaUpdated @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(FieldDeleted @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(SchemaFieldsReordered @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(SchemaPublished @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(SchemaUnpublished @event)
{
schema = SchemaEventDispatcher.Dispatch(@event, schema);
schema.Apply(@event);
}
protected void On(SchemaDeleted @event)
@ -122,8 +121,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Create(CreateSchema command)
{
Guard.Valid(command, nameof(command), () => "Cannot create schema");
VerifyNotCreated();
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(Id, command.Name) });
@ -147,12 +144,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Add(AddField command)
{
Guard.Valid(command, nameof(command), () => $"Cannot add field to schema {Id}");
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanAdd(schema, command.Name);
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(totalFields + 1, command.Name) }));
return this;
@ -160,12 +153,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject UpdateField(UpdateField command)
{
Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'");
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanUpdate(schema, command.FieldId);
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
return this;
@ -173,12 +162,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject LockField(LockField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanLock(schema, command.FieldId);
RaiseEvent(command, new FieldLocked());
return this;
@ -186,12 +171,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject HideField(HideField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanHide(schema, command.FieldId);
RaiseEvent(command, new FieldHidden());
return this;
@ -199,12 +180,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject ShowField(ShowField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanShow(schema, command.FieldId);
RaiseEvent(command, new FieldShown());
return this;
@ -212,12 +189,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject DisableField(DisableField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanDisable(schema, command.FieldId);
RaiseEvent(command, new FieldDisabled());
return this;
@ -225,12 +198,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject EnableField(EnableField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanEnable(schema, command.FieldId);
RaiseEvent(command, new FieldEnabled());
return this;
@ -238,12 +207,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject DeleteField(DeleteField command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanDelete(schema, command.FieldId);
RaiseEvent(command, new FieldDeleted());
return this;
@ -251,12 +216,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Reorder(ReorderFields command)
{
Guard.Valid(command, nameof(command), () => $"Cannot reorder fields for schema '{Id}'");
VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanReorder(schema, command.FieldIds);
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
return this;
@ -264,12 +225,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Publish(PublishSchema command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanPublish(schema);
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
return this;
@ -277,12 +234,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Unpublish(UnpublishSchema command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanUnpublish(schema);
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
return this;
@ -290,8 +243,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject ConfigureScripts(ConfigureScripts command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
@ -310,8 +261,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Update(UpdateSchema command)
{
Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'");
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));

3
src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj

@ -7,7 +7,8 @@
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj" />

25
src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs

@ -9,34 +9,13 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Webhooks.Commands
{
public abstract class WebhookEditCommand : WebhookAggregateCommand, IValidatable
public abstract class WebhookEditCommand : WebhookAggregateCommand
{
private List<WebhookSchema> schemas = new List<WebhookSchema>();
public Uri Url { get; set; }
public List<WebhookSchema> Schemas
{
get
{
return schemas ?? (schemas = new List<WebhookSchema>());
}
set
{
schemas = value;
}
}
public virtual void Validate(IList<ValidationError> errors)
{
if (Url == null || !Url.IsAbsoluteUri)
{
errors.Add(new ValidationError("Url must be specified and absolute.", nameof(Url)));
}
}
public List<WebhookSchema> Schemas { get; set; } = new List<WebhookSchema>();
}
}

63
src/Squidex.Domain.Apps.Write/Webhooks/Guards/GuardWebhook.cs

@ -0,0 +1,63 @@
// ==========================================================================
// GuardWebhook.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Webhooks.Guards
{
public static class GuardWebhook
{
public static Task CanCreate(CreateWebhook command, ISchemaProvider schemas)
{
Guard.NotNull(command, nameof(command));
return Validate.It(() => "Cannot create webhook.", error => ValidateCommandAsync(command, error, schemas));
}
public static Task CanUpdate(UpdateWebhook command, ISchemaProvider schemas)
{
Guard.NotNull(command, nameof(command));
return Validate.It(() => "Cannot update webhook.", error => ValidateCommandAsync(command, error, schemas));
}
public static void CanDelete(DeleteWebhook command)
{
Guard.NotNull(command, nameof(command));
}
private static async Task ValidateCommandAsync(WebhookEditCommand command, Action<ValidationError> error, ISchemaProvider schemas)
{
if (command.Url == null || !command.Url.IsAbsoluteUri)
{
error(new ValidationError("Url must be specified and absolute.", nameof(command.Url)));
}
if (command.Schemas == null)
{
error(new ValidationError("Schemas cannot be null.", nameof(command.Schemas)));
}
var schemaErrors = await Task.WhenAll(
command.Schemas.Select(async s =>
await schemas.FindSchemaByIdAsync(s.SchemaId) == null
? new ValidationError($"Schema {s.SchemaId} does not exist.", nameof(command.Schemas))
: null));
foreach (var schemaError in schemaErrors.Where(x => x != null))
{
error(schemaError);
}
}
}
}

39
src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs

@ -7,10 +7,10 @@
// ==========================================================================
using System;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Domain.Apps.Write.Webhooks.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Dispatching;
@ -33,21 +33,32 @@ namespace Squidex.Domain.Apps.Write.Webhooks
protected async Task On(CreateWebhook command, CommandContext context)
{
await ValidateAsync(command, () => "Failed to create webhook");
await handler.CreateAsync<WebhookDomainObject>(context, async w =>
{
await GuardWebhook.CanCreate(command, schemas);
await handler.CreateAsync<WebhookDomainObject>(context, c => c.Create(command));
w.Create(command);
});
}
protected async Task On(UpdateWebhook command, CommandContext context)
{
await ValidateAsync(command, () => "Failed to update content");
await handler.UpdateAsync<WebhookDomainObject>(context, async c =>
{
await GuardWebhook.CanUpdate(command, schemas);
await handler.UpdateAsync<WebhookDomainObject>(context, c => c.Update(command));
c.Update(command);
});
}
protected Task On(DeleteWebhook command, CommandContext context)
{
return handler.UpdateAsync<WebhookDomainObject>(context, c => c.Delete(command));
return handler.UpdateAsync<WebhookDomainObject>(context, c =>
{
GuardWebhook.CanDelete(command);
c.Delete(command);
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
@ -57,21 +68,5 @@ namespace Squidex.Domain.Apps.Write.Webhooks
await next();
}
}
private async Task ValidateAsync(WebhookEditCommand command, Func<string> message)
{
var results = await Task.WhenAll(
command.Schemas.Select(async schema =>
await schemas.FindSchemaByIdAsync(schema.SchemaId) == null
? new ValidationError($"Schema {schema.SchemaId} does not exist.")
: null));
var errors = results.Where(x => x != null).ToArray();
if (errors.Length > 0)
{
throw new ValidationException(message(), errors);
}
}
}
}

6
src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs

@ -39,8 +39,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks
public void Create(CreateWebhook command)
{
Guard.Valid(command, nameof(command), () => "Cannot create webhook");
VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new WebhookCreated()));
@ -48,8 +46,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks
public void Update(UpdateWebhook command)
{
Guard.Valid(command, nameof(command), () => "Cannot update webhook");
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookUpdated()));
@ -57,8 +53,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks
public void Delete(DeleteWebhook command)
{
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookDeleted()));

4
src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs → src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonConverter.cs

@ -1,5 +1,5 @@
// ==========================================================================
// BsonConverter.cs
// JsonBsonConverter.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -12,7 +12,7 @@ using Newtonsoft.Json.Linq;
namespace Squidex.Infrastructure.MongoDb
{
public static class BsonConverter
public static class JsonBsonConverter
{
public static BsonDocument ToBson(this JObject source)
{

38
src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonSerializer.cs

@ -0,0 +1,38 @@
// ==========================================================================
// RefTokenSerializer.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Squidex.Infrastructure.MongoDb
{
public class JsonBsonSerializer : ClassSerializerBase<object>
{
private readonly JsonSerializer serializer;
public JsonBsonSerializer(JsonSerializer serializer)
{
Guard.NotNull(serializer, nameof(serializer));
this.serializer = serializer;
}
protected override object DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
return BsonSerializer.Deserialize<BsonDocument>(context.Reader).ToJson().ToObject(args.NominalType, serializer);
}
protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, object value)
{
BsonSerializer.Serialize(context.Writer, JObject.FromObject(value, serializer).ToBson());
}
}
}

17
src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs

@ -12,7 +12,7 @@ using MongoDB.Bson.Serialization.Serializers;
namespace Squidex.Infrastructure.MongoDb
{
public class RefTokenSerializer : SerializerBase<RefToken>
public class RefTokenSerializer : ClassSerializerBase<RefToken>
{
private static readonly Lazy<bool> Registerer = new Lazy<bool>(() =>
{
@ -26,23 +26,16 @@ namespace Squidex.Infrastructure.MongoDb
return !Registerer.IsValueCreated && Registerer.Value;
}
public override RefToken Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
protected override RefToken DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var value = context.Reader.ReadString();
return value != null ? RefToken.Parse(value) : null;
return RefToken.Parse(value);
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, RefToken value)
protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, RefToken value)
{
if (value != null)
{
context.Writer.WriteString(value.ToString());
}
else
{
context.Writer.WriteNull();
}
context.Writer.WriteString(value.ToString());
}
}
}

42
src/Squidex.Infrastructure/Validate.cs

@ -0,0 +1,42 @@
// ==========================================================================
// TypeNameRegistry.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Squidex.Infrastructure
{
public static class Validate
{
public static void It(Func<string> message, Action<Action<ValidationError>> action)
{
var errors = new List<ValidationError>();
action(errors.Add);
if (errors.Any())
{
throw new ValidationException(message(), errors);
}
}
public static async Task It(Func<string> message, Func<Action<ValidationError>, Task> action)
{
var errors = new List<ValidationError>();
await action(errors.Add);
if (errors.Any())
{
throw new ValidationException(message(), errors);
}
}
}
}

1
src/Squidex/Squidex.csproj

@ -29,7 +29,6 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Domain.Users.MongoDb\Squidex.Domain.Users.MongoDb.csproj" />
<ProjectReference Include="..\Squidex.Domain.Users\Squidex.Domain.Users.csproj" />

3
tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj

@ -10,7 +10,8 @@
<None Remove="MongoDb\**" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj" />

330
tests/Squidex.Domain.Apps.Write.Tests/Apps/AppCommandMiddlewareTests.cs

@ -1,330 +0,0 @@
// ==========================================================================
// AppCommandMiddlewareTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Read.Apps.Services.Implementations;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Shared.Users;
using Xunit;
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppCommandMiddlewareTests : HandlerTestBase<AppDomainObject>
{
private readonly IAppRepository appRepository = A.Fake<IAppRepository>();
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>();
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>();
private readonly IUserResolver userResolver = A.Fake<IUserResolver>();
private readonly AppCommandMiddleware sut;
private readonly AppDomainObject app;
private readonly Language language = Language.DE;
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientName = "client";
public AppCommandMiddlewareTests()
{
app = new AppDomainObject(AppId, -1);
sut = new AppCommandMiddleware(Handler, appRepository, appPlansProvider, appPlansBillingManager, userResolver);
}
[Fact]
public async Task Create_should_throw_exception_if_a_name_with_same_name_already_exists()
{
var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId });
A.CallTo(() => appRepository.FindAppAsync(AppName))
.Returns(A.Dummy<IAppEntity>());
await TestCreate(app, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(async () => await sut.HandleAsync(context));
}, false);
A.CallTo(() => appRepository.FindAppAsync(AppName)).MustHaveHappened();
}
[Fact]
public async Task Create_should_create_app_if_name_is_free()
{
var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId });
A.CallTo(() => appRepository.FindAppAsync(AppName))
.Returns((IAppEntity)null);
await TestCreate(app, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(AppId, context.Result<EntityCreatedResult<Guid>>().IdOrValue);
}
[Fact]
public async Task AssignContributor_should_throw_exception_if_user_not_found()
{
CreateApp();
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId });
A.CallTo(() => userResolver.FindByIdAsync(contributorId))
.Returns((IUser)null);
await TestUpdate(app, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task AssignContributor_throw_exception_if_reached_max_contributor_size()
{
A.CallTo(() => appPlansProvider.GetPlan(null))
.Returns(new ConfigAppLimitsPlan { MaxContributors = 2 });
CreateApp()
.AssignContributor(CreateCommand(new AssignContributor { ContributorId = "1" }))
.AssignContributor(CreateCommand(new AssignContributor { ContributorId = "2" }));
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId });
A.CallTo(() => userResolver.FindByIdAsync(A<string>.Ignored))
.Returns(A.Dummy<IUser>());
await TestUpdate(app, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task AssignContributor_should_throw_exception_if_null_user_not_found()
{
CreateApp();
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId });
A.CallTo(() => userResolver.FindByIdAsync(contributorId))
.Returns((IUser)null);
await TestUpdate(app, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task AssignContributor_should_assign_if_user_found()
{
A.CallTo(() => appPlansProvider.GetPlan(null))
.Returns(new ConfigAppLimitsPlan { MaxContributors = -1 });
CreateApp();
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId });
A.CallTo(() => userResolver.FindByIdAsync(contributorId))
.Returns(A.Dummy<IUser>());
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task RemoveContributor_should_update_domain_object()
{
CreateApp()
.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId }));
var context = CreateContextForCommand(new RemoveContributor { ContributorId = contributorId });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task AttachClient_should_update_domain_object()
{
CreateApp();
var context = CreateContextForCommand(new AttachClient { Id = clientName });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task ChangePlan_should_throw_if_plan_not_found()
{
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan"))
.Returns(false);
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
await TestUpdate(app, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task RenameClient_should_update_domain_object()
{
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new UpdateClient { Id = clientName, Name = "New Name" });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task RevokeClient_should_update_domain_object()
{
CreateApp()
.AttachClient(CreateCommand(new AttachClient { Id = clientName }));
var context = CreateContextForCommand(new RevokeClient { Id = clientName });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task ChangePlan_should_update_domain_object()
{
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan"))
.Returns(true);
CreateApp();
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustHaveHappened();
}
[Fact]
public async Task ChangePlan_should_not_make_update_for_redirect_result()
{
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan"))
.Returns(true);
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan"))
.Returns(CreateRedirectResult());
CreateApp();
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Null(app.PlanId);
}
[Fact]
public async Task ChangePlan_should_not_call_billing_manager_for_callback()
{
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan"))
.Returns(true);
CreateApp();
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan", FromCallback = true });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustNotHaveHappened();
}
[Fact]
public async Task AddLanguage_should_update_domain_object()
{
CreateApp();
var context = CreateContextForCommand(new AddLanguage { Language = language });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task RemoveLanguage_should_update_domain_object()
{
CreateApp()
.AddLanguage(CreateCommand(new AddLanguage { Language = language }));
var context = CreateContextForCommand(new RemoveLanguage { Language = language });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task UpdateLanguage_should_update_domain_object()
{
CreateApp()
.AddLanguage(CreateCommand(new AddLanguage { Language = language }));
var context = CreateContextForCommand(new UpdateLanguage { Language = language });
await TestUpdate(app, async _ =>
{
await sut.HandleAsync(context);
});
}
private AppDomainObject CreateApp()
{
app.Create(CreateCommand(new CreateApp { Name = AppName }));
return app;
}
private static Task<IChangePlanResult> CreateRedirectResult()
{
return Task.FromResult<IChangePlanResult>(new RedirectToCheckoutResult(new Uri("http://squidex.io")));
}
}
}

597
tests/Squidex.Domain.Apps.Write.Tests/Apps/AppDomainObjectTests.cs

@ -1,597 +0,0 @@
// ==========================================================================
// AppDomainObjectTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
using Xunit;
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppDomainObjectTests : HandlerTestBase<AppDomainObject>
{
private readonly AppDomainObject sut;
private readonly string contributorId = Guid.NewGuid().ToString();
private readonly string clientId = "client";
private readonly string clientNewName = "My Client";
private readonly string planId = "premium";
public AppDomainObjectTests()
{
sut = new AppDomainObject(AppId, 0);
}
[Fact]
public void Create_should_throw_exception_if_created()
{
CreateApp();
Assert.Throws<DomainException>(() =>
{
sut.Create(CreateCommand(new CreateApp { Name = AppName }));
});
}
[Fact]
public void Create_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateCommand(new CreateApp()));
});
}
[Fact]
public void Create_should_specify_name_and_owner()
{
sut.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId }));
Assert.Equal(AppName, sut.Name);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppCreated { Name = AppName }),
CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }),
CreateEvent(new AppLanguageAdded { Language = Language.EN })
);
}
[Fact]
public void ChangePlan_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
});
}
[Fact]
public void ChangePlan_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.ChangePlan(CreateCommand(new ChangePlan()));
});
}
[Fact]
public void ChangePlan_should_throw_exception_if_plan_configured_from_other_user()
{
CreateApp();
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = "other-plan", Actor = new RefToken("User", "other") }));
Assert.Throws<ValidationException>(() =>
{
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
});
}
[Fact]
public void ChangePlan_should_throw_exception_if_same_plan()
{
CreateApp();
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
Assert.Throws<ValidationException>(() =>
{
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
});
}
[Fact]
public void ChangePlan_should_create_events()
{
CreateApp();
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppPlanChanged { PlanId = planId })
);
}
[Fact]
public void AssignContributor_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId }));
});
}
[Fact]
public void AssignContributor_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.AssignContributor(CreateCommand(new AssignContributor { Permission = (AppContributorPermission)123 }));
});
}
[Fact]
public void AssignContributor_should_throw_exception_if_single_owner_becomes_non_owner()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = User.Identifier, Permission = AppContributorPermission.Editor }));
});
}
[Fact]
public void AssignContributor_should_throw_exception_if_user_already_contributor()
{
CreateApp();
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
Assert.Throws<ValidationException>(() =>
{
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
});
}
[Fact]
public void AssignContributor_should_create_events()
{
CreateApp();
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor })
);
}
[Fact]
public void RemoveContributor_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId }));
});
}
[Fact]
public void RemoveContributor_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.RemoveContributor(CreateCommand(new RemoveContributor()));
});
}
[Fact]
public void RemoveContributor_should_throw_exception_if_all_owners_removed()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = User.Identifier }));
});
}
[Fact]
public void RemoveContributor_should_throw_exception_if_contributor_not_found()
{
CreateApp();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = "not-found" }));
});
}
[Fact]
public void RemoveContributor_should_create_events_and_remove_contributor()
{
CreateApp();
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId }));
sut.GetUncomittedEvents().Skip(1)
.ShouldHaveSameEvents(
CreateEvent(new AppContributorRemoved { ContributorId = contributorId })
);
}
[Fact]
public void AttachClient_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
});
}
[Fact]
public void AttachClient_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient()));
});
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = string.Empty }));
});
}
[Fact]
public void AttachClient_should_throw_exception_if_id_already_exists()
{
CreateApp();
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
Assert.Throws<ValidationException>(() =>
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
});
}
[Fact]
public void AttachClient_should_create_events()
{
var command = new AttachClient { Id = clientId };
CreateApp();
sut.AttachClient(CreateCommand(command));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret })
);
}
[Fact]
public void RevokeClient_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" }));
});
}
[Fact]
public void RevokeClient_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.RevokeClient(CreateCommand(new RevokeClient()));
});
Assert.Throws<ValidationException>(() =>
{
sut.RevokeClient(CreateCommand(new RevokeClient { Id = string.Empty }));
});
}
[Fact]
public void RevokeClient_should_throw_exception_if_client_not_found()
{
CreateApp();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" }));
});
}
[Fact]
public void RevokeClient_should_create_events()
{
CreateApp();
CreateClient();
sut.RevokeClient(CreateCommand(new RevokeClient { Id = clientId }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppClientRevoked { Id = clientId })
);
}
[Fact]
public void UpdateClient_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName }));
});
}
[Fact]
public void UpdateClient_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient()));
});
Assert.Throws<ValidationException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Id = string.Empty }));
});
Assert.Throws<ValidationException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Permission = (AppClientPermission)int.MaxValue }));
});
}
[Fact]
public void UpdateClient_should_throw_exception_if_client_not_found()
{
CreateApp();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName }));
});
}
[Fact]
public void UpdateClient_should_throw_exception_if_client_has_same_reader_state()
{
CreateApp();
CreateClient();
Assert.Throws<ValidationException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Permission = AppClientPermission.Editor }));
});
}
[Fact]
public void UpdateClient_should_throw_exception_if_same_client_name()
{
CreateApp();
CreateClient();
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName }));
Assert.Throws<ValidationException>(() =>
{
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName }));
});
}
[Fact]
public void UpdateClient_should_create_events()
{
CreateApp();
CreateClient();
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }),
CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer })
);
}
[Fact]
public void AddLanguage_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE }));
});
}
[Fact]
public void AddLanguage_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.AddLanguage(CreateCommand(new AddLanguage()));
});
}
[Fact]
public void AddLanguage_should_throw_exception_if_language_already_exists()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.EN }));
});
}
[Fact]
public void AddLanguage_should_create_events()
{
CreateApp();
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppLanguageAdded { Language = Language.DE })
);
}
[Fact]
public void RemoveLanguage_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN }));
});
}
[Fact]
public void RemoveLanguage_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.RemoveLanguage(CreateCommand(new RemoveLanguage()));
});
}
[Fact]
public void RemoveLanguage_should_throw_exception_if_language_not_found()
{
CreateApp();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE }));
});
}
[Fact]
public void RemoveLanguage_should_throw_exception_if_master_language()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN }));
});
}
[Fact]
public void RemoveLanguage_should_create_events()
{
CreateApp();
CreateLanguage(Language.DE);
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppLanguageRemoved { Language = Language.DE })
);
}
[Fact]
public void UpdateLanguage_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN }));
});
}
[Fact]
public void UpdateLanguage_should_throw_exception_if_command_is_not_valid()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.UpdateLanguage(CreateCommand(new UpdateLanguage()));
});
}
[Fact]
public void UpdateLanguage_should_throw_exception_if_language_not_found()
{
CreateApp();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE }));
});
}
[Fact]
public void UpdateLanguage_should_throw_exception_if_master_language()
{
CreateApp();
Assert.Throws<ValidationException>(() =>
{
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN, IsOptional = true }));
});
}
[Fact]
public void UpdateLanguage_should_create_events()
{
CreateApp();
CreateLanguage(Language.DE);
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new AppLanguageUpdated { Language = Language.DE, Fallback = new List<Language> { Language.EN } })
);
}
private void CreateApp()
{
sut.Create(CreateCommand(new CreateApp { Name = AppName }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void CreateClient()
{
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void CreateLanguage(Language language)
{
sut.AddLanguage(CreateCommand(new AddLanguage { Language = language }));
((IAggregate)sut).ClearUncommittedEvents();
}
}
}

51
tests/Squidex.Domain.Apps.Write.Tests/Apps/AppEventTests.cs

@ -1,51 +0,0 @@
// ==========================================================================
// AppEventTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Old;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Xunit;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppEventTests
{
private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString());
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app");
[Fact]
public void Should_migrate_client_changed_as_reader_to_client_updated()
{
var source = CreateEvent(new AppClientChanged { IsReader = true });
source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Reader }));
}
[Fact]
public void Should_migrate_client_changed_as_writer_to_client_updated()
{
var source = CreateEvent(new AppClientChanged { IsReader = false });
source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Editor }));
}
private T CreateEvent<T>(T contentEvent) where T : AppEvent
{
contentEvent.Actor = actor;
contentEvent.AppId = appId;
return contentEvent;
}
}
}

139
tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandMiddlewareTests.cs

@ -1,139 +0,0 @@
// ==========================================================================
// AssetCommandMiddlewareTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Write.Assets.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Tasks;
using Xunit;
namespace Squidex.Domain.Apps.Write.Assets
{
public class AssetCommandMiddlewareTests : HandlerTestBase<AssetDomainObject>
{
private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake<IAssetThumbnailGenerator>();
private readonly IAssetStore assetStore = A.Fake<IAssetStore>();
private readonly AssetCommandMiddleware sut;
private readonly AssetDomainObject asset;
private readonly Guid assetId = Guid.NewGuid();
private readonly Stream stream = new MemoryStream();
private readonly ImageInfo image = new ImageInfo(2048, 2048);
private readonly AssetFile file;
public AssetCommandMiddlewareTests()
{
file = new AssetFile("my-image.png", "image/png", 1024, () => stream);
asset = new AssetDomainObject(assetId, -1);
sut = new AssetCommandMiddleware(Handler, assetStore, assetThumbnailGenerator);
}
[Fact]
public async Task Create_should_create_asset()
{
var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, File = file });
SetupStore(0, context.ContextId);
SetupImageInfo();
await TestCreate(asset, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(assetId, context.Result<EntityCreatedResult<Guid>>().IdOrValue);
VerifyStore(0, context.ContextId);
VerifyImageInfo();
}
[Fact]
public async Task Update_should_update_domain_object()
{
var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file });
SetupStore(1, context.ContextId);
SetupImageInfo();
CreateAsset();
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(context);
});
VerifyStore(1, context.ContextId);
VerifyImageInfo();
}
[Fact]
public async Task Rename_should_update_domain_object()
{
CreateAsset();
var context = CreateContextForCommand(new RenameAsset { AssetId = assetId, FileName = "my-new-image.png" });
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(context);
});
}
[Fact]
public async Task Delete_should_update_domain_object()
{
CreateAsset();
var command = CreateContextForCommand(new DeleteAsset { AssetId = assetId });
await TestUpdate(asset, async _ =>
{
await sut.HandleAsync(command);
});
}
private void CreateAsset()
{
asset.Create(new CreateAsset { File = file });
}
private void SetupImageInfo()
{
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream))
.Returns(image);
}
private void SetupStore(long version, Guid commitId)
{
A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null))
.Returns(TaskHelper.Done);
A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString()))
.Returns(TaskHelper.Done);
}
private void VerifyImageInfo()
{
A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)).MustHaveHappened();
}
private void VerifyStore(long version, Guid commitId)
{
A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream)).MustHaveHappened();
A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null)).MustHaveHappened();
A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString())).MustHaveHappened();
}
}
}

235
tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetDomainObjectTests.cs

@ -1,235 +0,0 @@
// ==========================================================================
// AssetDomainObjectTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.IO;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Write.Assets.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.CQRS;
using Xunit;
namespace Squidex.Domain.Apps.Write.Assets
{
public class AssetDomainObjectTests : HandlerTestBase<AssetDomainObject>
{
private readonly AssetDomainObject sut;
private readonly ImageInfo image = new ImageInfo(2048, 2048);
private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream());
public Guid AssetId { get; } = Guid.NewGuid();
public AssetDomainObjectTests()
{
sut = new AssetDomainObject(AssetId, 0);
}
[Fact]
public void Create_should_throw_exception_if_created()
{
sut.Create(new CreateAsset { File = file });
Assert.Throws<DomainException>(() =>
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file }));
});
}
[Fact]
public void Create_should_create_events()
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetCreated
{
IsImage = true,
FileName = file.FileName,
FileSize = file.FileSize,
FileVersion = 0,
MimeType = file.MimeType,
PixelWidth = image.PixelWidth,
PixelHeight = image.PixelHeight
})
);
}
[Fact]
public void Update_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset { File = file }));
});
}
[Fact]
public void Update_should_throw_exception_if_asset_is_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset()));
});
}
[Fact]
public void Update_should_create_events()
{
CreateAsset();
sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetUpdated
{
IsImage = true,
FileSize = file.FileSize,
FileVersion = 1,
MimeType = file.MimeType,
PixelWidth = image.PixelWidth,
PixelHeight = image.PixelHeight
})
);
}
[Fact]
public void Rename_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "new-file.png" }));
});
}
[Fact]
public void Rename_should_throw_exception_if_asset_is_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateAssetCommand(new UpdateAsset()));
});
}
[Fact]
public void Rename_should_throw_exception_if_command_is_not_valid()
{
CreateAsset();
Assert.Throws<ValidationException>(() =>
{
sut.Rename(CreateAssetCommand(new RenameAsset()));
});
}
[Fact]
public void Rename_should_throw_exception_if_new_name_is_equal_to_old_name()
{
CreateAsset();
Assert.Throws<ValidationException>(() =>
{
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = file.FileName }));
});
}
[Fact]
public void Rename_should_create_events()
{
CreateAsset();
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "my-new-image.png" }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetRenamed { FileName = "my-new-image.png" })
);
}
[Fact]
public void Delete_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
});
}
[Fact]
public void Delete_should_throw_exception_if_already_deleted()
{
CreateAsset();
DeleteAsset();
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
});
}
[Fact]
public void Delete_should_create_events_with_total_file_size()
{
CreateAsset();
UpdateAsset();
sut.Delete(CreateAssetCommand(new DeleteAsset()));
Assert.True(sut.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 })
);
}
private void CreateAsset()
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void UpdateAsset()
{
sut.Update(CreateAssetCommand(new UpdateAsset { File = file }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void DeleteAsset()
{
sut.Delete(CreateAssetCommand(new DeleteAsset()));
((IAggregate)sut).ClearUncommittedEvents();
}
protected T CreateAssetEvent<T>(T @event) where T : AssetEvent
{
@event.AssetId = AssetId;
return CreateEvent(@event);
}
protected T CreateAssetCommand<T>(T command) where T : AssetAggregateCommand
{
command.AssetId = AssetId;
return CreateCommand(command);
}
}
}

240
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs

@ -1,240 +0,0 @@
// ==========================================================================
// ContentCommandMiddlewareTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Xunit;
namespace Squidex.Domain.Apps.Write.Contents
{
public class ContentCommandMiddlewareTests : HandlerTestBase<ContentDomainObject>
{
private readonly ContentCommandMiddleware sut;
private readonly ContentDomainObject content;
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>();
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>();
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly IAppEntity app = A.Fake<IAppEntity>();
private readonly ClaimsPrincipal user = new ClaimsPrincipal();
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Create(Language.DE);
private readonly Guid contentId = Guid.NewGuid();
private readonly NamedContentData invalidData =
new NamedContentData()
.AddField("my-field", new ContentFieldData()
.SetValue(null));
private readonly NamedContentData data =
new NamedContentData()
.AddField("my-field", new ContentFieldData()
.SetValue(1));
public ContentCommandMiddlewareTests()
{
var schemaDef =
Schema.Create("my-schema", new SchemaProperties())
.AddField(new NumberField(1, "my-field", Partitioning.Invariant,
new NumberFieldProperties { IsRequired = true }));
content = new ContentDomainObject(contentId, -1);
sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy<IAssetRepository>(), schemas, scriptEngine, A.Dummy<IContentRepository>());
A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig);
A.CallTo(() => app.PartitionResolver).Returns(languagesConfig.ToResolver());
A.CallTo(() => appProvider.FindAppByIdAsync(AppId)).Returns(app);
A.CallTo(() => schema.SchemaDef).Returns(schemaDef);
A.CallTo(() => schema.ScriptCreate).Returns("<create-script>");
A.CallTo(() => schema.ScriptChange).Returns("<change-script>");
A.CallTo(() => schema.ScriptUpdate).Returns("<update-script>");
A.CallTo(() => schema.ScriptDelete).Returns("<delete-script>");
A.CallTo(() => schemas.FindSchemaByIdAsync(SchemaId, false)).Returns(schema);
}
[Fact]
public async Task Create_should_throw_exception_if_data_is_not_valid()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(invalidData);
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = invalidData, User = user });
await TestCreate(content, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task Create_should_create_content()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(data);
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user });
await TestCreate(content, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue);
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened();
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustNotHaveHappened();
}
[Fact]
public async Task Create_should_also_invoke_publish_script_when_publishing()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(data);
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user, Publish = true });
await TestCreate(content, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue);
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened();
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened();
}
[Fact]
public async Task Update_should_throw_exception_if_data_is_not_valid()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)).Returns(invalidData);
CreateContent();
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = invalidData, User = user });
await TestUpdate(content, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task Update_should_update_domain_object()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(data);
CreateContent();
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = data, User = user });
await TestUpdate(content, async _ =>
{
await sut.HandleAsync(context);
});
Assert.Equal(data, context.Result<ContentDataChangedResult>().Data);
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened();
}
[Fact]
public async Task Patch_should_throw_exception_if_data_is_not_valid()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(invalidData);
CreateContent();
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = invalidData, User = user });
await TestUpdate(content, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context));
}, false);
}
[Fact]
public async Task Patch_should_update_domain_object()
{
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored))
.Returns(data);
var patch = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(3));
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)).Returns(patch);
CreateContent();
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = patch, User = user });
await TestUpdate(content, async _ =>
{
await sut.HandleAsync(context);
});
Assert.NotNull(context.Result<ContentDataChangedResult>().Data);
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened();
}
[Fact]
public async Task ChangeStatus_should_publish_domain_object()
{
CreateContent();
var context = CreateContextForCommand(new ChangeContentStatus { ContentId = contentId, User = user, Status = Status.Published });
await TestUpdate(content, async _ =>
{
await sut.HandleAsync(context);
});
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened();
}
[Fact]
public async Task Delete_should_update_domain_object()
{
CreateContent();
var command = CreateContextForCommand(new DeleteContent { ContentId = contentId, User = user });
await TestUpdate(content, async _ =>
{
await sut.HandleAsync(command);
});
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<delete-script>")).MustHaveHappened();
}
private void CreateContent()
{
content.Create(new CreateContent { Data = data });
}
}
}

323
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentDomainObjectTests.cs

@ -1,323 +0,0 @@
// ==========================================================================
// ContentDomainObjectTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using FluentAssertions;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
using Xunit;
namespace Squidex.Domain.Apps.Write.Contents
{
public class ContentDomainObjectTests : HandlerTestBase<ContentDomainObject>
{
private readonly ContentDomainObject sut;
private readonly NamedContentData data =
new NamedContentData()
.AddField("field1",
new ContentFieldData()
.AddValue("iv", 1));
private readonly NamedContentData otherData =
new NamedContentData()
.AddField("field2",
new ContentFieldData()
.AddValue("iv", 2));
public Guid ContentId { get; } = Guid.NewGuid();
public ContentDomainObjectTests()
{
sut = new ContentDomainObject(ContentId, 0);
}
[Fact]
public void Create_should_throw_exception_if_created()
{
sut.Create(new CreateContent { Data = data });
Assert.Throws<DomainException>(() =>
{
sut.Create(CreateContentCommand(new CreateContent { Data = data }));
});
}
[Fact]
public void Create_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateContentCommand(new CreateContent()));
});
}
[Fact]
public void Create_should_create_events()
{
sut.Create(CreateContentCommand(new CreateContent { Data = data }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentCreated { Data = data })
);
}
[Fact]
public void Create_should_also_publish_if_set_to_true()
{
sut.Create(CreateContentCommand(new CreateContent { Data = data, Publish = true }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentCreated { Data = data }),
CreateContentEvent(new ContentStatusChanged { Status = Status.Published })
);
}
[Fact]
public void Update_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateContentCommand(new UpdateContent { Data = data }));
});
}
[Fact]
public void Update_should_throw_exception_if_content_is_deleted()
{
CreateContent();
DeleteContent();
Assert.Throws<ValidationException>(() =>
{
sut.Update(CreateContentCommand(new UpdateContent()));
});
}
[Fact]
public void Update_should_throw_exception_if_command_is_not_valid()
{
CreateContent();
Assert.Throws<ValidationException>(() =>
{
sut.Update(CreateContentCommand(new UpdateContent()));
});
}
[Fact]
public void Update_should_create_events()
{
CreateContent();
sut.Update(CreateContentCommand(new UpdateContent { Data = otherData }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentUpdated { Data = otherData })
);
}
[Fact]
public void Update_should_not_create_event_for_same_data()
{
CreateContent();
UpdateContent();
sut.Update(CreateContentCommand(new UpdateContent { Data = data }));
sut.GetUncomittedEvents().Should().BeEmpty();
}
[Fact]
public void Patch_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Patch(CreateContentCommand(new PatchContent { Data = data }));
});
}
[Fact]
public void Patch_should_throw_exception_if_content_is_deleted()
{
CreateContent();
DeleteContent();
Assert.Throws<ValidationException>(() =>
{
sut.Patch(CreateContentCommand(new PatchContent()));
});
}
[Fact]
public void Patch_should_throw_exception_if_command_is_not_valid()
{
CreateContent();
Assert.Throws<ValidationException>(() =>
{
sut.Patch(CreateContentCommand(new PatchContent()));
});
}
[Fact]
public void Patch_should_create_events()
{
CreateContent();
sut.Patch(CreateContentCommand(new PatchContent { Data = otherData }));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentUpdated { Data = otherData })
);
}
[Fact]
public void Patch_should_not_create_event_for_same_data()
{
CreateContent();
UpdateContent();
sut.Patch(CreateContentCommand(new PatchContent { Data = data }));
sut.GetUncomittedEvents().Should().BeEmpty();
}
[Fact]
public void ChangeStatus_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus()));
});
}
[Fact]
public void ChangeStatus_should_throw_exception_if_content_is_deleted()
{
CreateContent();
DeleteContent();
Assert.Throws<DomainException>(() =>
{
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus()));
});
}
[Fact]
public void ChangeStatus_should_throw_exception_if_status_flow_not_valid()
{
CreateContent();
ChangeStatus(Status.Archived);
Assert.Throws<DomainException>(() =>
{
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published }));
});
}
[Fact]
public void ChangeStatus_should_refresh_properties_and_create_events()
{
CreateContent();
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published }));
Assert.Equal(Status.Published, sut.Status);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentStatusChanged { Status = Status.Published })
);
}
[Fact]
public void Delete_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateContentCommand(new DeleteContent()));
});
}
[Fact]
public void Delete_should_throw_exception_if_already_deleted()
{
CreateContent();
DeleteContent();
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateContentCommand(new DeleteContent()));
});
}
[Fact]
public void Delete_should_update_properties_create_events()
{
CreateContent();
sut.Delete(CreateContentCommand(new DeleteContent()));
Assert.True(sut.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateContentEvent(new ContentDeleted())
);
}
private void CreateContent()
{
sut.Create(CreateContentCommand(new CreateContent { Data = data }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void UpdateContent()
{
sut.Update(CreateContentCommand(new UpdateContent { Data = data }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void ChangeStatus(Status status)
{
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = status }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void DeleteContent()
{
sut.Delete(CreateContentCommand(new DeleteContent()));
((IAggregate)sut).ClearUncommittedEvents();
}
protected T CreateContentEvent<T>(T @event) where T : ContentEvent
{
@event.ContentId = ContentId;
return CreateEvent(@event);
}
protected T CreateContentCommand<T>(T command) where T : ContentCommand
{
command.ContentId = ContentId;
return CreateCommand(command);
}
}
}

70
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs

@ -1,70 +0,0 @@
// ==========================================================================
// SchemaEventTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Events.Contents.Old;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Infrastructure;
using Xunit;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Squidex.Domain.Apps.Write.Contents
{
public class ContentEventTests
{
private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString());
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app");
private readonly NamedId<Guid> schemaId = new NamedId<Guid>(Guid.NewGuid(), "my-schema");
private readonly Guid contentId = Guid.NewGuid();
[Fact]
public void Should_migrate_content_published_to_content_status_changed()
{
var source = CreateEvent(new ContentPublished());
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Published }));
}
[Fact]
public void Should_migrate_content_unpublished_to_content_status_changed()
{
var source = CreateEvent(new ContentUnpublished());
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft }));
}
[Fact]
public void Should_migrate_content_restored_to_content_status_changed()
{
var source = CreateEvent(new ContentRestored());
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft }));
}
[Fact]
public void Should_migrate_content_archived_to_content_status_changed()
{
var source = CreateEvent(new ContentArchived());
source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Archived }));
}
private T CreateEvent<T>(T contentEvent) where T : ContentEvent
{
contentEvent.Actor = actor;
contentEvent.AppId = appId;
contentEvent.SchemaId = schemaId;
contentEvent.ContentId = contentId;
return contentEvent;
}
}
}

139
tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs

@ -1,139 +0,0 @@
// ==========================================================================
// ContentVersionLoaderTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Xunit;
namespace Squidex.Domain.Apps.Write.Contents
{
public class ContentVersionLoaderTests
{
private readonly IEventStore eventStore = A.Fake<IEventStore>();
private readonly IStreamNameResolver nameResolver = A.Fake<IStreamNameResolver>();
private readonly EventDataFormatter formatter = A.Fake<EventDataFormatter>();
private readonly Guid id = Guid.NewGuid();
private readonly Guid appId = Guid.NewGuid();
private readonly string streamName = Guid.NewGuid().ToString();
private readonly ContentVersionLoader sut;
public ContentVersionLoaderTests()
{
A.CallTo(() => nameResolver.GetStreamName(typeof(ContentDomainObject), id))
.Returns(streamName);
sut = new ContentVersionLoader(eventStore, nameResolver, formatter);
}
[Fact]
public async Task Should_throw_exception_when_event_store_returns_no_events()
{
A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(new List<StoredEvent>());
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, -1));
}
[Fact]
public async Task Should_throw_exception_when_version_not_found()
{
A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(new List<StoredEvent>());
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 3));
}
[Fact]
public async Task Should_throw_exception_when_content_is_from_another_event()
{
var eventData1 = new EventData();
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(Guid.NewGuid(), "my-app") };
var events = new List<StoredEvent>
{
new StoredEvent("0", 0, eventData1)
};
A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events);
A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.LoadAsync(appId, id, 0));
}
[Fact]
public async Task Should_load_content_from_created_event()
{
var eventData1 = new EventData();
var eventData2 = new EventData();
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(appId, "my-app") };
var event2 = new ContentStatusChanged();
var events = new List<StoredEvent>
{
new StoredEvent("0", 0, eventData1),
new StoredEvent("1", 1, eventData2)
};
A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events);
A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2));
var data = await sut.LoadAsync(appId, id, 3);
Assert.Same(event1.Data, data);
}
[Fact]
public async Task Should_load_content_from_correct_version()
{
var eventData1 = new EventData();
var eventData2 = new EventData();
var eventData3 = new EventData();
var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId<Guid>(appId, "my-app") };
var event2 = new ContentUpdated { Data = new NamedContentData() };
var event3 = new ContentUpdated { Data = new NamedContentData() };
var events = new List<StoredEvent>
{
new StoredEvent("0", 0, eventData1),
new StoredEvent("1", 1, eventData2),
new StoredEvent("2", 2, eventData3)
};
A.CallTo(() => eventStore.GetEventsAsync(streamName))
.Returns(events);
A.CallTo(() => formatter.Parse(eventData1, true))
.Returns(new Envelope<IEvent>(event1));
A.CallTo(() => formatter.Parse(eventData2, true))
.Returns(new Envelope<IEvent>(event2));
A.CallTo(() => formatter.Parse(eventData3, true))
.Returns(new Envelope<IEvent>(event3));
var data = await sut.LoadAsync(appId, id, 1);
Assert.Equal(event2.Data, data);
}
}
}

4
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs

@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Write.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<double>(4) };
var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = new[] { 4d } };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Write.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<double>(4) };
var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = new[] { 4d } };
var errors = FieldPropertiesValidator.Validate(sut).ToList();

4
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs

@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Write.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 = new[] { "4" } };
var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Write.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 = new string[] { "4" } };
var errors = FieldPropertiesValidator.Validate(sut).ToList();

234
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs

@ -0,0 +1,234 @@
// ==========================================================================
// GuardSchemaFieldTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Write.Schemas
{
public class GuardSchemaFieldTests
{
private readonly Schema schema = new Schema("my-schema");
public GuardSchemaFieldTests()
{
schema.AddField(new StringField(1, "field1", Partitioning.Invariant));
schema.AddField(new StringField(2, "field2", Partitioning.Invariant));
}
[Fact]
public void CanHide_should_throw_exception_if_already_hidden()
{
var command = new HideField { FieldId = 1 };
schema.FieldsById[1].Hide();
Assert.Throws<DomainException>(() => GuardSchemaField.CanHide(schema, command));
}
[Fact]
public void CanHide_should_throw_exception_if_not_found()
{
var command = new HideField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanHide(schema, command));
}
[Fact]
public void CanHide_hould_not_throw_exception_if_visible()
{
var command = new HideField { FieldId = 1 };
GuardSchemaField.CanHide(schema, command);
}
[Fact]
public void CanDisable_should_throw_exception_if_already_disabled()
{
var command = new DisableField { FieldId = 1 };
schema.FieldsById[1].Disable();
Assert.Throws<DomainException>(() => GuardSchemaField.CanDisable(schema, command));
}
[Fact]
public void CanDisable_should_throw_exception_if_not_found()
{
var command = new DisableField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanDisable(schema, command));
}
[Fact]
public void CanDisable_Should_not_throw_exception_if_enabled()
{
var command = new DisableField { FieldId = 1 };
GuardSchemaField.CanDisable(schema, command);
}
[Fact]
public void CanShow_should_throw_exception_if_already_shown()
{
var command = new ShowField { FieldId = 1 };
Assert.Throws<DomainException>(() => GuardSchemaField.CanShow(schema, command));
}
[Fact]
public void CanShow_should_throw_exception_if_not_found()
{
var command = new ShowField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanShow(schema, command));
}
[Fact]
public void CanShow_should_not_throw_exception_if_hidden()
{
var command = new ShowField { FieldId = 1 };
schema.FieldsById[1].Hide();
GuardSchemaField.CanShow(schema, command);
}
[Fact]
public void CanEnable_should_throw_exception_if_already_enabled()
{
var command = new EnableField { FieldId = 1 };
Assert.Throws<DomainException>(() => GuardSchemaField.CanEnable(schema, command));
}
[Fact]
public void CanEnable_should_throw_exception_if_not_found()
{
var command = new EnableField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanEnable(schema, command));
}
[Fact]
public void CanEnable_should_not_throw_exception_if_disabled()
{
var command = new EnableField { FieldId = 1 };
schema.FieldsById[1].Disable();
GuardSchemaField.CanEnable(schema, command);
}
[Fact]
public void CanLock_should_throw_exception_if_already_locked()
{
var command = new LockField { FieldId = 1 };
schema.FieldsById[1].Lock();
Assert.Throws<DomainException>(() => GuardSchemaField.CanLock(schema, command));
}
[Fact]
public void LockField_should_throw_exception_if_not_found()
{
var command = new LockField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanLock(schema, command));
}
[Fact]
public void CanLock_should_not_throw_exception_if_not_locked()
{
var command = new LockField { FieldId = 1 };
GuardSchemaField.CanLock(schema, command);
}
[Fact]
public void CanDelete_should_throw_exception_if_not_found()
{
var command = new DeleteField { FieldId = 3 };
Assert.Throws<DomainObjectNotFoundException>(() => GuardSchemaField.CanDelete(schema, command));
}
[Fact]
public void CanDelete_should_throw_exception_if_locked()
{
var command = new DeleteField { FieldId = 1 };
schema.FieldsById[1].Lock();
Assert.Throws<DomainException>(() => GuardSchemaField.CanDelete(schema, command));
}
[Fact]
public void CanDelete_should_not_throw_exception_if_not_locked()
{
var command = new DeleteField { FieldId = 1 };
GuardSchemaField.CanDelete(schema, command);
}
[Fact]
public void CanUpdate_should_throw_exception_if_locked()
{
var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() };
schema.FieldsById[1].Lock();
Assert.Throws<DomainException>(() => GuardSchemaField.CanUpdate(schema, command));
}
[Fact]
public void CanUpdate_should_not_throw_exception_if_not_locked()
{
var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() };
GuardSchemaField.CanUpdate(schema, command);
}
[Fact]
public void CanAdd_should_throw_exception_if_field_already_exists()
{
var command = new AddField { Name = "field1", Properties = new StringFieldProperties() };
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema, command));
}
[Fact]
public void CanAdd_should_throw_exception_if_name_not_valid()
{
var command = new AddField { Name = "INVALID_NAME", Properties = new StringFieldProperties() };
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema, command));
}
[Fact]
public void CanAdd_should_throw_exception_if_properties_not_valid()
{
var command = new AddField { Name = "field3", Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } };
Assert.Throws<ValidationException>(() => GuardSchemaField.CanAdd(schema, command));
}
[Fact]
public void CanAdd_should_not_throw_exception_if_field_not_exists()
{
var command = new AddField { Name = "field3", Properties = new StringFieldProperties() };
GuardSchemaField.CanAdd(schema, command);
}
}
}

197
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaTests.cs

@ -0,0 +1,197 @@
// ==========================================================================
// GuardSchemaTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public class GuardSchemaTests
{
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>();
private readonly Schema schema = new Schema("my-schema");
private readonly NamedId<Guid> appId = new NamedId<Guid>(Guid.NewGuid(), "my-app");
public GuardSchemaTests()
{
schema.AddField(new StringField(1, "field1", Partitioning.Invariant));
schema.AddField(new StringField(2, "field2", Partitioning.Invariant));
A.CallTo(() => schemas.FindSchemaByNameAsync(A<Guid>.Ignored, "new-schema"))
.Returns(Task.FromResult<ISchemaEntity>(null));
}
[Fact]
public async Task CanCreate_should_throw_exception_if_name_not_valid()
{
var command = new CreateSchema { AppId = appId, Name = "INVALID NAME" };
await Assert.ThrowsAsync<ValidationException>(() => GuardSchema.CanCreate(command, schemas));
}
[Fact]
public async Task CanCreate_should_throw_exception_if_name_already_in_use()
{
A.CallTo(() => schemas.FindSchemaByNameAsync(A<Guid>.Ignored, "new-schema"))
.Returns(Task.FromResult(A.Fake<ISchemaEntity>()));
var command = new CreateSchema { AppId = appId, Name = "new-schema" };
await Assert.ThrowsAsync<ValidationException>(() => GuardSchema.CanCreate(command, schemas));
}
[Fact]
public async Task CanCreate_should_throw_exception_if_fields_not_valid()
{
var command = new CreateSchema
{
AppId = appId,
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = null,
Properties = null,
Partitioning = "invalid",
},
new CreateSchemaField
{
Name = null,
Properties = InvalidProperties(),
Partitioning = "invalid",
}
},
Name = "new-schema"
};
await Assert.ThrowsAsync<ValidationException>(() => GuardSchema.CanCreate(command, schemas));
}
[Fact]
public async Task CanCreate_should_throw_exception_if_fields_contain_duplicate_names()
{
var command = new CreateSchema
{
AppId = appId,
Fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "field1",
Properties = ValidProperties(),
Partitioning = "invariant"
},
new CreateSchemaField
{
Name = "field1",
Properties = ValidProperties(),
Partitioning = "invariant"
}
},
Name = "new-schema"
};
await Assert.ThrowsAsync<ValidationException>(() => GuardSchema.CanCreate(command, schemas));
}
[Fact]
public async Task CanCreate_should_not_throw_exception_if_command_is_valid()
{
var command = new CreateSchema { AppId = appId, Name = "new-schema" };
await GuardSchema.CanCreate(command, schemas);
}
[Fact]
public void CanPublish_should_throw_exception_if_already_published()
{
var command = new PublishSchema();
schema.Publish();
Assert.Throws<DomainException>(() => GuardSchema.CanPublish(schema, command));
}
[Fact]
public void CanPublish_should_not_throw_exception_if_not_published()
{
var command = new PublishSchema();
GuardSchema.CanPublish(schema, command);
}
[Fact]
public void CanUnpublish_should_throw_exception_if_already_unpublished()
{
var command = new UnpublishSchema();
Assert.Throws<DomainException>(() => GuardSchema.CanUnpublish(schema, command));
}
[Fact]
public void CanUnpublish_should_not_throw_exception_if_already_published()
{
var command = new UnpublishSchema();
schema.Publish();
GuardSchema.CanUnpublish(schema, command);
}
[Fact]
public void CanReorder_should_throw_exception_if_field_ids_contains_invalid_id()
{
var command = new ReorderFields { FieldIds = new List<long> { 1, 3 } };
Assert.Throws<ValidationException>(() => GuardSchema.CanReorder(schema, command));
}
[Fact]
public void CanReorder_should_throw_exception_if_field_ids_do_not_covers_all_fields()
{
var command = new ReorderFields { FieldIds = new List<long> { 1 } };
Assert.Throws<ValidationException>(() => GuardSchema.CanReorder(schema, command));
}
[Fact]
public void CanReorder_should_not_throw_exception_if_field_ids_are_valid()
{
var command = new ReorderFields { FieldIds = new List<long> { 1, 2 } };
GuardSchema.CanReorder(schema, command);
}
[Fact]
public void CanDelete_should_not_throw_exception()
{
var command = new DeleteSchema();
GuardSchema.CanDelete(schema, command);
}
private static StringFieldProperties ValidProperties()
{
return new StringFieldProperties { MinLength = 10, MaxLength = 20 };
}
private static StringFieldProperties InvalidProperties()
{
return new StringFieldProperties { MinLength = 20, MaxLength = 10 };
}
}
}

169
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaFieldGuardTests.cs

@ -1,169 +0,0 @@
// ==========================================================================
// SchemaFieldGuardTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public class SchemaFieldGuardTests
{
private Schema schema =
Schema.Create("my-schema", new SchemaProperties())
.AddField(new StringField(1, "field1", Partitioning.Invariant))
.AddField(new StringField(2, "field2", Partitioning.Invariant));
[Fact]
public void Should_throw_exception_if_field_to_hide_already_hidden()
{
schema = schema.HideField(1);
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanHide(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_hide_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanHide(schema, 3));
}
[Fact]
public void Should_not_throw_exception_if_field_to_hide_shown()
{
SchemaFieldGuard.GuardCanHide(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_disable_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanDisable(schema, 3));
}
[Fact]
public void Should_throw_exception_if_field_to_disable_already_disabled()
{
schema = schema.DisableField(1);
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanDisable(schema, 1));
}
[Fact]
public void Should_not_throw_exception_if_field_to_disable_shown()
{
SchemaFieldGuard.GuardCanDisable(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_show_already_shown()
{
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanShow(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_show_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanShow(schema, 3));
}
[Fact]
public void Should_not_throw_exception_if_field_to_show_hidden()
{
schema = schema.HideField(1);
SchemaFieldGuard.GuardCanShow(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_enable_already_enabled()
{
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanEnable(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_enable_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanEnable(schema, 3));
}
[Fact]
public void Should_not_throw_exception_if_field_to_enable_disabled()
{
schema = schema.DisableField(1);
SchemaFieldGuard.GuardCanEnable(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_lock_already_locked()
{
schema = schema.LockField(1);
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanLock(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_lock_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanLock(schema, 3));
}
[Fact]
public void Should_not_throw_exception_if_field_to_lock_not_locked()
{
SchemaFieldGuard.GuardCanLock(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_delete_not_found()
{
Assert.Throws<DomainObjectNotFoundException>(() => SchemaFieldGuard.GuardCanDelete(schema, 3));
}
[Fact]
public void Should_throw_exception_if_field_to_delete_is_locked()
{
schema = schema.LockField(1);
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanDelete(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_update_not_locked()
{
SchemaFieldGuard.GuardCanUpdate(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_update_is_locked()
{
schema = schema.LockField(1);
Assert.Throws<DomainException>(() => SchemaFieldGuard.GuardCanUpdate(schema, 1));
}
[Fact]
public void Should_throw_exception_if_field_to_delete_not_locked()
{
SchemaFieldGuard.GuardCanDelete(schema, 1);
}
[Fact]
public void Should_throw_exception_if_field_to_add_already_exists()
{
Assert.Throws<ValidationException>(() => SchemaFieldGuard.GuardCanAdd(schema, "field1"));
}
[Fact]
public void Should_not_throw_exception_if_field_to_add_not_exists()
{
SchemaFieldGuard.GuardCanAdd(schema, "field3");
}
}
}

65
tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaGuardTests.cs

@ -1,65 +0,0 @@
// ==========================================================================
// SchemaGuardTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Xunit;
namespace Squidex.Domain.Apps.Write.Schemas.Guards
{
public class SchemaGuardTests
{
private Schema schema =
Schema.Create("my-schema", new SchemaProperties())
.AddField(new StringField(1, "field1", Partitioning.Invariant))
.AddField(new StringField(2, "field2", Partitioning.Invariant));
[Fact]
public void Should_throw_exception_if_schema_to_publish_already_published()
{
schema = schema.Publish();
Assert.Throws<DomainException>(() => SchemaGuard.GuardCanPublish(schema));
}
[Fact]
public void Should_not_throw_exception_if_schema_to_publish_not_published()
{
SchemaGuard.GuardCanPublish(schema);
}
[Fact]
public void Should_throw_exception_if_schema_to_unpublish_already_unpublished()
{
Assert.Throws<DomainException>(() => SchemaGuard.GuardCanUnpublish(schema));
}
[Fact]
public void Should_not_throw_exception_if_schema_to_unpublish_published()
{
schema = schema.Publish();
SchemaGuard.GuardCanUnpublish(schema);
}
[Fact]
public void Should_throw_excepotion_if_schema_fields_to_reorder_not_valid()
{
Assert.Throws<ValidationException>(() => SchemaGuard.GuardCanReorder(schema, new List<long> { 1 }));
Assert.Throws<ValidationException>(() => SchemaGuard.GuardCanReorder(schema, new List<long> { 1, 3 }));
}
[Fact]
public void Should_not_throw_excepotion_if_schema_fields_to_reorder_are_valid()
{
SchemaGuard.GuardCanReorder(schema, new List<long> { 1, 2 });
}
}
}

22
tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs

@ -34,32 +34,16 @@ namespace Squidex.Domain.Apps.Write.Schemas
schema = new SchemaDomainObject(SchemaId, -1, registry);
sut = new SchemaCommandMiddleware(Handler, schemas);
}
[Fact]
public async Task Create_should_throw_exception_if_a_name_with_same_name_already_exists()
{
var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId });
A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName))
.Returns(A.Dummy<ISchemaEntity>());
await TestCreate(schema, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(async () => await sut.HandleAsync(context));
}, false);
A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened();
.Returns((ISchemaEntity)null);
}
[Fact]
public async Task Create_should_create_schema_if_name_is_free()
public async Task Create_should_create_schema_domain_object()
{
var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId });
A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName))
.Returns((ISchemaEntity)null);
await TestCreate(schema, async _ =>
{
await sut.HandleAsync(context);
@ -291,4 +275,4 @@ namespace Squidex.Domain.Apps.Write.Schemas
schema.DisableField(CreateCommand(new DisableField { FieldId = 1 }));
}
}
}
}

189
tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs

@ -44,69 +44,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void Create_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateCommand(new CreateSchema()));
});
}
[Fact]
public void Create_should_throw_exception_if_fields_are_not_valid()
{
var properties = new SchemaProperties();
var fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = null,
Properties = null,
Partitioning = "invalid",
},
new CreateSchemaField
{
Name = null,
Properties = InvalidProperties(),
Partitioning = "invalid",
}
};
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, Properties = properties, Fields = fields }));
});
}
[Fact]
public void Create_should_throw_exception_if_fields_contain_duplicate_names()
{
var properties = new SchemaProperties();
var fields = new List<CreateSchemaField>
{
new CreateSchemaField
{
Name = "field1",
Properties = ValidProperties(),
Partitioning = "invariant"
},
new CreateSchemaField
{
Name = "field1",
Properties = ValidProperties(),
Partitioning = "invariant"
}
};
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, Properties = properties, Fields = fields }));
});
}
[Fact]
public void Create_should_create_schema_and_create_events()
{
@ -118,7 +55,7 @@ namespace Squidex.Domain.Apps.Write.Schemas
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties, Fields = new List<SchemaCreatedField>() })
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties })
);
}
@ -162,17 +99,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void Update_should_throw_exception_if_command_is_not_valid()
{
CreateSchema();
Assert.Throws<ValidationException>(() =>
{
sut.Update(CreateCommand(new UpdateSchema()));
});
}
[Fact]
public void Update_should_refresh_properties_and_create_events()
{
@ -259,17 +185,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void Reorder_should_throw_exception_if_command_is_not_valid()
{
CreateSchema();
Assert.Throws<ValidationException>(() =>
{
sut.Reorder(CreateCommand(new ReorderFields()));
});
}
[Fact]
public void Reorder_should_refresh_properties_and_create_events()
{
@ -408,33 +323,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void AddField_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.Add(CreateCommand(new AddField()));
});
}
[Fact]
public void AddField_should_throw_exception_if_command_contains_invalid_partitioning()
{
Assert.Throws<ValidationException>(() =>
{
sut.Add(CreateCommand(new AddField { Name = fieldName, Partitioning = "invalid", Properties = ValidProperties() }));
});
}
[Fact]
public void AddField_should_throw_exception_if_command_contains_invalid_properties()
{
Assert.Throws<ValidationException>(() =>
{
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = InvalidProperties() }));
});
}
[Fact]
public void AddField_should_throw_exception_if_schema_is_deleted()
{
@ -473,26 +361,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void UpdateField_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.UpdateField(CreateCommand(new UpdateField()));
});
}
[Fact]
public void UpdateField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = new NumberFieldProperties() }));
});
}
[Fact]
public void UpdateField_should_throw_exception_if_schema_is_deleted()
{
@ -532,17 +400,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void LockField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.LockField(CreateCommand(new LockField { FieldId = 2 }));
});
}
[Fact]
public void LockField_should_throw_exception_if_schema_is_deleted()
{
@ -580,17 +437,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void HideField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.HideField(CreateCommand(new HideField { FieldId = 2 }));
});
}
[Fact]
public void HideField_should_throw_exception_if_schema_is_deleted()
{
@ -628,17 +474,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void ShowField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.ShowField(CreateCommand(new ShowField { FieldId = 2 }));
});
}
[Fact]
public void ShowField_should_throw_exception_if_schema_is_deleted()
{
@ -677,17 +512,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void DisableField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.DisableField(CreateCommand(new DisableField { FieldId = 2 }));
});
}
[Fact]
public void DisableField_should_throw_exception_if_schema_is_deleted()
{
@ -725,17 +549,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
});
}
[Fact]
public void EnableField_should_throw_exception_if_field_is_not_found()
{
CreateSchema();
Assert.Throws<DomainObjectNotFoundException>(() =>
{
sut.EnableField(CreateCommand(new EnableField { FieldId = 2 }));
});
}
[Fact]
public void EnableField_should_throw_exception_if_schema_is_deleted()
{

5
tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj

@ -5,9 +5,10 @@
<RootNamespace>Squidex.Domain.Apps.Write</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Write\Squidex.Domain.Apps.Write.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" Version="4.1.0" />

130
tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs

@ -1,130 +0,0 @@
// ==========================================================================
// WebhookCommandMiddlewareTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using FakeItEasy;
using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands;
using Xunit;
namespace Squidex.Domain.Apps.Write.Webhooks
{
public class WebhookCommandMiddlewareTests : HandlerTestBase<WebhookDomainObject>
{
private readonly ISchemaProvider schemas = A.Fake<ISchemaProvider>();
private readonly WebhookCommandMiddleware sut;
private readonly WebhookDomainObject webhook;
private readonly Uri url = new Uri("http://squidex.io");
private readonly Guid schemaId = Guid.NewGuid();
private readonly Guid webhookId = Guid.NewGuid();
private readonly List<WebhookSchema> webhookSchemas;
public WebhookCommandMiddlewareTests()
{
webhook = new WebhookDomainObject(webhookId, -1);
webhookSchemas = new List<WebhookSchema>
{
new WebhookSchema { SchemaId = schemaId }
};
sut = new WebhookCommandMiddleware(Handler, schemas);
}
[Fact]
public async Task Create_should_create_webhook()
{
var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId });
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(A.Fake<ISchemaEntity>());
await TestCreate(webhook, async _ =>
{
await sut.HandleAsync(context);
});
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened();
}
[Fact]
public async Task Create_should_throw_exception_when_schema_is_not_found()
{
var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId });
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns((ISchemaEntity)null);
await Assert.ThrowsAsync<ValidationException>(async () =>
{
await TestCreate(webhook, async _ =>
{
await sut.HandleAsync(context);
});
});
}
[Fact]
public async Task Update_should_update_domain_object()
{
var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId });
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(A.Fake<ISchemaEntity>());
CreateWebhook();
await TestUpdate(webhook, async _ =>
{
await sut.HandleAsync(context);
});
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened();
}
[Fact]
public async Task Update_should_throw_exception_when_schema_is_not_found()
{
var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId });
A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns((ISchemaEntity)null);
CreateWebhook();
await Assert.ThrowsAsync<ValidationException>(async () =>
{
await TestCreate(webhook, async _ =>
{
await sut.HandleAsync(context);
});
});
}
[Fact]
public async Task Delete_should_update_domain_object()
{
CreateWebhook();
var command = CreateContextForCommand(new DeleteWebhook { WebhookId = webhookId });
await TestUpdate(webhook, async _ =>
{
await sut.HandleAsync(command);
});
}
private void CreateWebhook()
{
webhook.Create(new CreateWebhook { Url = url });
}
}
}

179
tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookDomainObjectTests.cs

@ -1,179 +0,0 @@
// ==========================================================================
// WebhookDomainObjectTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events.Webhooks;
using Squidex.Domain.Apps.Write.TestHelpers;
using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS;
using Xunit;
namespace Squidex.Domain.Apps.Write.Webhooks
{
public class WebhookDomainObjectTests : HandlerTestBase<WebhookDomainObject>
{
private readonly Uri url = new Uri("http://squidex.io");
private readonly WebhookDomainObject sut;
public Guid WebhookId { get; } = Guid.NewGuid();
public WebhookDomainObjectTests()
{
sut = new WebhookDomainObject(WebhookId, 0);
}
[Fact]
public void Create_should_throw_exception_if_created()
{
sut.Create(new CreateWebhook { Url = url });
Assert.Throws<DomainException>(() =>
{
sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url }));
});
}
[Fact]
public void Create_should_throw_exception_if_command_is_not_valid()
{
Assert.Throws<ValidationException>(() =>
{
sut.Create(CreateWebhookCommand(new CreateWebhook { Url = new Uri("/invalid", UriKind.Relative) }));
});
}
[Fact]
public void Create_should_create_events()
{
var command = new CreateWebhook { Url = url };
sut.Create(CreateWebhookCommand(command));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateWebhookEvent(new WebhookCreated
{
Url = url,
Schemas = command.Schemas,
SharedSecret = command.SharedSecret,
WebhookId = command.WebhookId
})
);
}
[Fact]
public void Update_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url }));
});
}
[Fact]
public void Update_should_throw_exception_if_webhook_is_deleted()
{
CreateWebhook();
DeleteWebhook();
Assert.Throws<DomainException>(() =>
{
sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url }));
});
}
[Fact]
public void Update_should_throw_exception_if_command_is_not_valid()
{
CreateWebhook();
Assert.Throws<ValidationException>(() =>
{
sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = new Uri("/invalid", UriKind.Relative) }));
});
}
[Fact]
public void Update_should_create_events()
{
CreateWebhook();
var command = new UpdateWebhook { Url = url };
sut.Update(CreateWebhookCommand(command));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateWebhookEvent(new WebhookUpdated { Url = url, Schemas = command.Schemas })
);
}
[Fact]
public void Delete_should_throw_exception_if_not_created()
{
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateWebhookCommand(new DeleteWebhook()));
});
}
[Fact]
public void Delete_should_throw_exception_if_already_deleted()
{
CreateWebhook();
DeleteWebhook();
Assert.Throws<DomainException>(() =>
{
sut.Delete(CreateWebhookCommand(new DeleteWebhook()));
});
}
[Fact]
public void Delete_should_update_properties_create_events()
{
CreateWebhook();
sut.Delete(CreateWebhookCommand(new DeleteWebhook()));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
CreateWebhookEvent(new WebhookDeleted())
);
}
private void CreateWebhook()
{
sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url }));
((IAggregate)sut).ClearUncommittedEvents();
}
private void DeleteWebhook()
{
sut.Delete(CreateWebhookCommand(new DeleteWebhook()));
((IAggregate)sut).ClearUncommittedEvents();
}
protected T CreateWebhookEvent<T>(T @event) where T : WebhookEvent
{
@event.WebhookId = WebhookId;
return CreateEvent(@event);
}
protected T CreateWebhookCommand<T>(T command) where T : WebhookAggregateCommand
{
command.WebhookId = WebhookId;
return CreateCommand(command);
}
}
}
Loading…
Cancel
Save