Browse Source

Merge pull request #206 from Squidex/refactoring-no-cqrs

Refactoring no cqrs
pull/208/head
Sebastian Stehle 8 years ago
committed by GitHub
parent
commit
75be984bc6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      Dockerfile
  2. 3
      Dockerfile.build
  3. 131
      Squidex.sln
  4. 10
      src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs
  5. 7
      src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs
  6. 7
      src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs
  7. 2
      src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  8. 41
      src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppEntity.cs
  9. 104
      src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppRepository.cs
  10. 31
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs
  11. 80
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  12. 6
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsEntity.cs
  13. 22
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsRepository.cs
  14. 8
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsRepository_EventHandling.cs
  15. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs
  16. 35
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  17. 248
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  18. 58
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs
  19. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/ConstantVisitor.cs
  20. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterBuilder.cs
  21. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterVisitor.cs
  22. 3
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs
  23. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/PropertyVisitor.cs
  24. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/SearchTermVisitor.cs
  25. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/SortBuilder.cs
  26. 3
      src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs
  27. 26
      src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs
  28. 4
      src/Squidex.Domain.Apps.Entities.MongoDb/History/ParsedHistoryEvent.cs
  29. 4
      src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs
  30. 41
      src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEntity.cs
  31. 6
      src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventEntity.cs
  32. 22
      src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs
  33. 95
      src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository.cs
  34. 41
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs
  35. 104
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs
  36. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj
  37. 2
      src/Squidex.Domain.Apps.Entities/AppAggregateCommand.cs
  38. 2
      src/Squidex.Domain.Apps.Entities/AppCommand.cs
  39. 150
      src/Squidex.Domain.Apps.Entities/AppProvider.cs
  40. 55
      src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs
  41. 142
      src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs
  42. 2
      src/Squidex.Domain.Apps.Entities/Apps/AppEntityExtensions.cs
  43. 22
      src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs
  44. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs
  45. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs
  46. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs
  47. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs
  48. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs
  49. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs
  50. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs
  51. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs
  52. 4
      src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs
  53. 2
      src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs
  54. 7
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs
  55. 4
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs
  56. 8
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs
  57. 6
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs
  58. 8
      src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs
  59. 21
      src/Squidex.Domain.Apps.Entities/Apps/Repositories/IAppRepository.cs
  60. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/IAppLimitsPlan.cs
  61. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlanBillingManager.cs
  62. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs
  63. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/IChangePlanResult.cs
  64. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppLimitsPlan.cs
  65. 8
      src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs
  66. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/NoopAppPlanBillingManager.cs
  67. 3
      src/Squidex.Domain.Apps.Entities/Apps/Services/PlanChangeAsyncResult.cs
  68. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/PlanChangedResult.cs
  69. 2
      src/Squidex.Domain.Apps.Entities/Apps/Services/RedirectToCheckoutResult.cs
  70. 107
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  71. 24
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  72. 73
      src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs
  73. 2
      src/Squidex.Domain.Apps.Entities/Assets/AssetSavedResult.cs
  74. 2
      src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetAggregateCommand.cs
  75. 2
      src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs
  76. 2
      src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs
  77. 2
      src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs
  78. 2
      src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs
  79. 4
      src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs
  80. 2
      src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs
  81. 2
      src/Squidex.Domain.Apps.Entities/Assets/IAssetStatsEntity.cs
  82. 2
      src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs
  83. 2
      src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetStatsRepository.cs
  84. 91
      src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs
  85. 2
      src/Squidex.Domain.Apps.Entities/CachingProviderBase.cs
  86. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs
  87. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs
  88. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentDataCommand.cs
  89. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs
  90. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/DeleteContent.cs
  91. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/PatchContent.cs
  92. 2
      src/Squidex.Domain.Apps.Entities/Contents/Commands/UpdateContent.cs
  93. 17
      src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs
  94. 2
      src/Squidex.Domain.Apps.Entities/Contents/ContentDataChangedResult.cs
  95. 70
      src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs
  96. 4
      src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs
  97. 42
      src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs
  98. 21
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  99. 6
      src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelBuilder.cs
  100. 2
      src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelExtensions.cs

3
Dockerfile

@ -47,8 +47,7 @@ RUN cp -a /tmp/node_modules /src/Squidex/ \
RUN dotnet restore \
&& dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj
# Publish

3
Dockerfile.build

@ -44,8 +44,7 @@ RUN cp -a /tmp/node_modules /src/Squidex/ \
RUN dotnet restore \
&& dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj
# Publish

131
Squidex.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2009
VisualStudioVersion = 15.0.27130.2003
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}"
EndProject
@ -12,22 +12,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure", "s
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}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Write", "src\Squidex.Domain.Apps.Write\Squidex.Domain.Apps.Write.csproj", "{A85201C6-6AF8-4B63-8365-08F741050438}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Read", "src\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj", "{A92B4734-2587-4F6F-97A3-741BE48709A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Read.MongoDb", "src\Squidex.Domain.Apps.Read.MongoDb\Squidex.Domain.Apps.Read.MongoDb.csproj", "{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Write.Tests", "tests\Squidex.Domain.Apps.Write.Tests\Squidex.Domain.Apps.Write.Tests.csproj", "{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Tests", "tests\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj", "{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Tests", "tests\Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj", "{FD0AFD44-7A93-4F9E-B5ED-72582392E435}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.MongoDb", "src\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj", "{6A811927-3C37-430A-90F4-503E37123956}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Read.Tests", "tests\Squidex.Domain.Apps.Read.Tests\Squidex.Domain.Apps.Read.Tests.csproj", "{8B074219-F69A-4E41-83C6-12EE1E647779}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Redis", "src\Squidex.Infrastructure.Redis\Squidex.Infrastructure.Redis.csproj", "{D7166C56-178A-4457-B56A-C615C7450DEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.RabbitMq", "src\Squidex.Infrastructure.RabbitMq\Squidex.Infrastructure.RabbitMq.csproj", "{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343}"
@ -36,7 +26,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Goog
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{94207AA6-4923-4183-A558-E0F8196B8CA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{B51126A8-0D75-4A79-867D-10724EC6AC84}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_00", "tools\Migrate_00\Migrate_00.csproj", "{B51126A8-0D75-4A79-867D-10724EC6AC84}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Shared", "src\Squidex.Shared\Squidex.Shared.csproj", "{5E75AB7D-6F01-4313-AFF1-7F7128FFD71F}"
EndProject
@ -63,7 +53,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Mo
EndProject
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "tests\Benchmarks\Benchmarks.csproj", "{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Entities", "src\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj", "{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Entities.Tests", "tests\Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj", "{AA003372-CD8D-4DBC-962C-F61E0C93CF05}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Entities.MongoDb", "src\Squidex.Domain.Apps.Entities.MongoDb\Squidex.Domain.Apps.Entities.MongoDb.csproj", "{7DA5B308-D950-4496-93D5-21D6C4D91644}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -99,38 +95,6 @@ Global
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|Any CPU.Build.0 = Release|Any CPU
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|x64.ActiveCfg = Release|Any CPU
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|x86.ActiveCfg = Release|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|x64.ActiveCfg = Debug|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Debug|x86.ActiveCfg = Debug|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|Any CPU.Build.0 = Release|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|x64.ActiveCfg = Release|Any CPU
{A85201C6-6AF8-4B63-8365-08F741050438}.Release|x86.ActiveCfg = Release|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|x64.ActiveCfg = Debug|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Debug|x86.ActiveCfg = Debug|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|Any CPU.Build.0 = Release|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|x64.ActiveCfg = Release|Any CPU
{A92B4734-2587-4F6F-97A3-741BE48709A5}.Release|x86.ActiveCfg = Release|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Debug|x64.ActiveCfg = Debug|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Debug|x86.ActiveCfg = Debug|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|Any CPU.Build.0 = Release|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|x64.ActiveCfg = Release|Any CPU
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD}.Release|x86.ActiveCfg = Release|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|x64.ActiveCfg = Debug|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Debug|x86.ActiveCfg = Debug|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|Any CPU.Build.0 = Release|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|x64.ActiveCfg = Release|Any CPU
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203}.Release|x86.ActiveCfg = Release|Any CPU
{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -155,14 +119,6 @@ Global
{6A811927-3C37-430A-90F4-503E37123956}.Release|Any CPU.Build.0 = Release|Any CPU
{6A811927-3C37-430A-90F4-503E37123956}.Release|x64.ActiveCfg = Release|Any CPU
{6A811927-3C37-430A-90F4-503E37123956}.Release|x86.ActiveCfg = Release|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Debug|x64.ActiveCfg = Debug|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Debug|x86.ActiveCfg = Debug|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Release|Any CPU.Build.0 = Release|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Release|x64.ActiveCfg = Release|Any CPU
{8B074219-F69A-4E41-83C6-12EE1E647779}.Release|x86.ActiveCfg = Release|Any CPU
{D7166C56-178A-4457-B56A-C615C7450DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7166C56-178A-4457-B56A-C615C7450DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7166C56-178A-4457-B56A-C615C7450DEE}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -303,18 +259,54 @@ Global
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x64.Build.0 = Release|Any CPU
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x86.ActiveCfg = Release|Any CPU
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x86.Build.0 = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x64.ActiveCfg = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x64.Build.0 = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x86.ActiveCfg = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x86.Build.0 = Debug|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|Any CPU.Build.0 = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x64.ActiveCfg = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x64.Build.0 = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x86.ActiveCfg = Release|Any CPU
{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x86.Build.0 = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|x64.ActiveCfg = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|x64.Build.0 = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|x86.ActiveCfg = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|x86.Build.0 = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|Any CPU.Build.0 = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|x64.ActiveCfg = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|x64.Build.0 = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|x86.ActiveCfg = Release|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Release|x86.Build.0 = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|x64.ActiveCfg = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|x64.Build.0 = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|x86.ActiveCfg = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Debug|x86.Build.0 = Debug|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|Any CPU.Build.0 = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|x64.ActiveCfg = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|x64.Build.0 = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|x86.ActiveCfg = Release|Any CPU
{AA003372-CD8D-4DBC-962C-F61E0C93CF05}.Release|x86.Build.0 = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|x64.ActiveCfg = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|x64.Build.0 = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|x86.ActiveCfg = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Debug|x86.Build.0 = Debug|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|Any CPU.Build.0 = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x64.ActiveCfg = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x64.Build.0 = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x86.ActiveCfg = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x86.Build.0 = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|x64.Build.0 = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Debug|x86.Build.0 = Debug|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|Any CPU.Build.0 = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x64.ActiveCfg = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x64.Build.0 = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.ActiveCfg = Release|Any CPU
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -322,14 +314,9 @@ Global
GlobalSection(NestedProjects) = preSolution
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A85201C6-6AF8-4B63-8365-08F741050438} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A92B4734-2587-4F6F-97A3-741BE48709A5} = {C9809D59-6665-471E-AD87-5AC624C65892}
{28F8E9E2-FE24-41F7-A888-9FC244A9E2DD} = {C9809D59-6665-471E-AD87-5AC624C65892}
{9A3DEA7E-1681-4D48-AC5C-1F0DE421A203} = {C9809D59-6665-471E-AD87-5AC624C65892}
{7FD0A92B-7862-4BB1-932B-B52A9CACB56B} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{FD0AFD44-7A93-4F9E-B5ED-72582392E435} = {C9809D59-6665-471E-AD87-5AC624C65892}
{6A811927-3C37-430A-90F4-503E37123956} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{8B074219-F69A-4E41-83C6-12EE1E647779} = {C9809D59-6665-471E-AD87-5AC624C65892}
{D7166C56-178A-4457-B56A-C615C7450DEE} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{945871B1-77B8-43FB-B53C-27CF385AB756} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
@ -344,6 +331,10 @@ Global
{7931187E-A1E6-4F89-8BC8-20A1E445579F} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{F0A83301-50A5-40EA-A1A2-07C7858F5A3F} = {C9809D59-6665-471E-AD87-5AC624C65892}
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31} = {C9809D59-6665-471E-AD87-5AC624C65892}
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5} = {C9809D59-6665-471E-AD87-5AC624C65892}
{AA003372-CD8D-4DBC-962C-F61E0C93CF05} = {C9809D59-6665-471E-AD87-5AC624C65892}
{7DA5B308-D950-4496-93D5-21D6C4D91644} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A4823E14-C0E5-4A4D-B28F-27424C25C3C7} = {94207AA6-4923-4183-A558-E0F8196B8CA3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {02F2E872-3141-44F5-BD6A-33CD84E9FE08}

10
src/Squidex.Domain.Apps.Core.Model/Contents/ContentData.cs

@ -31,15 +31,15 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
protected static TResult Merge<TResult>(TResult target, TResult source1, TResult source2) where TResult : ContentData<T>
protected static TResult MergeTo<TResult>(TResult target, params TResult[] sources) where TResult : ContentData<T>
{
if (ReferenceEquals(source1, source2))
Guard.NotEmpty(sources, nameof(sources));
if (sources.Length == 1 || sources.Skip(1).All(x => ReferenceEquals(x, sources[0])))
{
return source1;
return sources[0];
}
var sources = new[] { source1, source2 };
foreach (var source in sources)
{
foreach (var otherValue in source)

7
src/Squidex.Domain.Apps.Core.Model/Contents/IdContentData.cs

@ -24,9 +24,14 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public static IdContentData Merge(params IdContentData[] contents)
{
return MergeTo(new IdContentData(), contents);
}
public IdContentData MergeInto(IdContentData target)
{
return Merge(new IdContentData(), this, target);
return Merge(target, this);
}
public IdContentData ToCleaned()

7
src/Squidex.Domain.Apps.Core.Model/Contents/NamedContentData.cs

@ -24,9 +24,14 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public static NamedContentData Merge(params NamedContentData[] contents)
{
return MergeTo(new NamedContentData(), contents);
}
public NamedContentData MergeInto(NamedContentData target)
{
return Merge(new NamedContentData(), this, target);
return Merge(target, this);
}
public NamedContentData ToCleaned()

2
src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -16,7 +16,7 @@
<PackageReference Include="Jint" Version="2.11.58" />
<PackageReference Include="Microsoft.OData.Core" Version="7.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="NJsonSchema" Version="9.10.10" />
<PackageReference Include="NJsonSchema" Version="9.10.13" />
<PackageReference Include="NodaTime" Version="2.2.3" />
<PackageReference Include="RefactoringEssentials" Version="5.4.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />

41
src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppEntity.cs

@ -0,0 +1,41 @@
// ==========================================================================
// MongoAppEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps
{
public sealed class MongoAppEntity
{
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[BsonElement]
[BsonRequired]
public int Version { get; set; }
[BsonElement]
[BsonRequired]
public string Name { get; set; }
[BsonElement]
[BsonRequired]
public string[] UserIds { get; set; }
[BsonJson]
[BsonElement]
[BsonRequired]
public AppState State { get; set; }
}
}

104
src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppRepository.cs

@ -0,0 +1,104 @@
// ==========================================================================
// MongoAppRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Apps.Repositories;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Apps
{
public sealed class MongoAppRepository : MongoRepositoryBase<MongoAppEntity>, IAppRepository, ISnapshotStore<AppState, Guid>
{
public MongoAppRepository(IMongoDatabase database)
: base(database)
{
}
protected override string CollectionName()
{
return "States_Apps";
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection)
{
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.UserIds));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name));
}
public async Task<Guid> FindAppIdByNameAsync(string name)
{
var appEntity =
await Collection.Find(x => x.Name == name).Only(x => x.Id)
.FirstOrDefaultAsync();
return appEntity != null ? Guid.Parse(appEntity["_id"].AsString) : Guid.Empty;
}
public async Task<IReadOnlyList<Guid>> QueryUserAppIdsAsync(string userId)
{
var appEntities =
await Collection.Find(x => x.UserIds.Contains(userId)).Only(x => x.Id)
.ToListAsync();
return appEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList();
}
public async Task<(AppState Value, long Version)> ReadAsync(Guid key)
{
var existing =
await Collection.Find(x => x.Id == key)
.FirstOrDefaultAsync();
if (existing != null)
{
return (existing.State, existing.Version);
}
return (null, EtagVersion.NotFound);
}
public async Task WriteAsync(Guid key, AppState value, long oldVersion, long newVersion)
{
try
{
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion,
Update
.Set(x => x.UserIds, value.Contributors.Keys.ToArray())
.Set(x => x.Name, value.Name)
.Set(x => x.State, value)
.Set(x => x.Version, newVersion),
Upsert);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await Collection.Find(x => x.Id == key)
.Project<MongoAppEntity>(Projection.Exclude(x => x.Id)).FirstOrDefaultAsync();
if (existingVersion != null)
{
throw new InconsistentStateException(existingVersion.Version, oldVersion, ex);
}
}
else
{
throw;
}
}
}
}
}

