Browse Source

Some progress with refactoring.

pull/152/head
Sebastian Stehle 9 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 EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "domain", "domain", "{4C6B06C2-6D77-4E0E-AE32-D7050236433A}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "domain", "domain", "{4C6B06C2-6D77-4E0E-AE32-D7050236433A}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure", "src\Squidex.Infrastructure\Squidex.Infrastructure.csproj", "{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}"
EndProject 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}" 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 stylecop.json = stylecop.json
EndProjectSection EndProjectSection
EndProject 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution 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|Any CPU.Build.0 = Release|Any CPU
{61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|x64.ActiveCfg = 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 {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.ActiveCfg = Debug|Any CPU
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.Build.0 = 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 {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -332,7 +322,6 @@ Global
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0} = {C9809D59-6665-471E-AD87-5AC624C65892}
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892} {25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A85201C6-6AF8-4B63-8365-08F741050438} = {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 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) if (@event.Fields != null)
{ {
@ -33,20 +38,20 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
if (eventField.IsHidden) if (eventField.IsHidden)
{ {
field = field.Hide(); field.Hide();
} }
if (eventField.IsDisabled) if (eventField.IsDisabled)
{ {
field = field.Disable(); field.Disable();
} }
if (eventField.IsLocked) if (eventField.IsLocked)
{ {
field = field.Lock(); field.Lock();
} }
schema = schema.AddField(field); schema.AddField(field);
fieldId++; fieldId++;
} }
@ -55,7 +60,7 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
return schema; 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 = var partitioning =
string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? 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 fieldId = @event.FieldId.Id;
var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties); var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties);
if (schema.FieldsById.ContainsKey(fieldId)) schema.DeleteField(fieldId);
{ schema.AddField(field);
return schema.UpdateField(fieldId, f => field);
}
else
{
return 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> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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" /> <ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

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

@ -6,11 +6,9 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
@ -19,10 +17,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{ {
public sealed class MongoAppEntity : MongoEntity, IAppEntity public sealed class MongoAppEntity : MongoEntity, IAppEntity
{ {
private readonly IReadOnlyDictionary<string, IAppClientEntity> clientWrapper;
private readonly IReadOnlyDictionary<string, IAppContributorEntity> contributorWrapper;
private LanguagesConfig languagesConfig;
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public string Name { get; set; } public string Name { get; set; }
@ -39,93 +33,28 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
[BsonElement] [BsonElement]
public string PlanOwner { get; set; } public string PlanOwner { get; set; }
[BsonRequired] [BsonIgnoreIfDefault]
[BsonElement]
public string MasterLanguage { get; set; }
[BsonRequired]
[BsonElement] [BsonElement]
public List<string> ContributorIds { get; set; } public string[] ContributorIds { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public List<MongoAppEntityLanguage> Languages { get; set; } [BsonSerializer(typeof(JsonBsonSerializer))]
public AppClients Clients { get; set; } = new AppClients();
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public Dictionary<string, MongoAppEntityClient> Clients { get; set; } [BsonSerializer(typeof(JsonBsonSerializer))]
public AppContributors Contributors { get; set; } = new AppContributors();
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public Dictionary<string, MongoAppEntityContributor> Contributors { get; set; } [BsonSerializer(typeof(JsonBsonSerializer))]
public LanguagesConfig LanguagesConfig { get; } = LanguagesConfig.Build(Language.EN);
public PartitionResolver PartitionResolver public PartitionResolver PartitionResolver
{ {
get { return LanguagesConfig.ToResolver(); } 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Bson.Serialization;
using MongoDB.Driver; using MongoDB.Driver;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Repositories; using Squidex.Domain.Apps.Read.Apps.Repositories;
using Squidex.Infrastructure.CQRS.Events; 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 partial class MongoAppRepository : MongoRepositoryBase<MongoAppEntity>, IAppRepository, IEventConsumer
{ {
public MongoAppRepository(IMongoDatabase database) public MongoAppRepository(IMongoDatabase database, JsonSerializer serializer)
: base(database) : base(database)
{ {
BsonSerializer.RegisterSerializer(new JsonBsonSerializer(serializer));
} }
protected override string CollectionName() protected override string CollectionName()

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

@ -7,13 +7,12 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Utils;
using Squidex.Domain.Apps.Read.MongoDb.Utils; using Squidex.Domain.Apps.Read.MongoDb.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
@ -41,10 +40,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{ {
return Collection.CreateAsync(@event, headers, a => return Collection.CreateAsync(@event, headers, a =>
{ {
a.Clients = new Dictionary<string, MongoAppEntityClient>(); SimpleMapper.Map(@event, a);
a.Contributors = new Dictionary<string, MongoAppEntityContributor>(); });
a.ContributorIds = new List<string>(); }
protected Task On(AppPlanChanged @event, EnvelopeHeaders headers)
{
return UpdateAppAsync(@event, headers, a =>
{
SimpleMapper.Map(@event, a); SimpleMapper.Map(@event, a);
}); });
} }
@ -53,7 +56,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{ {
return UpdateAppAsync(@event, headers, a => 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 => 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 => 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 => 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 => 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 => 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 => 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 => 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 => return UpdateAppAsync(@event, headers, a =>
{ {
a.UpdateLanguages(c => c.Update(@event.Language, @event.IsOptional, @event.IsMaster, @event.Fallback)); a.LanguagesConfig.Apply(@event);
});
}
protected Task On(AppPlanChanged @event, EnvelopeHeaders headers)
{
return UpdateAppAsync(@event, headers, a =>
{
a.ChangePlan(@event.PlanId, @event.Actor);
}); });
} }
@ -135,7 +130,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps
{ {
updater(a); 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;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents; 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.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;

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

@ -9,6 +9,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents; 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 System.Linq;
using Microsoft.OData.UriParser; using Microsoft.OData.UriParser;
using MongoDB.Driver; using MongoDB.Driver;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Edm;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors 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 System;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -20,16 +18,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
{ {
public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity
{ {
private Lazy<Schema> schema;
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public string Name { get; set; } public string Name { get; set; }
[BsonRequired]
[BsonElement]
public BsonDocument SchemaDocument { get; set; }
[BsonRequired] [BsonRequired]
[BsonElement] [BsonElement]
public long Version { get; set; } public long Version { get; set; }
@ -74,32 +66,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
[BsonElement] [BsonElement]
public string ScriptChange { get; set; } public string ScriptChange { get; set; }
Schema ISchemaEntity.SchemaDef [BsonRequired]
{ [BsonElement]
get { return schema.Value; } [BsonSerializer(typeof(JsonBsonSerializer))]
} public Schema SchemaDef { get; set; }
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);
}
}
} }
} }

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

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

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

@ -8,7 +8,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Old; 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) s.IsPublished = s.SchemaDef.IsPublished;
{ });
return Collection.UpdateAsync(@event, headers, e => { });
}
protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
{
return Collection.UpdateAsync(@event, headers, e => { });
} }
} }
} }

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

@ -7,7 +7,8 @@
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> <ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Read.Apps namespace Squidex.Domain.Apps.Read.Apps
{ {
@ -19,11 +19,11 @@ namespace Squidex.Domain.Apps.Read.Apps
string PlanOwner { get; } 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; } 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.Extensions.Caching.Memory;
using Microsoft.OData.Edm; using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Edm;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Utils; using Squidex.Domain.Apps.Read.Utils;

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

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

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

@ -6,22 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Contents.Commands namespace Squidex.Domain.Apps.Write.Contents.Commands
{ {
public abstract class ContentDataCommand : ContentCommand, IValidatable public abstract class ContentDataCommand : ContentCommand
{ {
public NamedContentData Data { get; set; } 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
namespace Squidex.Domain.Apps.Write.Contents.Commands namespace Squidex.Domain.Apps.Write.Contents.Commands
{ {
public sealed class CreateContent : ContentDataCommand public sealed class CreateContent : ContentDataCommand
{ {
public bool Publish { get; set; } 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands namespace Squidex.Domain.Apps.Write.Contents.Commands
{ {
public sealed class PatchContent : ContentDataCommand public sealed class PatchContent : ContentDataCommand

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

@ -6,6 +6,8 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands namespace Squidex.Domain.Apps.Write.Contents.Commands
{ {
public sealed class UpdateContent : ContentDataCommand 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.EnrichContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Apps.Services; using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Read.Assets.Repositories; 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) 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 schemaErrors = new List<ValidationError>();
var appId = command.AppId.Id; 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) public ContentDomainObject Create(CreateContent command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot create content");
VerifyNotCreated(); VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new ContentCreated())); RaiseEvent(SimpleMapper.Map(command, new ContentCreated()));
@ -85,8 +83,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Delete(DeleteContent command) public ContentDomainObject Delete(DeleteContent command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ContentDeleted())); RaiseEvent(SimpleMapper.Map(command, new ContentDeleted()));
@ -96,10 +92,7 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject ChangeStatus(ChangeContentStatus command) public ContentDomainObject ChangeStatus(ChangeContentStatus command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
VerifyCanChangeStatus(command.Status);
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged()));
@ -108,8 +101,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Update(UpdateContent command) public ContentDomainObject Update(UpdateContent command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot update content");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
if (!command.Data.Equals(Data)) if (!command.Data.Equals(Data))
@ -122,8 +113,6 @@ namespace Squidex.Domain.Apps.Write.Contents
public ContentDomainObject Patch(PatchContent command) public ContentDomainObject Patch(PatchContent command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot patch content");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
var newData = Data.MergeInto(command.Data); var newData = Data.MergeInto(command.Data);
@ -136,14 +125,6 @@ namespace Squidex.Domain.Apps.Write.Contents
return this; 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() private void VerifyNotCreated()
{ {
if (isCreated) 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands 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 Name { get; set; }
public string Partitioning { get; set; } public string Partitioning { get; set; }
public FieldProperties Properties { 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;
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using SchemaFields = System.Collections.Generic.List<Squidex.Domain.Apps.Write.Schemas.Commands.CreateSchemaField>; using SchemaFields = System.Collections.Generic.List<Squidex.Domain.Apps.Write.Schemas.Commands.CreateSchemaField>;
namespace Squidex.Domain.Apps.Write.Schemas.Commands 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 Guid SchemaId { get; set; }
public SchemaProperties Properties public SchemaFields Fields { get; set; }
{
get
{
return properties ?? (properties = new SchemaProperties());
}
set
{
properties = value;
}
}
public SchemaFields Fields public SchemaProperties Properties { get; set; }
{
get
{
return fields ?? (fields = new SchemaFields());
}
set
{
fields = value;
}
}
public string Name { get; set; } public string Name { get; set; }
@ -58,28 +32,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands
{ {
SchemaId = Guid.NewGuid(); 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands namespace Squidex.Domain.Apps.Write.Schemas.Commands
{ {
@ -27,34 +23,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands
public bool IsDisabled { get; set; } public bool IsDisabled { get; set; }
public FieldProperties Properties { 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 System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands 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 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands namespace Squidex.Domain.Apps.Write.Schemas.Commands
{ {
public sealed class UpdateField : FieldCommand, IValidatable public sealed class UpdateField : FieldCommand
{ {
public FieldProperties Properties { get; set; } 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. // All rights reserved.
// ========================================================================== // ==========================================================================
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Schemas.Commands namespace Squidex.Domain.Apps.Write.Schemas.Commands
{ {
public sealed class UpdateSchema : SchemaAggregateCommand, IValidatable public sealed class UpdateSchema : SchemaAggregateCommand
{ {
public SchemaProperties Properties { get; set; } 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.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -22,7 +23,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
public static IEnumerable<ValidationError> Validate(FieldProperties properties) public static IEnumerable<ValidationError> Validate(FieldProperties properties)
{ {
return properties.Accept(Instance); return properties?.Accept(Instance) ?? Enumerable.Empty<ValidationError>();
} }
public IEnumerable<ValidationError> Visit(AssetsFieldProperties properties) public IEnumerable<ValidationError> Visit(AssetsFieldProperties properties)
@ -110,7 +111,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.Editor)); 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.", yield return new ValidationError("Radio buttons or dropdown list need allowed values.",
nameof(properties.AllowedValues)); nameof(properties.AllowedValues));
@ -135,7 +136,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.MaxValue)); 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.", yield return new ValidationError("Either allowed values or min and max value can be defined.",
nameof(properties.AllowedValues), nameof(properties.AllowedValues),
@ -162,7 +163,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.Editor)); 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.", yield return new ValidationError("Radio buttons or dropdown list need allowed values.",
nameof(properties.AllowedValues)); nameof(properties.AllowedValues));
@ -181,7 +182,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards
nameof(properties.MaxLength)); 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.", yield return new ValidationError("Either allowed values or min and max length can be defined.",
nameof(properties.AllowedValues), 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 System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Schemas.Commands; using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
@ -31,19 +32,12 @@ namespace Squidex.Domain.Apps.Write.Schemas
this.schemas = 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 = await GuardSchema.CanCreate(command, schemas);
new ValidationError($"A schema with name '{command.Name}' already exists", "Name",
nameof(CreateSchema.Name));
throw new ValidationException("Cannot create a new schema.", error);
}
await handler.CreateAsync<SchemaDomainObject>(context, s =>
{
s.Create(command); s.Create(command);
context.Complete(EntityCreatedResult.Create(s.Id, s.Version)); context.Complete(EntityCreatedResult.Create(s.Id, s.Version));
@ -54,75 +48,142 @@ namespace Squidex.Domain.Apps.Write.Schemas
{ {
return handler.UpdateAsync<SchemaDomainObject>(context, s => return handler.UpdateAsync<SchemaDomainObject>(context, s =>
{ {
GuardSchemaField.CanAdd(s.Schema, command);
s.Add(command); s.Add(command);
context.Complete(EntityCreatedResult.Create(s.Schema.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version)); 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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;
using Squidex.Domain.Apps.Events.Schemas.Utils; using Squidex.Domain.Apps.Events.Schemas.Utils;
using Squidex.Domain.Apps.Write.Schemas.Commands; using Squidex.Domain.Apps.Write.Schemas.Commands;
using Squidex.Domain.Apps.Write.Schemas.Guards;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS; using Squidex.Infrastructure.CQRS;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
@ -46,73 +45,73 @@ namespace Squidex.Domain.Apps.Write.Schemas
this.registry = registry; 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) protected void On(FieldUpdated @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldLocked @event) protected void On(FieldLocked @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldHidden @event) protected void On(FieldHidden @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldShown @event) protected void On(FieldShown @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldDisabled @event) protected void On(FieldDisabled @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldEnabled @event) protected void On(FieldEnabled @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(SchemaUpdated @event) protected void On(SchemaUpdated @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(FieldDeleted @event) protected void On(FieldDeleted @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(SchemaFieldsReordered @event) protected void On(SchemaFieldsReordered @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(SchemaPublished @event) protected void On(SchemaPublished @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(SchemaUnpublished @event) protected void On(SchemaUnpublished @event)
{ {
schema = SchemaEventDispatcher.Dispatch(@event, schema); schema.Apply(@event);
} }
protected void On(SchemaDeleted @event) protected void On(SchemaDeleted @event)
@ -122,8 +121,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Create(CreateSchema command) public SchemaDomainObject Create(CreateSchema command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot create schema");
VerifyNotCreated(); VerifyNotCreated();
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(Id, command.Name) }); 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) public SchemaDomainObject Add(AddField command)
{ {
Guard.Valid(command, nameof(command), () => $"Cannot add field to schema {Id}");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanAdd(schema, command.Name);
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(totalFields + 1, command.Name) })); RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(totalFields + 1, command.Name) }));
return this; return this;
@ -160,12 +153,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject UpdateField(UpdateField command) public SchemaDomainObject UpdateField(UpdateField command)
{ {
Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanUpdate(schema, command.FieldId);
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
return this; return this;
@ -173,12 +162,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject LockField(LockField command) public SchemaDomainObject LockField(LockField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanLock(schema, command.FieldId);
RaiseEvent(command, new FieldLocked()); RaiseEvent(command, new FieldLocked());
return this; return this;
@ -186,12 +171,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject HideField(HideField command) public SchemaDomainObject HideField(HideField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanHide(schema, command.FieldId);
RaiseEvent(command, new FieldHidden()); RaiseEvent(command, new FieldHidden());
return this; return this;
@ -199,12 +180,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject ShowField(ShowField command) public SchemaDomainObject ShowField(ShowField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanShow(schema, command.FieldId);
RaiseEvent(command, new FieldShown()); RaiseEvent(command, new FieldShown());
return this; return this;
@ -212,12 +189,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject DisableField(DisableField command) public SchemaDomainObject DisableField(DisableField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanDisable(schema, command.FieldId);
RaiseEvent(command, new FieldDisabled()); RaiseEvent(command, new FieldDisabled());
return this; return this;
@ -225,12 +198,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject EnableField(EnableField command) public SchemaDomainObject EnableField(EnableField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanEnable(schema, command.FieldId);
RaiseEvent(command, new FieldEnabled()); RaiseEvent(command, new FieldEnabled());
return this; return this;
@ -238,12 +207,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject DeleteField(DeleteField command) public SchemaDomainObject DeleteField(DeleteField command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaFieldGuard.GuardCanDelete(schema, command.FieldId);
RaiseEvent(command, new FieldDeleted()); RaiseEvent(command, new FieldDeleted());
return this; return this;
@ -251,12 +216,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Reorder(ReorderFields command) public SchemaDomainObject Reorder(ReorderFields command)
{ {
Guard.Valid(command, nameof(command), () => $"Cannot reorder fields for schema '{Id}'");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanReorder(schema, command.FieldIds);
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
return this; return this;
@ -264,12 +225,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Publish(PublishSchema command) public SchemaDomainObject Publish(PublishSchema command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanPublish(schema);
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
return this; return this;
@ -277,12 +234,8 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Unpublish(UnpublishSchema command) public SchemaDomainObject Unpublish(UnpublishSchema command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
SchemaGuard.GuardCanUnpublish(schema);
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
return this; return this;
@ -290,8 +243,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject ConfigureScripts(ConfigureScripts command) public SchemaDomainObject ConfigureScripts(ConfigureScripts command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
@ -310,8 +261,6 @@ namespace Squidex.Domain.Apps.Write.Schemas
public SchemaDomainObject Update(UpdateSchema command) public SchemaDomainObject Update(UpdateSchema command)
{ {
Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));

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

@ -7,7 +7,8 @@
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> <ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.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;
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Webhooks; using Squidex.Domain.Apps.Core.Webhooks;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Webhooks.Commands 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 Uri Url { get; set; }
public List<WebhookSchema> Schemas public List<WebhookSchema> Schemas { get; set; } = new List<WebhookSchema>();
{
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)));
}
}
} }
} }

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;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Domain.Apps.Write.Webhooks.Commands; using Squidex.Domain.Apps.Write.Webhooks.Commands;
using Squidex.Domain.Apps.Write.Webhooks.Guards;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.CQRS.Commands;
using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Dispatching;
@ -33,21 +33,32 @@ namespace Squidex.Domain.Apps.Write.Webhooks
protected async Task On(CreateWebhook command, CommandContext context) 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) 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) 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) public async Task HandleAsync(CommandContext context, Func<Task> next)
@ -57,21 +68,5 @@ namespace Squidex.Domain.Apps.Write.Webhooks
await next(); 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) public void Create(CreateWebhook command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot create webhook");
VerifyNotCreated(); VerifyNotCreated();
RaiseEvent(SimpleMapper.Map(command, new WebhookCreated())); RaiseEvent(SimpleMapper.Map(command, new WebhookCreated()));
@ -48,8 +46,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks
public void Update(UpdateWebhook command) public void Update(UpdateWebhook command)
{ {
Guard.Valid(command, nameof(command), () => "Cannot update webhook");
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookUpdated())); RaiseEvent(SimpleMapper.Map(command, new WebhookUpdated()));
@ -57,8 +53,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks
public void Delete(DeleteWebhook command) public void Delete(DeleteWebhook command)
{ {
Guard.NotNull(command, nameof(command));
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new WebhookDeleted())); 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 // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -12,7 +12,7 @@ using Newtonsoft.Json.Linq;
namespace Squidex.Infrastructure.MongoDb namespace Squidex.Infrastructure.MongoDb
{ {
public static class BsonConverter public static class JsonBsonConverter
{ {
public static BsonDocument ToBson(this JObject source) 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 namespace Squidex.Infrastructure.MongoDb
{ {
public class RefTokenSerializer : SerializerBase<RefToken> public class RefTokenSerializer : ClassSerializerBase<RefToken>
{ {
private static readonly Lazy<bool> Registerer = new Lazy<bool>(() => private static readonly Lazy<bool> Registerer = new Lazy<bool>(() =>
{ {
@ -26,23 +26,16 @@ namespace Squidex.Infrastructure.MongoDb
return !Registerer.IsValueCreated && Registerer.Value; 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(); 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());
{
context.Writer.WriteString(value.ToString());
}
else
{
context.Writer.WriteNull();
}
} }
} }
} }

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>
<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.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Domain.Users.MongoDb\Squidex.Domain.Users.MongoDb.csproj" /> <ProjectReference Include="..\Squidex.Domain.Users.MongoDb\Squidex.Domain.Users.MongoDb.csproj" />
<ProjectReference Include="..\Squidex.Domain.Users\Squidex.Domain.Users.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\**" /> <None Remove="MongoDb\**" />
</ItemGroup> </ItemGroup>
<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.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> <ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.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] [Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified() public void Should_add_error_if_allowed_values_and_max_value_is_specified()
{ {
var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = ImmutableList.Create<double>(4) }; var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = new[] { 4d } };
var errors = FieldPropertiesValidator.Validate(sut).ToList(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified() public void Should_add_error_if_allowed_values_and_min_value_is_specified()
{ {
var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = ImmutableList.Create<double>(4) }; var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = new[] { 4d } };
var errors = FieldPropertiesValidator.Validate(sut).ToList(); 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] [Fact]
public void Should_add_error_if_allowed_values_and_max_value_is_specified() public void Should_add_error_if_allowed_values_and_max_value_is_specified()
{ {
var sut = new StringFieldProperties { MinLength = 10, AllowedValues = ImmutableList.Create("4") }; var sut = new StringFieldProperties { MinLength = 10, AllowedValues = new[] { "4" } };
var errors = FieldPropertiesValidator.Validate(sut).ToList(); var errors = FieldPropertiesValidator.Validate(sut).ToList();
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties
[Fact] [Fact]
public void Should_add_error_if_allowed_values_and_min_value_is_specified() public void Should_add_error_if_allowed_values_and_min_value_is_specified()
{ {
var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = ImmutableList.Create("4") }; var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = new string[] { "4" } };
var errors = FieldPropertiesValidator.Validate(sut).ToList(); 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); schema = new SchemaDomainObject(SchemaId, -1, registry);
sut = new SchemaCommandMiddleware(Handler, schemas); 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)) A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName))
.Returns(A.Dummy<ISchemaEntity>()); .Returns((ISchemaEntity)null);
await TestCreate(schema, async _ =>
{
await Assert.ThrowsAsync<ValidationException>(async () => await sut.HandleAsync(context));
}, false);
A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened();
} }
[Fact] [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 }); var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId });
A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName))
.Returns((ISchemaEntity)null);
await TestCreate(schema, async _ => await TestCreate(schema, async _ =>
{ {
await sut.HandleAsync(context); await sut.HandleAsync(context);
@ -291,4 +275,4 @@ namespace Squidex.Domain.Apps.Write.Schemas
schema.DisableField(CreateCommand(new DisableField { FieldId = 1 })); 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] [Fact]
public void Create_should_create_schema_and_create_events() public void Create_should_create_schema_and_create_events()
{ {
@ -118,7 +55,7 @@ namespace Squidex.Domain.Apps.Write.Schemas
sut.GetUncomittedEvents() sut.GetUncomittedEvents()
.ShouldHaveSameEvents( .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] [Fact]
public void Update_should_refresh_properties_and_create_events() 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] [Fact]
public void Reorder_should_refresh_properties_and_create_events() 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] [Fact]
public void AddField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void UpdateField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void LockField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void HideField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void ShowField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void DisableField_should_throw_exception_if_schema_is_deleted() 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] [Fact]
public void EnableField_should_throw_exception_if_schema_is_deleted() 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> <RootNamespace>Squidex.Domain.Apps.Write</RootNamespace>
</PropertyGroup> </PropertyGroup>
<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.Infrastructure\Squidex.Infrastructure.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.Domain.Apps.Write\Squidex.Domain.Apps.Write.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FakeItEasy" Version="4.1.0" /> <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