31
src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetEntity.cs

@ -0,0 +1,31 @@
// ==========================================================================
// MongoAssetEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Entities.Assets.State;
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed class MongoAssetEntity
{
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[BsonElement]
[BsonRequired]
public AssetState State { get; set; }
[BsonElement]
[BsonRequired]
public int Version { get; set; }
}
}

80
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetRepository.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -12,13 +12,16 @@ using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Read.MongoDb.Assets
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public partial class MongoAssetRepository : MongoRepositoryBase<MongoAssetEntity>, IAssetRepository, IAssetEventConsumer
public sealed class MongoAssetRepository : MongoRepositoryBase<MongoAssetEntity>, IAssetRepository, ISnapshotStore<AssetState, Guid>
{
public MongoAssetRepository(IMongoDatabase database)
: base(database)
@ -27,16 +30,31 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
protected override string CollectionName()
{
return "Projections_Assets";
return "States_Assets";
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection)
{
return collection.Indexes.CreateOneAsync(
Index.Ascending(x => x.AppId)
.Ascending(x => x.FileName)
.Ascending(x => x.MimeType)
.Descending(x => x.LastModified));
Index
.Ascending(x => x.State.AppId)
.Ascending(x => x.State.FileName)
.Ascending(x => x.State.MimeType)
.Descending(x => x.State.LastModified));
}
public async Task<(AssetState Value, long Version)> ReadAsync(Guid key)
{
var existing =
await Collection.Find(x => x.Id == key)
.FirstOrDefaultAsync();
if (existing != null)
{
return (existing.State, existing.Version);
}
return (null, EtagVersion.NotFound);
}
public async Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0)
@ -44,7 +62,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
var filter = CreateFilter(appId, mimeTypes, ids, query);
var assetEntities =
await Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.LastModified)
await Collection.Find(filter).Skip(skip).Limit(take).SortByDescending(x => x.State.LastModified)
.ToListAsync();
return assetEntities.OfType<IAssetEntity>().ToList();
@ -63,18 +81,16 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
public async Task<IAssetEntity> FindAssetAsync(Guid id)
{
var assetEntity =
await Collection.Find(s => s.Id == id)
.FirstOrDefaultAsync();
var (state, etag) = await ReadAsync(id);
return assetEntity;
return state;
}
private static FilterDefinition<MongoAssetEntity> CreateFilter(Guid appId, ICollection<string> mimeTypes, ICollection<Guid> ids, string query)
{
var filters = new List<FilterDefinition<MongoAssetEntity>>
{
Filter.Eq(x => x.AppId, appId)
Filter.Eq(x => x.State.AppId, appId)
};
if (ids != null && ids.Count > 0)
@ -84,17 +100,47 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
if (mimeTypes != null && mimeTypes.Count > 0)
{
filters.Add(Filter.In(x => x.MimeType, mimeTypes));
filters.Add(Filter.In(x => x.State.MimeType, mimeTypes));
}
if (!string.IsNullOrWhiteSpace(query))
{
filters.Add(Filter.Regex(x => x.FileName, new BsonRegularExpression(query, "i")));
filters.Add(Filter.Regex(x => x.State.FileName, new BsonRegularExpression(query, "i")));
}
var filter = Filter.And(filters);
return filter;
}
public async Task WriteAsync(Guid key, AssetState value, long oldVersion, long newVersion)
{
try
{
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion,
Update
.Set(x => x.State, value)
.Set(x => x.Version, newVersion),
Upsert);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await Collection.Find(x => x.Id == key).Only(x => x.Id, x => x.Version)
.FirstOrDefaultAsync();
if (existingVersion != null)
{
throw new InconsistentStateException(existingVersion["Version"].AsInt64, oldVersion, ex);
}
}
else
{
throw;
}
}
}
}
}

6
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsEntity.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsEntity.cs

@ -9,9 +9,9 @@
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Entities.Assets;
namespace Squidex.Domain.Apps.Read.MongoDb.Assets
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public sealed class MongoAssetStatsEntity : IAssetStatsEntity
{
@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
[BsonRequired]
[BsonElement]
public Guid AppId { get; set; }
public Guid AssetId { get; set; }
[BsonRequired]
[BsonElement]

22
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsRepository.cs

@ -11,14 +11,15 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.Assets
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public partial class MongoAssetStatsRepository : MongoRepositoryBase<MongoAssetStatsEntity>, IAssetStatsRepository, IAssetEventConsumer
public partial class MongoAssetStatsRepository : MongoRepositoryBase<MongoAssetStatsEntity>, IAssetStatsRepository, IEventConsumer
{
public MongoAssetStatsRepository(IMongoDatabase database)
: base(database)
@ -30,17 +31,16 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
return "Projections_AssetStats";
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetStatsEntity> collection)
protected override async Task SetupCollectionAsync(IMongoCollection<MongoAssetStatsEntity> collection)
{
return Task.WhenAll(
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.Date)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Descending(x => x.Date)));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AssetId).Ascending(x => x.Date));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AssetId).Descending(x => x.Date));
}
public async Task<IReadOnlyList<IAssetStatsEntity>> QueryAsync(Guid appId, DateTime fromDate, DateTime toDate)
{
var originalSizesEntities =
await Collection.Find(x => x.AppId == appId && x.Date >= fromDate && x.Date <= toDate).SortBy(x => x.Date)
await Collection.Find(x => x.AssetId == appId && x.Date >= fromDate && x.Date <= toDate).SortBy(x => x.Date)
.ToListAsync();
var enrichedSizes = new List<MongoAssetStatsEntity>();
@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
if (previousSize < 0)
{
var firstBeforeRangeEntity =
await Collection.Find(x => x.AppId == appId && x.Date < fromDate).SortByDescending(x => x.Date)
await Collection.Find(x => x.AssetId == appId && x.Date < fromDate).SortByDescending(x => x.Date)
.FirstOrDefaultAsync();
previousSize = firstBeforeRangeEntity?.TotalSize ?? 0L;
@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
public async Task<long> GetTotalSizeAsync(Guid appId)
{
var totalSizeEntity =
await Collection.Find(x => x.AppId == appId).SortByDescending(x => x.Date)
await Collection.Find(x => x.AssetId == appId).SortByDescending(x => x.Date)
.FirstOrDefaultAsync();
return totalSizeEntity?.TotalSize ?? 0;

8
src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetStatsRepository_EventHandling.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetStatsRepository_EventHandling.cs

@ -13,12 +13,10 @@ using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.MongoDb.Assets
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
{
public partial class MongoAssetStatsRepository
{
private static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true };
public string Name
{
get { return GetType().Name; }
@ -60,14 +58,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Assets
if (assetStatsEntity == null)
{
var lastEntity =
await Collection.Find(x => x.AppId == appId).SortByDescending(x => x.Date)
await Collection.Find(x => x.AssetId == appId).SortByDescending(x => x.Date)
.FirstOrDefaultAsync();
assetStatsEntity = new MongoAssetStatsEntity
{
Id = id,
Date = date,
AppId = appId,
AssetId = appId,
TotalSize = lastEntity?.TotalSize ?? 0,
TotalCount = lastEntity?.TotalCount ?? 0
};

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

@ -16,7 +16,7 @@ using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public static class Extensions
{

35
src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs

@ -13,30 +13,23 @@ using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Contents;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public sealed class MongoContentEntity :
IContentEntity,
IUpdateableEntityWithVersion,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy,
IUpdateableEntityWithAppRef
public sealed class MongoContentEntity : IContentEntity
{
private NamedContentData data;
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
public string DocumentId { get; set; }
[BsonRequired]
[BsonElement("st")]
[BsonRepresentation(BsonType.String)]
public Status Status { get; set; }
[BsonElement]
public Guid Id { get; set; }
[BsonRequired]
[BsonElement("ct")]
@ -78,19 +71,29 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
[BsonElement("rd")]
public List<Guid> ReferencedIdsDeleted { get; set; } = new List<Guid>();
[BsonRequired]
[BsonElement("lt")]
public bool IsLatest { get; set; }
[BsonRequired]
[BsonElement("st")]
[BsonRepresentation(BsonType.String)]
public Status Status { get; set; }
[BsonRequired]
[BsonElement("do")]
[BsonJson]
public IdContentData IdData { get; set; }
public IdContentData DataByIds { get; set; }
NamedContentData IContentEntity.Data
[BsonIgnore]
public NamedContentData Data
{
get { return data; }
}
public void ParseData(Schema schema)
{
data = IdData.ToData(schema, ReferencedIdsDeleted);
data = DataByIds.ToData(schema, ReferencedIdsDeleted);
}
}
}

248
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -0,0 +1,248 @@
// ==========================================================================
// MongoContentRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.OData.UriParser;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public partial class MongoContentRepository : MongoRepositoryBase<MongoContentEntity>,
IEventConsumer,
IContentRepository,
ISnapshotStore<ContentState, Guid>
{
private readonly IAppProvider appProvider;
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
: base(database)
{
Guard.NotNull(appProvider, nameof(appProvider));
this.appProvider = appProvider;
}
protected override string CollectionName()
{
return "States_Contents";
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection)
{
await collection.Indexes.CreateOneAsync(
Index
.Ascending(x => x.Id)
.Ascending(x => x.Version));
await collection.Indexes.CreateOneAsync(
Index
.Ascending(x => x.Id)
.Descending(x => x.Version));
await collection.Indexes.CreateOneAsync(
Index
.Ascending(x => x.SchemaId)
.Descending(x => x.IsLatest)
.Descending(x => x.LastModified));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.ReferencedIds));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Status));
await collection.Indexes.CreateOneAsync(Index.Text(x => x.DataText));
}
public async Task WriteAsync(Guid key, ContentState value, long oldVersion, long newVersion)
{
var documentId = $"{key}_{newVersion}";
var schema = await appProvider.GetSchemaAsync(value.AppId, value.SchemaId, true);
if (schema == null)
{
throw new InvalidOperationException($"Cannot find schema {value.SchemaId}");
}
var idData = value.Data?.ToIdModel(schema.SchemaDef, true);
var document = SimpleMapper.Map(value, new MongoContentEntity
{
DocumentId = documentId,
DataText = idData?.ToFullText(),
DataByIds = idData,
IsLatest = true,
ReferencedIds = idData?.ToReferencedIds(schema.SchemaDef),
});
try
{
await Collection.InsertOneAsync(document);
await Collection.UpdateManyAsync(x => x.Id == value.Id && x.Version < value.Version, Update.Set(x => x.IsLatest, false));
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await Collection.Find(x => x.Id == value.Id && x.IsLatest).Only(x => x.Id, x => x.Version)
.FirstOrDefaultAsync();
if (existingVersion != null)
{
throw new InconsistentStateException(existingVersion["Version"].AsInt64, oldVersion, ex);
}
}
else
{
throw;
}
}
}
public async Task<(ContentState Value, long Version)> ReadAsync(Guid key)
{
var contentEntity =
await Collection.Find(x => x.Id == key && x.IsLatest)
.FirstOrDefaultAsync();
if (contentEntity != null)
{
var schema = await appProvider.GetSchemaAsync(contentEntity.AppId, contentEntity.SchemaId, true);
if (schema == null)
{
throw new InvalidOperationException($"Cannot find schema {contentEntity.SchemaId}");
}
contentEntity?.ParseData(schema.SchemaDef);
return (SimpleMapper.Map(contentEntity, new ContentState()), contentEntity.Version);
}
return (null, EtagVersion.NotFound);
}
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery)
{
IFindFluent<MongoContentEntity, MongoContentEntity> cursor;
try
{
cursor =
Collection
.Find(odataQuery, schema.Id, schema.SchemaDef, status)
.Take(odataQuery)
.Skip(odataQuery)
.Sort(odataQuery, schema.SchemaDef);
}
catch (NotSupportedException)
{
throw new ValidationException("This odata operation is not supported.");
}
catch (NotImplementedException)
{
throw new ValidationException("This odata operation is not supported.");
}
var contentEntities = await cursor.ToListAsync();
foreach (var entity in contentEntities)
{
entity.ParseData(schema.SchemaDef);
}
return contentEntities;
}
public async Task<long> CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery)
{
IFindFluent<MongoContentEntity, MongoContentEntity> cursor;
try
{
cursor = Collection.Find(odataQuery, schema.Id, schema.SchemaDef, status);
}
catch (NotSupportedException)
{
throw new ValidationException("This odata operation is not supported.");
}
catch (NotImplementedException)
{
throw new ValidationException("This odata operation is not supported.");
}
return await cursor.CountAsync();
}
public async Task<long> CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids)
{
var contentsCount =
await Collection.Find(x => ids.Contains(x.Id) && x.IsLatest)
.CountAsync();
return contentsCount;
}
public async Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids)
{
var contentEntities =
await Collection.Find(x => ids.Contains(x.Id) && x.IsLatest)
.ToListAsync();
foreach (var entity in contentEntities)
{
entity.ParseData(schema.SchemaDef);
}
return contentEntities.OfType<IContentEntity>().ToList();
}
public async Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, Guid schemaId, IList<Guid> contentIds)
{
var contentEntities =
await Collection.Find(x => contentIds.Contains(x.Id) && x.AppId == appId).Only(x => x.Id)
.ToListAsync();
return contentIds.Except(contentEntities.Select(x => Guid.Parse(x["_id"].AsString))).ToList();
}
public async Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id, long version)
{
var contentEntity =
await Collection.Find(x => x.Id == id && x.Version >= version).SortBy(x => x.Version)
.FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef);
return contentEntity;
}
public async Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id)
{
var contentEntity =
await Collection.Find(x => x.Id == id && x.IsLatest)
.FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef);
return contentEntity;
}
}
}

58
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_EventHandling.cs

@ -0,0 +1,58 @@
// ==========================================================================
// MongoContentRepository_EventHandling.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
public partial class MongoContentRepository
{
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return "^(content-)|(asset-)"; }
}
public override Task ClearAsync()
{
return TaskHelper.Done;
}
public Task On(Envelope<IEvent> @event)
{
return this.DispatchActionAsync(@event.Payload, @event.Headers);
}
protected Task On(AssetDeleted @event)
{
return Collection.UpdateManyAsync(
Filter.And(
Filter.AnyEq(x => x.ReferencedIds, @event.AssetId),
Filter.AnyNe(x => x.ReferencedIdsDeleted, @event.AssetId)),
Update.AddToSet(x => x.ReferencedIdsDeleted, @event.AssetId));
}
protected Task On(ContentDeleted @event)
{
return Collection.UpdateManyAsync(
Filter.And(
Filter.AnyEq(x => x.ReferencedIds, @event.ContentId),
Filter.AnyNe(x => x.ReferencedIdsDeleted, @event.ContentId)),
Update.AddToSet(x => x.ReferencedIdsDeleted, @event.ContentId));
}
}
}

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/ConstantVisitor.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/ConstantVisitor.cs

@ -12,7 +12,7 @@ using Microsoft.OData.UriParser;
using NodaTime;
using NodaTime.Text;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public sealed class ConstantVisitor : QueryNodeVisitor<object>
{

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FilterBuilder.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterBuilder.cs

@ -12,7 +12,7 @@ using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public static class FilterBuilder
{

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FilterVisitor.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FilterVisitor.cs

@ -13,7 +13,7 @@ using MongoDB.Bson;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public class FilterVisitor : QueryNodeVisitor<FilterDefinition<MongoContentEntity>>
{

3
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/FindExtensions.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs

@ -14,7 +14,7 @@ using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public static class FindExtensions
{
@ -69,6 +69,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
var filters = new List<FilterDefinition<MongoContentEntity>>
{
Filter.Eq(x => x.SchemaId, schemaId),
Filter.Eq(x => x.IsLatest, true),
Filter.In(x => x.Status, status)
};

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

@ -14,7 +14,7 @@ using MongoDB.Driver;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public sealed class PropertyVisitor : QueryNodeVisitor<ImmutableList<string>>
{

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/SearchTermVisitor.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/SearchTermVisitor.cs

@ -9,7 +9,7 @@
using System;
using Microsoft.OData.UriParser;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public class SearchTermVisitor : QueryNodeVisitor<string>
{

2
src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/SortBuilder.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/SortBuilder.cs

@ -11,7 +11,7 @@ using Microsoft.OData.UriParser;
using MongoDB.Driver;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors
{
public static class SortBuilder
{

3
src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs → src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventEntity.cs

@ -12,11 +12,12 @@ using MongoDB.Bson.Serialization.Attributes;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.History
namespace Squidex.Domain.Apps.Entities.MongoDb.History
{
public sealed class MongoHistoryEventEntity : MongoEntity,
IEntity,
IEntityWithAppRef,
IUpdateableEntity,
IUpdateableEntityWithVersion,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithAppRef

26
src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventRepository.cs → src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs

@ -11,13 +11,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Entities.History.Repositories;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Read.History;
using Squidex.Domain.Apps.Read.History.Repositories;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.History
namespace Squidex.Domain.Apps.Entities.MongoDb.History
{
public class MongoHistoryEventRepository : MongoRepositoryBase<MongoHistoryEventEntity>, IHistoryEventRepository, IEventConsumer
{
@ -53,23 +53,22 @@ namespace Squidex.Domain.Apps.Read.MongoDb.History
return "Projections_History";
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoHistoryEventEntity> collection)
protected override async Task SetupCollectionAsync(IMongoCollection<MongoHistoryEventEntity> collection)
{
return Task.WhenAll(
collection.Indexes.CreateOneAsync(
await collection.Indexes.CreateOneAsync(
Index
.Ascending(x => x.AppId)
.Ascending(x => x.Channel)
.Descending(x => x.Created)
.Descending(x => x.Version)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Created), new CreateIndexOptions { ExpireAfter = TimeSpan.FromDays(365) }));
.Descending(x => x.Version));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Created), new CreateIndexOptions { ExpireAfter = TimeSpan.FromDays(365) });
}
public async Task<IReadOnlyList<IHistoryEventEntity>> QueryByChannelAsync(Guid appId, string channelPrefix, int count)
{
var historyEventEntities =
await Collection.Find(x => x.AppId == appId && x.Channel == channelPrefix)
.SortByDescending(x => x.Created).ThenByDescending(x => x.Version).Limit(count)
await Collection.Find(x => x.AppId == appId && x.Channel == channelPrefix).SortByDescending(x => x.Created).ThenByDescending(x => x.Version).Limit(count)
.ToListAsync();
return historyEventEntities.Select(x => (IHistoryEventEntity)new ParsedHistoryEvent(x, texts)).ToList();
@ -91,7 +90,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.History
entity.AppId = appEvent.AppId.Id;
if (@event.Headers.Contains(CommonHeaders.SnapshotVersion))
{
entity.Version = @event.Headers.SnapshotVersion();
}
else
{
entity.Version = @event.Headers.EventStreamNumber();
}
entity.Channel = message.Channel;
entity.Message = message.Message;

4
src/Squidex.Domain.Apps.Read.MongoDb/History/ParsedHistoryEvent.cs → src/Squidex.Domain.Apps.Entities.MongoDb/History/ParsedHistoryEvent.cs

@ -9,12 +9,12 @@
using System;
using System.Collections.Generic;
using NodaTime;
using Squidex.Domain.Apps.Read.History;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Infrastructure;
#pragma warning disable RECS0029 // Warns about property or indexer setters and event adders or removers that do not use the value parameter
namespace Squidex.Domain.Apps.Read.MongoDb.History
namespace Squidex.Domain.Apps.Entities.MongoDb.History
{
internal sealed class ParsedHistoryEvent : IHistoryEventEntity
{

4
src/Squidex.Domain.Apps.Read.MongoDb/MongoCollectionExtensions.cs → src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs

@ -14,13 +14,13 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb
namespace Squidex.Domain.Apps.Entities.MongoDb
{
public static class MongoCollectionExtensions
{
public static Task CreateAsync<T>(this IMongoCollection<T> collection, SquidexEvent @event, EnvelopeHeaders headers, Action<T> updater) where T : class, IEntity, new()
{
var entity = EntityMapper.Create(@event, headers, updater);
var entity = new T().Update(@event, headers, updater);
return collection.InsertOneIfNotExistsAsync(entity);
}

41
src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEntity.cs

@ -0,0 +1,41 @@
// ==========================================================================
// MongoRuleEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
public sealed class MongoRuleEntity
{
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[BsonElement]
[BsonRequired]
public int Version { get; set; }
[BsonElement]
[BsonRequired]
public Guid AppId { get; set; }
[BsonElement]
[BsonRequired]
public bool IsDeleted { get; set; }
[BsonJson]
[BsonElement]
[BsonRequired]
public RuleState State { get; set; }
}
}

6
src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventEntity.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventEntity.cs

@ -11,16 +11,16 @@ using MongoDB.Bson.Serialization.Attributes;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.Rules
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
public sealed class MongoRuleEventEntity : MongoEntity, IRuleEventEntity
{
[BsonRequired]
[BsonElement]
public Guid AppId { get; set; }
public Guid AssetId { get; set; }
[BsonRequired]
[BsonElement]

22
src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventRepository.cs → src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleEventRepository.cs

@ -14,12 +14,12 @@ using MongoDB.Driver;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Read.MongoDb.Rules
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
public sealed class MongoRuleEventRepository : MongoRepositoryBase<MongoRuleEventEntity>, IRuleEventRepository
{
@ -33,12 +33,11 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
return "RuleEvents";
}
protected override Task SetupCollectionAsync(IMongoCollection<MongoRuleEventEntity> collection)
protected override async Task SetupCollectionAsync(IMongoCollection<MongoRuleEventEntity> collection)
{
return Task.WhenAll(
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.NextAttempt)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Descending(x => x.Created)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Expires), new CreateIndexOptions { ExpireAfter = TimeSpan.Zero }));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.NextAttempt));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AssetId).Descending(x => x.Created));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Expires), new CreateIndexOptions { ExpireAfter = TimeSpan.Zero });
}
public Task QueryPendingAsync(Instant now, Func<IRuleEventEntity, Task> callback, CancellationToken cancellationToken = default(CancellationToken))
@ -49,7 +48,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
public async Task<IReadOnlyList<IRuleEventEntity>> QueryByAppAsync(Guid appId, int skip = 0, int take = 20)
{
var ruleEventEntities =
await Collection.Find(x => x.AppId == appId).Skip(skip).Limit(take).SortByDescending(x => x.Created)
await Collection.Find(x => x.AssetId == appId).Skip(skip).Limit(take).SortByDescending(x => x.Created)
.ToListAsync();
return ruleEventEntities;
@ -66,7 +65,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
public async Task<int> CountByAppAsync(Guid appId)
{
return (int)await Collection.CountAsync(x => x.AppId == appId);
return (int)await Collection.CountAsync(x => x.AssetId == appId);
}
public Task EnqueueAsync(Guid id, Instant nextAttempt)
@ -84,7 +83,8 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
public Task MarkSentAsync(Guid jobId, string dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant? nextAttempt)
{
return Collection.UpdateOneAsync(x => x.Id == jobId,
Update.Set(x => x.Result, result)
Update
.Set(x => x.Result, result)
.Set(x => x.LastDump, dump)
.Set(x => x.JobResult, jobResult)
.Set(x => x.NextAttempt, nextAttempt)

95
src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository.cs

@ -0,0 +1,95 @@
// ==========================================================================
// MongoRuleRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
{
public sealed class MongoRuleRepository : MongoRepositoryBase<MongoRuleEntity>, IRuleRepository, ISnapshotStore<RuleState, Guid>
{
public MongoRuleRepository(IMongoDatabase database)
: base(database)
{
}
protected override string CollectionName()
{
return "States_Rules";
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoRuleEntity> collection)
{
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.IsDeleted));
}
public async Task<(RuleState Value, long Version)> ReadAsync(Guid key)
{
var existing =
await Collection.Find(x => x.Id == key)
.FirstOrDefaultAsync();
if (existing != null)
{
return (existing.State, existing.Version);
}
return (null, EtagVersion.NotFound);
}
public async Task<IReadOnlyList<Guid>> QueryRuleIdsAsync(Guid appId)
{
var ruleEntities =
await Collection.Find(x => x.AppId == appId && !x.IsDeleted).Only(x => x.Id)
.ToListAsync();
return ruleEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList();
}
public async Task WriteAsync(Guid key, RuleState value, long oldVersion, long newVersion)
{
try
{
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion,
Update
.Set(x => x.State, value)
.Set(x => x.AppId, value.AppId)
.Set(x => x.IsDeleted, value.IsDeleted)
.Set(x => x.Version, newVersion),
Upsert);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await Collection.Find(x => x.Id == key).Only(x => x.Id, x => x.Version)
.FirstOrDefaultAsync();
if (existingVersion != null)
{
throw new InconsistentStateException(existingVersion["Version"].AsInt64, oldVersion, ex);
}
}
else
{
throw;
}
}
}
}
}

41
src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaEntity.cs

@ -0,0 +1,41 @@
// ==========================================================================
// MongoSchemaEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
{
public sealed class MongoSchemaEntity
{
[BsonId]
[BsonElement]
[BsonRepresentation(BsonType.String)]
public Guid Id { get; set; }
[BsonElement]
[BsonRequired]
public string Name { get; set; }
[BsonElement]
[BsonRequired]
public int Version { get; set; }
[BsonElement]
[BsonRequired]
public Guid AppId { get; set; }
[BsonJson]
[BsonElement]
[BsonRequired]
public SchemaState State { get; set; }
}
}

104
src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs

@ -0,0 +1,104 @@
// ==========================================================================
// MongoSchemaRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Squidex.Domain.Apps.Entities.Schemas.Repositories;
using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Infrastructure;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
{
public sealed class MongoSchemaRepository : MongoRepositoryBase<MongoSchemaEntity>, ISchemaRepository, ISnapshotStore<SchemaState, Guid>
{
public MongoSchemaRepository(IMongoDatabase database)
: base(database)
{
}
protected override string CollectionName()
{
return "States_Schemas";
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection)
{
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId));
await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name));
}
public async Task<(SchemaState Value, long Version)> ReadAsync(Guid key)
{
var existing =
await Collection.Find(x => x.Id == key)
.FirstOrDefaultAsync();
if (existing != null)
{
return (existing.State, existing.Version);
}
return (null, EtagVersion.NotFound);
}
public async Task<Guid> FindSchemaIdAsync(Guid appId, string name)
{
var schemaEntity =
await Collection.Find(x => x.Name == name).Only(x => x.Id)
.FirstOrDefaultAsync();
return schemaEntity != null ? Guid.Parse(schemaEntity["_id"].AsString) : Guid.Empty;
}
public async Task<IReadOnlyList<Guid>> QuerySchemaIdsAsync(Guid appId)
{
var schemaEntities =
await Collection.Find(x => x.AppId == appId).Only(x => x.Id)
.ToListAsync();
return schemaEntities.Select(x => Guid.Parse(x["_id"].AsString)).ToList();
}
public async Task WriteAsync(Guid key, SchemaState value, long oldVersion, long newVersion)
{
try
{
await Collection.UpdateOneAsync(x => x.Id == key && x.Version == oldVersion,
Update
.Set(x => x.State, value)
.Set(x => x.AppId, value.AppId)
.Set(x => x.Name, value.Name)
.Set(x => x.Version, newVersion),
Upsert);
}
catch (MongoWriteException ex)
{
if (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
{
var existingVersion =
await Collection.Find(x => x.Id == key).Only(x => x.Version)
.FirstOrDefaultAsync();
if (existingVersion != null)
{
throw new InconsistentStateException(existingVersion["Version"].AsInt64, oldVersion, ex);
}
}
else
{
throw;
}
}
}
}
}

2
src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj → src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj

@ -12,7 +12,7 @@
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Read\Squidex.Domain.Apps.Read.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.OData.Core" Version="7.3.1" />

2
src/Squidex.Domain.Apps.Write/AppAggregateCommand.cs → src/Squidex.Domain.Apps.Entities/AppAggregateCommand.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write
namespace Squidex.Domain.Apps.Entities
{
public class AppAggregateCommand : AppCommand, IAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/AppCommand.cs → src/Squidex.Domain.Apps.Entities/AppCommand.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write
namespace Squidex.Domain.Apps.Entities
{
public abstract class AppCommand : SquidexCommand
{

150
src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -0,0 +1,150 @@
// ==========================================================================
// AppProvider.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Repositories;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Rules.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities
{
public sealed class AppProvider : IAppProvider
{
private readonly IAppRepository appRepository;
private readonly IRuleRepository ruleRepository;
private readonly ISchemaRepository schemaRepository;
private readonly IStateFactory stateFactory;
public AppProvider(
IAppRepository appRepository,
ISchemaRepository schemaRepository,
IStateFactory stateFactory,
IRuleRepository ruleRepository)
{
Guard.NotNull(appRepository, nameof(appRepository));
Guard.NotNull(schemaRepository, nameof(schemaRepository));
Guard.NotNull(stateFactory, nameof(stateFactory));
Guard.NotNull(ruleRepository, nameof(ruleRepository));
this.appRepository = appRepository;
this.schemaRepository = schemaRepository;
this.stateFactory = stateFactory;
this.ruleRepository = ruleRepository;
}
public async Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid appId, Guid id)
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appId);
if (IsNotFound(app))
{
return (null, null);
}
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
return IsNotFound(false, schema) ? (null, null) : (app.State, schema.State);
}
public async Task<IAppEntity> GetAppAsync(string appName)
{
var appId = await GetAppIdAsync(appName);
if (appId == Guid.Empty)
{
return null;
}
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appId);
return IsNotFound(app) ? null : app.State;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name, bool provideDeleted = false)
{
var schemaId = await GetSchemaIdAsync(appId, name);
if (schemaId == Guid.Empty)
{
return null;
}
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaId);
return IsNotFound(provideDeleted, schema) ? null : schema.State;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool provideDeleted = false)
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
return IsNotFound(provideDeleted, schema) ? null : schema.State;
}
public async Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId)
{
var ids = await schemaRepository.QuerySchemaIdsAsync(appId);
var schemas =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<SchemaDomainObject>(id)));
return schemas.Select(a => (ISchemaEntity)a.State).ToList();
}
public async Task<List<IRuleEntity>> GetRulesAsync(Guid appId)
{
var ids = await ruleRepository.QueryRuleIdsAsync(appId);
var rules =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<RuleDomainObject>(id)));
return rules.Select(a => (IRuleEntity)a.State).ToList();
}
public async Task<List<IAppEntity>> GetUserApps(string userId)
{
var ids = await appRepository.QueryUserAppIdsAsync(userId);
var apps =
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<AppDomainObject>(id)));
return apps.Select(a => (IAppEntity)a.State).ToList();
}
private Task<Guid> GetAppIdAsync(string name)
{
return appRepository.FindAppIdByNameAsync(name);
}
private Task<Guid> GetSchemaIdAsync(Guid appId, string name)
{
return schemaRepository.FindSchemaIdAsync(appId, name);
}
private static bool IsNotFound(AppDomainObject app)
{
return app.Version < 0;
}
private static bool IsNotFound(bool provideDeleted, SchemaDomainObject schema)
{
return schema.Version < 0 || (schema.State.IsDeleted && !provideDeleted);
}
}
}

55
src/Squidex.Domain.Apps.Write/Apps/AppCommandMiddleware.cs → src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs

@ -8,16 +8,15 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Write.Apps.Guards;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Guards;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Write.Apps
namespace Squidex.Domain.Apps.Entities.Apps
{
public class AppCommandMiddleware : ICommandMiddleware
{
@ -47,23 +46,23 @@ namespace Squidex.Domain.Apps.Write.Apps
this.appPlansBillingManager = appPlansBillingManager;
}
protected async Task On(CreateApp command, CommandContext context)
protected Task On(CreateApp command, CommandContext context)
{
await handler.CreateAsync<AppDomainObject>(context, async a =>
return handler.CreateSyncedAsync<AppDomainObject>(context, async a =>
{
await GuardApp.CanCreate(command, appProvider);
a.Create(command);
context.Complete(EntityCreatedResult.Create(a.Id, a.Version));
context.Complete(EntityCreatedResult.Create(command.AppId, a.Version));
});
}
protected async Task On(AssignContributor command, CommandContext context)
protected Task On(AssignContributor command, CommandContext context)
{
await handler.UpdateAsync<AppDomainObject>(context, async a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
await GuardAppContributors.CanAssign(a.Contributors, command, userResolver, appPlansProvider.GetPlan(a.Plan?.PlanId));
await GuardAppContributors.CanAssign(a.State.Contributors, command, userResolver, appPlansProvider.GetPlan(a.State.Plan?.PlanId));
a.AssignContributor(command);
});
@ -71,9 +70,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(RemoveContributor command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppContributors.CanRemove(a.Contributors, command);
GuardAppContributors.CanRemove(a.State.Contributors, command);
a.RemoveContributor(command);
});
@ -81,9 +80,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(AttachClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanAttach(a.Clients, command);
GuardAppClients.CanAttach(a.State.Clients, command);
a.AttachClient(command);
});
@ -91,9 +90,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(UpdateClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanUpdate(a.Clients, command);
GuardAppClients.CanUpdate(a.State.Clients, command);
a.UpdateClient(command);
});
@ -101,9 +100,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(RevokeClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanRevoke(a.Clients, command);
GuardAppClients.CanRevoke(a.State.Clients, command);
a.RevokeClient(command);
});
@ -111,9 +110,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(AddLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanAdd(a.LanguagesConfig, command);
GuardAppLanguages.CanAdd(a.State.LanguagesConfig, command);
a.AddLanguage(command);
});
@ -121,9 +120,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(RemoveLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanRemove(a.LanguagesConfig, command);
GuardAppLanguages.CanRemove(a.State.LanguagesConfig, command);
a.RemoveLanguage(command);
});
@ -131,9 +130,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(UpdateLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanUpdate(a.LanguagesConfig, command);
GuardAppLanguages.CanUpdate(a.State.LanguagesConfig, command);
a.UpdateLanguage(command);
});
@ -141,9 +140,9 @@ namespace Squidex.Domain.Apps.Write.Apps
protected Task On(ChangePlan command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, async a =>
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
GuardApp.CanChangePlan(command, a.Plan, appPlansProvider);
GuardApp.CanChangePlan(command, a.State.Plan, appPlansProvider);
if (command.FromCallback)
{
@ -151,7 +150,7 @@ namespace Squidex.Domain.Apps.Write.Apps
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, a.Id, a.Name, command.PlanId);
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, command.AppId.Id, a.State.Name, command.PlanId);
if (result is PlanChangedResult)
{

142
src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs → src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs

@ -8,130 +8,41 @@
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Utils;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Write.Apps
namespace Squidex.Domain.Apps.Entities.Apps
{
public class AppDomainObject : DomainObjectBase
public sealed class AppDomainObject : DomainObjectBase<AppState>
{
private AppContributors contributors = AppContributors.Empty;
private AppClients clients = AppClients.Empty;
private LanguagesConfig languagesConfig = LanguagesConfig.English;
private AppPlan plan;
private string name;
public string Name
{
get { return name; }
}
public AppPlan Plan
{
get { return plan; }
}
public AppClients Clients
{
get { return clients; }
}
public AppContributors Contributors
{
get { return contributors; }
}
public LanguagesConfig LanguagesConfig
{
get { return languagesConfig; }
}
public AppDomainObject(Guid id, int version)
: base(id, version)
{
}
protected void On(AppCreated @event)
{
name = @event.Name;
}
protected void On(AppContributorAssigned @event)
{
contributors = contributors.Apply(@event);
}
protected void On(AppContributorRemoved @event)
{
contributors = contributors.Apply(@event);
}
protected void On(AppClientAttached @event)
{
clients = clients.Apply(@event);
}
protected void On(AppClientUpdated @event)
{
clients = clients.Apply(@event);
}
protected void On(AppClientRenamed @event)
{
clients = clients.Apply(@event);
}
protected void On(AppClientRevoked @event)
{
clients = clients.Apply(@event);
}
protected void On(AppLanguageAdded @event)
{
languagesConfig = languagesConfig.Apply(@event);
}
protected void On(AppLanguageRemoved @event)
{
languagesConfig = languagesConfig.Apply(@event);
}
protected void On(AppLanguageUpdated @event)
{
languagesConfig = languagesConfig.Apply(@event);
}
protected void On(AppPlanChanged @event)
{
plan = string.IsNullOrWhiteSpace(@event.PlanId) ? null : new AppPlan(@event.Actor, @event.PlanId);
}
protected override void DispatchEvent(Envelope<IEvent> @event)
{
this.DispatchAction(@event.Payload);
}
public AppDomainObject Create(CreateApp command)
{
ThrowIfCreated();
var appId = new NamedId<Guid>(command.AppId, command.Name);
RaiseEvent(SimpleMapper.Map(command, new AppCreated { AppId = appId }));
RaiseEvent(SimpleMapper.Map(command, CreateInitalEvent(appId)));
RaiseEvent(SimpleMapper.Map(command, CreateInitialOwner(appId, command)));
RaiseEvent(SimpleMapper.Map(command, CreateInitialLanguage(appId)));
return this;
}
public AppDomainObject UpdateLanguage(UpdateLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
return this;
}
public AppDomainObject UpdateClient(UpdateClient command)
{
ThrowIfNotCreated();
@ -203,15 +114,6 @@ namespace Squidex.Domain.Apps.Write.Apps
return this;
}
public AppDomainObject UpdateLanguage(UpdateLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
return this;
}
public AppDomainObject ChangePlan(ChangePlan command)
{
ThrowIfNotCreated();
@ -225,12 +127,17 @@ namespace Squidex.Domain.Apps.Write.Apps
{
if (@event.AppId == null)
{
@event.AppId = new NamedId<Guid>(Id, name);
@event.AppId = new NamedId<Guid>(State.Id, State.Name);
}
RaiseEvent(Envelope.Create(@event));
}
private static AppCreated CreateInitalEvent(NamedId<Guid> appId)
{
return new AppCreated { AppId = appId };
}
private static AppLanguageAdded CreateInitialLanguage(NamedId<Guid> id)
{
return new AppLanguageAdded { AppId = id, Language = Language.EN };
@ -243,7 +150,7 @@ namespace Squidex.Domain.Apps.Write.Apps
private void ThrowIfNotCreated()
{
if (string.IsNullOrWhiteSpace(name))
if (string.IsNullOrWhiteSpace(State.Name))
{
throw new DomainException("App has not been created.");
}
@ -251,10 +158,15 @@ namespace Squidex.Domain.Apps.Write.Apps
private void ThrowIfCreated()
{
if (!string.IsNullOrWhiteSpace(name))
if (!string.IsNullOrWhiteSpace(State.Name))
{
throw new DomainException("App has already been created.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event));
}
}
}

2
src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs → src/Squidex.Domain.Apps.Entities/Apps/AppEntityExtensions.cs

@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core;
namespace Squidex.Domain.Apps.Read.Apps
namespace Squidex.Domain.Apps.Entities.Apps
{
public static class AppEntityExtensions
{

22
src/Squidex.Domain.Apps.Read/Apps/AppHistoryEventsCreator.cs → src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs

@ -7,13 +7,13 @@
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.History;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.Apps
namespace Squidex.Domain.Apps.Entities.Apps
{
public class AppHistoryEventsCreator : HistoryEventsCreatorBase
{
@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Read.Apps
"changed master language to {[Language]}");
}
protected Task<HistoryEventToStore> On(AppContributorRemoved @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppContributorRemoved @event)
{
const string channel = "settings.contributors";
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Contributor", @event.ContributorId));
}
protected Task<HistoryEventToStore> On(AppContributorAssigned @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppContributorAssigned @event)
{
const string channel = "settings.contributors";
@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission));
}
protected Task<HistoryEventToStore> On(AppClientAttached @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppClientAttached @event)
{
const string channel = "settings.clients";
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Id", @event.Id));
}
protected Task<HistoryEventToStore> On(AppClientRevoked @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppClientRevoked @event)
{
const string channel = "settings.clients";
@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Id", @event.Id));
}
protected Task<HistoryEventToStore> On(AppClientRenamed @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppClientRenamed @event)
{
const string channel = "settings.clients";
@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Id", @event.Id).AddParameter("Name", ClientName(@event)));
}
protected Task<HistoryEventToStore> On(AppLanguageAdded @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppLanguageAdded @event)
{
const string channel = "settings.languages";
@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppLanguageRemoved @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppLanguageRemoved @event)
{
const string channel = "settings.languages";
@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppLanguageUpdated @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppLanguageUpdated @event)
{
const string channel = "settings.languages";
@ -123,7 +123,7 @@ namespace Squidex.Domain.Apps.Read.Apps
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppMasterLanguageSet @event, EnvelopeHeaders headers)
protected Task<HistoryEventToStore> On(AppMasterLanguageSet @event)
{
const string channel = "settings.languages";

2
src/Squidex.Domain.Apps.Write/Apps/Commands/AddLanguage.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/AddLanguage.cs

@ -8,7 +8,7 @@
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AddLanguage : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/AssignContributor.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/AssignContributor.cs

@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AssignContributor : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/AttachClient.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/AttachClient.cs

@ -8,7 +8,7 @@
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AttachClient : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/ChangePlan.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/ChangePlan.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class ChangePlan : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/CreateApp.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/CreateApp.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class CreateApp : SquidexCommand, IAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveContributor.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveContributor.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class RemoveContributor : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveLanguage.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/RemoveLanguage.cs

@ -8,7 +8,7 @@
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class RemoveLanguage : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/RevokeClient.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/RevokeClient.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class RevokeClient : AppAggregateCommand
{

4
src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateClient.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateClient.cs

@ -1,5 +1,5 @@
// ==========================================================================
// RenameClient.cs
// UpdateClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class UpdateClient : AppAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateLanguage.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateLanguage.cs

@ -9,7 +9,7 @@
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class UpdateLanguage : AppAggregateCommand
{

7
src/Squidex.Domain.Apps.Write/Apps/Guards/GuardApp.cs → src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs

@ -9,12 +9,11 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Guards
namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public static class GuardApp
{

4
src/Squidex.Domain.Apps.Write/Apps/Guards/GuardAppClients.cs → src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs

@ -7,10 +7,10 @@
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Guards
namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public static class GuardAppClients
{

8
src/Squidex.Domain.Apps.Write/Apps/Guards/GuardAppContributors.cs → src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs

@ -1,5 +1,5 @@
// ==========================================================================
// GuardApp.cs
// GuardAppContributors.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -9,12 +9,12 @@
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps.Services;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Write.Apps.Guards
namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public static class GuardAppContributors
{

6
src/Squidex.Domain.Apps.Write/Apps/Guards/GuardAppLanguages.cs → src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs

@ -1,5 +1,5 @@
// ==========================================================================
// GuardApp.cs
// GuardAppLanguages.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -7,10 +7,10 @@
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Write.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Guards
namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public static class GuardAppLanguages
{

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

@ -8,17 +8,13 @@
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Read.Apps
namespace Squidex.Domain.Apps.Entities.Apps
{
public interface IAppEntity : IEntity, IEntityWithVersion
{
string Etag { get; }
string Name { get; }
string PlanId { get; }
string PlanOwner { get; }
AppPlan Plan { get; }
AppClients Clients { get; }

21
src/Squidex.Domain.Apps.Entities/Apps/Repositories/IAppRepository.cs

@ -0,0 +1,21 @@
// ==========================================================================
// IAppRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Entities.Apps.Repositories
{
public interface IAppRepository
{
Task<Guid> FindAppIdByNameAsync(string name);
Task<IReadOnlyList<Guid>> QueryUserAppIdsAsync(string userId);
}
}

2
src/Squidex.Domain.Apps.Read/Apps/Services/IAppLimitsPlan.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/IAppLimitsPlan.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public interface IAppLimitsPlan
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlanBillingManager.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlanBillingManager.cs

@ -9,7 +9,7 @@
using System;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public interface IAppPlanBillingManager
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlansProvider.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/IAppPlansProvider.cs

@ -8,7 +8,7 @@
using System.Collections.Generic;
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public interface IAppPlansProvider
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/IChangePlanResult.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/IChangePlanResult.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public interface IChangePlanResult
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppLimitsPlan.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppLimitsPlan.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations
{
public sealed class ConfigAppLimitsPlan : IAppLimitsPlan
{

8
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppPlansProvider.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/ConfigAppPlansProvider.cs

@ -1,5 +1,5 @@
// ==========================================================================
// ConfigAppLimitsProvider.cs
// ConfigAppPlansProvider.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@ -11,7 +11,7 @@ using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations
{
public sealed class ConfigAppPlansProvider : IAppPlansProvider
{
@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{
Guard.NotNull(app, nameof(app));
return GetPlan(app.PlanId);
return GetPlan(app.Plan?.PlanId);
}
public IAppLimitsPlan GetPlan(string planId)
@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{
Guard.NotNull(app, nameof(app));
return GetPlanUpgrade(app.PlanId);
return GetPlanUpgrade(app.Plan?.PlanId);
}
public IAppLimitsPlan GetPlanUpgrade(string planId)

2
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/NoopAppPlanBillingManager.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/Implementations/NoopAppPlanBillingManager.cs

@ -9,7 +9,7 @@
using System;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
namespace Squidex.Domain.Apps.Entities.Apps.Services.Implementations
{
public sealed class NoopAppPlanBillingManager : IAppPlanBillingManager
{

3
src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangeAsyncResult.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/PlanChangeAsyncResult.cs

@ -5,7 +5,8 @@
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public sealed class PlanChangeAsyncResult : IChangePlanResult
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangedResult.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/PlanChangedResult.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public sealed class PlanChangedResult : IChangePlanResult
{

2
src/Squidex.Domain.Apps.Read/Apps/Services/RedirectToCheckoutResult.cs → src/Squidex.Domain.Apps.Entities/Apps/Services/RedirectToCheckoutResult.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Services
namespace Squidex.Domain.Apps.Entities.Apps.Services
{
public sealed class RedirectToCheckoutResult : IChangePlanResult
{

107
src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs

@ -0,0 +1,107 @@
// ==========================================================================
// AppState.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Apps.State
{
public class AppState : DomainObjectState<AppState>,
IAppEntity
{
private static readonly LanguagesConfig English = LanguagesConfig.Build(Language.EN);
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public AppPlan Plan { get; set; }
[JsonProperty]
public AppClients Clients { get; set; } = AppClients.Empty;
[JsonProperty]
public AppContributors Contributors { get; set; } = AppContributors.Empty;
[JsonProperty]
public LanguagesConfig LanguagesConfig { get; set; } = English;
protected void On(AppCreated @event)
{
SimpleMapper.Map(@event, this);
}
protected void On(AppPlanChanged @event)
{
Plan = @event.PlanId == null ? null : new AppPlan(@event.Actor, @event.PlanId);
}
protected void On(AppContributorAssigned @event)
{
Contributors = Contributors.Assign(@event.ContributorId, @event.Permission);
}
protected void On(AppContributorRemoved @event)
{
Contributors = Contributors.Remove(@event.ContributorId);
}
protected void On(AppClientAttached @event)
{
Clients = Clients.Add(@event.Id, @event.Secret);
}
protected void On(AppClientUpdated @event)
{
Clients = Clients.Update(@event.Id, @event.Permission);
}
protected void On(AppClientRenamed @event)
{
Clients = Clients.Rename(@event.Id, @event.Name);
}
protected void On(AppClientRevoked @event)
{
Clients = Clients.Revoke(@event.Id);
}
protected void On(AppLanguageAdded @event)
{
LanguagesConfig = LanguagesConfig.Set(new LanguageConfig(@event.Language));
}
protected void On(AppLanguageRemoved @event)
{
LanguagesConfig = LanguagesConfig.Remove(@event.Language);
}
protected void On(AppLanguageUpdated @event)
{
LanguagesConfig = LanguagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, @event.Fallback));
if (@event.IsMaster)
{
LanguagesConfig = LanguagesConfig.MakeMaster(@event.Language);
}
}
public AppState Apply(Envelope<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
}
}

24
src/Squidex.Domain.Apps.Write/Assets/AssetCommandMiddleware.cs → src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -8,14 +8,14 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Write.Assets.Commands;
using Squidex.Domain.Apps.Write.Assets.Guards;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.Guards;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Write.Assets
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetCommandMiddleware : ICommandMiddleware
{
@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Write.Assets
command.ImageInfo = await assetThumbnailGenerator.GetImageInfoAsync(command.File.OpenRead());
try
{
var asset = await handler.CreateAsync<AssetDomainObject>(context, async a =>
var asset = await handler.CreateSyncedAsync<AssetDomainObject>(context, async a =>
{
GuardAsset.CanCreate(command);
@ -50,10 +50,10 @@ namespace Squidex.Domain.Apps.Write.Assets
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
context.Complete(EntityCreatedResult.Create(a.Id, a.Version));
context.Complete(EntityCreatedResult.Create(command.AssetId, a.Version));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), asset.Id.ToString(), asset.FileVersion, null);
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.State.FileVersion, null);
}
finally
{
@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Write.Assets
try
{
var asset = await handler.UpdateAsync<AssetDomainObject>(context, async a =>
var asset = await handler.UpdateSyncedAsync<AssetDomainObject>(context, async a =>
{
GuardAsset.CanUpdate(command);
@ -75,10 +75,10 @@ namespace Squidex.Domain.Apps.Write.Assets
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
context.Complete(new AssetSavedResult(a.Version, a.FileVersion));
context.Complete(new AssetSavedResult(a.Version, a.State.FileVersion));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), asset.Id.ToString(), asset.FileVersion, null);
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.State.FileVersion, null);
}
finally
{
@ -88,9 +88,9 @@ namespace Squidex.Domain.Apps.Write.Assets
protected Task On(RenameAsset command, CommandContext context)
{
return handler.UpdateAsync<AssetDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AssetDomainObject>(context, a =>
{
GuardAsset.CanRename(command, a.FileName);
GuardAsset.CanRename(command, a.State.FileName);
a.Rename(command);
});
@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Write.Assets
protected Task On(DeleteAsset command, CommandContext context)
{
return handler.UpdateAsync<AssetDomainObject>(context, a =>
return handler.UpdateSyncedAsync<AssetDomainObject>(context, a =>
{
GuardAsset.CanDelete(command);

73
src/Squidex.Domain.Apps.Write/Assets/AssetDomainObject.cs → src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs

@ -6,69 +6,18 @@
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Write.Assets.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Write.Assets
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetDomainObject : DomainObjectBase
public sealed class AssetDomainObject : DomainObjectBase<AssetState>
{
private bool isDeleted;
private long fileVersion = -1;
private long totalSize;
private string fileName;
public bool IsDeleted
{
get { return isDeleted; }
}
public long FileVersion
{
get { return fileVersion; }
}
public string FileName
{
get { return fileName; }
}
public AssetDomainObject(Guid id, int version)
: base(id, version)
{
}
protected void On(AssetCreated @event)
{
fileVersion = @event.FileVersion;
fileName = @event.FileName;
totalSize += @event.FileSize;
}
protected void On(AssetUpdated @event)
{
fileVersion = @event.FileVersion;
totalSize += @event.FileSize;
}
protected void On(AssetRenamed @event)
{
fileName = @event.FileName;
}
protected void On(AssetDeleted @event)
{
isDeleted = true;
}
public AssetDomainObject Create(CreateAsset command)
{
VerifyNotCreated();
@ -77,7 +26,7 @@ namespace Squidex.Domain.Apps.Write.Assets
{
FileName = command.File.FileName,
FileSize = command.File.FileSize,
FileVersion = fileVersion + 1,
FileVersion = 0,
MimeType = command.File.MimeType,
PixelWidth = command.ImageInfo?.PixelWidth,
PixelHeight = command.ImageInfo?.PixelHeight,
@ -95,7 +44,7 @@ namespace Squidex.Domain.Apps.Write.Assets
var @event = SimpleMapper.Map(command, new AssetUpdated
{
FileVersion = fileVersion + 1,
FileVersion = State.FileVersion + 1,
FileSize = command.File.FileSize,
MimeType = command.File.MimeType,
PixelWidth = command.ImageInfo?.PixelWidth,
@ -112,7 +61,7 @@ namespace Squidex.Domain.Apps.Write.Assets
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = totalSize }));
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = State.TotalSize }));
return this;
}
@ -128,7 +77,7 @@ namespace Squidex.Domain.Apps.Write.Assets
private void VerifyNotCreated()
{
if (!string.IsNullOrWhiteSpace(fileName))
if (!string.IsNullOrWhiteSpace(State.FileName))
{
throw new DomainException("Asset has already been created.");
}
@ -136,15 +85,15 @@ namespace Squidex.Domain.Apps.Write.Assets
private void VerifyCreatedAndNotDeleted()
{
if (isDeleted || string.IsNullOrWhiteSpace(fileName))
if (State.IsDeleted || string.IsNullOrWhiteSpace(State.FileName))
{
throw new DomainException("Asset has already been deleted or not created yet.");
}
}
protected override void DispatchEvent(Envelope<IEvent> @event)
protected override void OnRaised(Envelope<IEvent> @event)
{
this.DispatchAction(@event.Payload);
UpdateState(State.Apply(@event));
}
}
}

2
src/Squidex.Domain.Apps.Write/Assets/AssetSavedResult.cs → src/Squidex.Domain.Apps.Entities/Assets/AssetSavedResult.cs

@ -8,7 +8,7 @@
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Assets
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetSavedResult : EntitySavedResult
{

2
src/Squidex.Domain.Apps.Write/Assets/Commands/AssetAggregateCommand.cs → src/Squidex.Domain.Apps.Entities/Assets/Commands/AssetAggregateCommand.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Assets.Commands
namespace Squidex.Domain.Apps.Entities.Assets.Commands
{
public abstract class AssetAggregateCommand : AppCommand, IAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Assets/Commands/CreateAsset.cs → src/Squidex.Domain.Apps.Entities/Assets/Commands/CreateAsset.cs

@ -9,7 +9,7 @@
using System;
using Squidex.Infrastructure.Assets;
namespace Squidex.Domain.Apps.Write.Assets.Commands
namespace Squidex.Domain.Apps.Entities.Assets.Commands
{
public sealed class CreateAsset : AssetAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Assets/Commands/DeleteAsset.cs → src/Squidex.Domain.Apps.Entities/Assets/Commands/DeleteAsset.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Assets.Commands
namespace Squidex.Domain.Apps.Entities.Assets.Commands
{
public sealed class DeleteAsset : AssetAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Assets/Commands/RenameAsset.cs → src/Squidex.Domain.Apps.Entities/Assets/Commands/RenameAsset.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Assets.Commands
namespace Squidex.Domain.Apps.Entities.Assets.Commands
{
public sealed class RenameAsset : AssetAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Assets/Commands/UpdateAsset.cs → src/Squidex.Domain.Apps.Entities/Assets/Commands/UpdateAsset.cs

@ -8,7 +8,7 @@
using Squidex.Infrastructure.Assets;
namespace Squidex.Domain.Apps.Write.Assets.Commands
namespace Squidex.Domain.Apps.Entities.Assets.Commands
{
public sealed class UpdateAsset : AssetAggregateCommand
{

4
src/Squidex.Domain.Apps.Write/Assets/Guards/GuardAsset.cs → src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs

@ -6,10 +6,10 @@
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Write.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Assets.Guards
namespace Squidex.Domain.Apps.Entities.Assets.Guards
{
public static class GuardAsset
{

2
src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs → src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs

@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core.ValidateContent;
namespace Squidex.Domain.Apps.Read.Assets
namespace Squidex.Domain.Apps.Entities.Assets
{
public interface IAssetEntity :
IEntity,

2
src/Squidex.Domain.Apps.Read/Assets/IAssetStatsEntity.cs → src/Squidex.Domain.Apps.Entities/Assets/IAssetStatsEntity.cs

@ -8,7 +8,7 @@
using System;
namespace Squidex.Domain.Apps.Read.Assets
namespace Squidex.Domain.Apps.Entities.Assets
{
public interface IAssetStatsEntity
{

2
src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetRepository.cs → src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs

@ -10,7 +10,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Assets.Repositories
namespace Squidex.Domain.Apps.Entities.Assets.Repositories
{
public interface IAssetRepository
{

2
src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetStatsRepository.cs → src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetStatsRepository.cs

@ -10,7 +10,7 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Assets.Repositories
namespace Squidex.Domain.Apps.Entities.Assets.Repositories
{
public interface IAssetStatsRepository
{

91
src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs

@ -0,0 +1,91 @@
// ==========================================================================
// AssetState.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Assets.State
{
public class AssetState : DomainObjectState<AssetState>,
IAssetEntity,
IAssetInfo,
IUpdateableEntityWithAppRef
{
[JsonProperty]
public Guid AppId { get; set; }
[JsonProperty]
public string FileName { get; set; }
[JsonProperty]
public string MimeType { get; set; }
[JsonProperty]
public long FileVersion { get; set; }
[JsonProperty]
public long FileSize { get; set; }
[JsonProperty]
public long TotalSize { get; set; }
[JsonProperty]
public bool IsImage { get; set; }
[JsonProperty]
public int? PixelWidth { get; set; }
[JsonProperty]
public int? PixelHeight { get; set; }
[JsonProperty]
public bool IsDeleted { get; set; }
Guid IAssetInfo.AssetId
{
get { return Id; }
}
protected void On(AssetCreated @event)
{
SimpleMapper.Map(@event, this);
TotalSize += @event.FileSize;
}
protected void On(AssetUpdated @event)
{
SimpleMapper.Map(@event, this);
TotalSize += @event.FileSize;
}
protected void On(AssetRenamed @event)
{
FileName = @event.FileName;
}
protected void On(AssetDeleted @event)
{
IsDeleted = true;
}
public AssetState Apply(Envelope<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
}
}

2
src/Squidex.Domain.Apps.Read/CachingProviderBase.cs → src/Squidex.Domain.Apps.Entities/CachingProviderBase.cs

@ -9,7 +9,7 @@
using Microsoft.Extensions.Caching.Memory;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
namespace Squidex.Domain.Apps.Entities
{
public abstract class CachingProviderBase
{

2
src/Squidex.Domain.Apps.Write/Contents/Commands/ChangeContentStatus.cs → src/Squidex.Domain.Apps.Entities/Contents/Commands/ChangeContentStatus.cs

@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public sealed class ChangeContentStatus : ContentCommand
{

2
src/Squidex.Domain.Apps.Write/Contents/Commands/ContentCommand.cs → src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentCommand.cs

@ -10,7 +10,7 @@ using System;
using System.Security.Claims;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Contents.Commands
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public abstract class ContentCommand : SchemaCommand, IAggregateCommand
{

2
src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs → src/Squidex.Domain.Apps.Entities/Contents/Commands/ContentDataCommand.cs

@ -8,7 +8,7 @@
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Write.Contents.Commands
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public abstract class ContentDataCommand : ContentCommand
{

2
src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs → src/Squidex.Domain.Apps.Entities/Contents/Commands/CreateContent.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Contents.Commands
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public sealed class CreateContent : ContentDataCommand
{

2
src/Squidex.Domain.Apps.Write/Contents/Commands/DeleteContent.cs → src/Squidex.Domain.Apps.Entities/Contents/Commands/DeleteContent.cs

@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Contents.Commands
namespace Squidex.Domain.Apps.Entities.Contents.Commands
{
public sealed class DeleteContent : ContentCommand
{

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

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

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

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

17
src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs

@ -9,16 +9,15 @@
using System;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Read;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Domain.Apps.Write.Contents.Guards;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Guards;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
namespace Squidex.Domain.Apps.Write.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public class ContentCommandMiddleware : ICommandMiddleware
{
@ -84,7 +83,7 @@ namespace Squidex.Domain.Apps.Write.Contents
content.Update(command);
context.Complete(new ContentDataChangedResult(content.Data, content.Version));
context.Complete(new ContentDataChangedResult(content.State.Data, content.Version));
});
}
@ -101,7 +100,7 @@ namespace Squidex.Domain.Apps.Write.Contents
content.Patch(command);
context.Complete(new ContentDataChangedResult(content.Data, content.Version));
context.Complete(new ContentDataChangedResult(content.State.Data, content.Version));
});
}
@ -109,7 +108,7 @@ namespace Squidex.Domain.Apps.Write.Contents
{
return handler.UpdateAsync<ContentDomainObject>(context, async content =>
{
GuardContent.CanChangeContentStatus(content.Status, command);
GuardContent.CanChangeContentStatus(content.State.Status, command);
var operationContext = await CreateContext(command, content, () => "Failed to patch content.");

2
src/Squidex.Domain.Apps.Write/Contents/ContentDataChangedResult.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentDataChangedResult.cs

@ -9,7 +9,7 @@
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentDataChangedResult : EntitySavedResult
{

70
src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs

@ -6,67 +6,19 @@
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Write.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public class ContentDomainObject : DomainObjectBase
public sealed class ContentDomainObject : DomainObjectBase<ContentState>
{
private bool isDeleted;
private bool isCreated;
private Status status;
private NamedContentData data;
public bool IsDeleted
{
get { return isDeleted; }
}
public Status Status
{
get { return status; }
}
public NamedContentData Data
{
get { return data; }
}
public ContentDomainObject(Guid id, int version)
: base(id, version)
{
}
protected void On(ContentCreated @event)
{
isCreated = true;
data = @event.Data;
}
protected void On(ContentUpdated @event)
{
data = @event.Data;
}
protected void On(ContentStatusChanged @event)
{
status = @event.Status;
}
protected void On(ContentDeleted @event)
{
isDeleted = true;
}
public ContentDomainObject Create(CreateContent command)
{
VerifyNotCreated();
@ -103,7 +55,7 @@ namespace Squidex.Domain.Apps.Write.Contents
{
VerifyCreatedAndNotDeleted();
if (!command.Data.Equals(Data))
if (!command.Data.Equals(State.Data))
{
RaiseEvent(SimpleMapper.Map(command, new ContentUpdated()));
}
@ -115,9 +67,9 @@ namespace Squidex.Domain.Apps.Write.Contents
{
VerifyCreatedAndNotDeleted();
var newData = command.Data.MergeInto(Data);
var newData = command.Data.MergeInto(State.Data);
if (!newData.Equals(Data))
if (!newData.Equals(State.Data))
{
var @event = SimpleMapper.Map(command, new ContentUpdated());
@ -131,7 +83,7 @@ namespace Squidex.Domain.Apps.Write.Contents
private void VerifyNotCreated()
{
if (isCreated)
if (State.Data != null)
{
throw new DomainException("Content has already been created.");
}
@ -139,15 +91,15 @@ namespace Squidex.Domain.Apps.Write.Contents
private void VerifyCreatedAndNotDeleted()
{
if (isDeleted || !isCreated)
if (State.IsDeleted || State.Data == null)
{
throw new DomainException("Content has already been deleted or not created yet.");
}
}
protected override void DispatchEvent(Envelope<IEvent> @event)
protected override void OnRaised(Envelope<IEvent> @event)
{
this.DispatchAction(@event.Payload);
UpdateState(State.Apply(@event));
}
}
}

4
src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentHistoryEventsCreator.cs

@ -7,12 +7,12 @@
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Entities.History;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Read.History;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentHistoryEventsCreator : HistoryEventsCreatorBase
{

42
src/Squidex.Domain.Apps.Write/Contents/ContentOperationContext.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs

@ -13,18 +13,15 @@ using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.EnrichContent;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Read;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Write.Contents.Commands;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Tasks;
#pragma warning disable IDE0017 // Simplify object initialization
namespace Squidex.Domain.Apps.Write.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentOperationContext
{
@ -46,18 +43,19 @@ namespace Squidex.Domain.Apps.Write.Contents
IScriptEngine scriptEngine,
Func<string> message)
{
var (appEntity, schemaEntity) = await appProvider.GetAppWithSchemaAsync(command.AppId.Name, command.SchemaId.Id);
var context = new ContentOperationContext();
var (appEntity, schemaEntity) = await appProvider.GetAppWithSchemaAsync(command.AppId.Id, command.SchemaId.Id);
context.appEntity = appEntity;
context.assetRepository = assetRepository;
context.contentRepository = contentRepository;
context.content = content;
context.command = command;
context.message = message;
context.schemaEntity = schemaEntity;
context.scriptEngine = scriptEngine;
var context = new ContentOperationContext
{
appEntity = appEntity,
assetRepository = assetRepository,
contentRepository = contentRepository,
content = content,
command = command,
message = message,
schemaEntity = schemaEntity,
scriptEngine = scriptEngine
};
return context;
}
@ -121,7 +119,7 @@ namespace Squidex.Domain.Apps.Write.Contents
{
if (command is ContentDataCommand dataCommand)
{
var ctx = new ScriptContext { ContentId = content.Id, OldData = content.Data, User = command.User, Operation = operation.ToString(), Data = dataCommand.Data };
var ctx = new ScriptContext { ContentId = content.State.Id, OldData = content.State.Data, User = command.User, Operation = operation.ToString(), Data = dataCommand.Data };
dataCommand.Data = scriptEngine.ExecuteAndTransform(ctx, script(schemaEntity));
}
@ -131,7 +129,7 @@ namespace Squidex.Domain.Apps.Write.Contents
public Task ExecuteScriptAsync(Func<ISchemaEntity, string> script, object operation)
{
var ctx = new ScriptContext { ContentId = content.Id, OldData = content.Data, User = command.User, Operation = operation.ToString() };
var ctx = new ScriptContext { ContentId = content.State.Id, OldData = content.State.Data, User = command.User, Operation = operation.ToString() };
scriptEngine.Execute(ctx, script(schemaEntity));

21
src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs → src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs

@ -16,15 +16,15 @@ using Microsoft.OData.UriParser;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Contents.Edm;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents.Edm;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
namespace Squidex.Domain.Apps.Read.Contents
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentQueryService : IContentQueryService
{
@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Read.Contents
this.modelBuilder = modelBuilder;
}
public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id)
public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id, long version = -1)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(user, nameof(user));
@ -60,7 +60,10 @@ namespace Squidex.Domain.Apps.Read.Contents
var schema = await FindSchemaAsync(app, schemaIdOrName);
var content = await contentRepository.FindContentAsync(app, schema, id);
var content =
version > EtagVersion.Empty ?
await contentRepository.FindContentAsync(app, schema, id, version) :
await contentRepository.FindContentAsync(app, schema, id);
if (content == null || (content.Status != Status.Published && !isFrontendClient))
{
@ -155,12 +158,12 @@ namespace Squidex.Domain.Apps.Read.Contents
if (Guid.TryParse(schemaIdOrName, out var id))
{
schema = await appProvider.GetSchemaAsync(app.Name, id);
schema = await appProvider.GetSchemaAsync(app.Id, id);
}
if (schema == null)
{
schema = await appProvider.GetSchemaAsync(app.Name, schemaIdOrName);
schema = await appProvider.GetSchemaAsync(app.Id, schemaIdOrName);
}
if (schema == null)

6
src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs → src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelBuilder.cs

@ -12,11 +12,11 @@ using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.Edm
namespace Squidex.Domain.Apps.Entities.Contents.Edm
{
public class EdmModelBuilder : CachingProviderBase
{

2
src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelExtensions.cs → src/Squidex.Domain.Apps.Entities/Contents/Edm/EdmModelExtensions.cs

@ -11,7 +11,7 @@ using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;
namespace Squidex.Domain.Apps.Read.Contents.Edm
namespace Squidex.Domain.Apps.Entities.Contents.Edm
{
public static class EdmModelExtensions
{

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save