Browse Source

Logging cleaned.

pull/206/head
Sebastian Stehle 8 years ago
parent
commit
9a2e531c72
  1. 86
      Squidex.sln
  2. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Apps/MongoAppRepository.cs
  3. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  4. 3
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  5. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Rules/MongoRuleRepository.cs
  6. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Schemas/MongoSchemaRepository.cs
  7. 77
      src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs
  8. 45
      src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs
  9. 133
      src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
  10. 20
      src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs
  11. 145
      src/Squidex.Domain.Apps.Read/Apps/AppHistoryEventsCreator.cs
  12. 29
      src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
  13. 25
      src/Squidex.Domain.Apps.Read/Apps/Services/IAppLimitsPlan.cs
  14. 22
      src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlanBillingManager.cs
  15. 27
      src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlansProvider.cs
  16. 14
      src/Squidex.Domain.Apps.Read/Apps/Services/IChangePlanResult.cs
  17. 30
      src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppLimitsPlan.cs
  18. 86
      src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppPlansProvider.cs
  19. 31
      src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/NoopAppPlanBillingManager.cs
  20. 19
      src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangeAsyncResult.cs
  21. 19
      src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangedResult.cs
  22. 25
      src/Squidex.Domain.Apps.Read/Apps/Services/RedirectToCheckoutResult.cs
  23. 25
      src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
  24. 16
      src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs
  25. 21
      src/Squidex.Domain.Apps.Read/Assets/IAssetStatsEntity.cs
  26. 23
      src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetRepository.cs
  27. 21
      src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetStatsRepository.cs
  28. 30
      src/Squidex.Domain.Apps.Read/CachingProviderBase.cs
  29. 49
      src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs
  30. 216
      src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs
  31. 74
      src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs
  32. 39
      src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelExtensions.cs
  33. 85
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs
  34. 229
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
  35. 23
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs
  36. 67
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQueryContext.cs
  37. 38
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLContext.cs
  38. 19
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs
  39. 27
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLUrlGenerator.cs
  40. 170
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/AssetGraphType.cs
  41. 68
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentDataGraphType.cs
  42. 112
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentGraphType.cs
  43. 192
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs
  44. 37
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/NoopGraphType.cs
  45. 25
      src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
  46. 28
      src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs
  47. 146
      src/Squidex.Domain.Apps.Read/Contents/QueryContext.cs
  48. 33
      src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs
  49. 90
      src/Squidex.Domain.Apps.Read/EntityMapper.cs
  50. 44
      src/Squidex.Domain.Apps.Read/History/HistoryEventToStore.cs
  51. 66
      src/Squidex.Domain.Apps.Read/History/HistoryEventsCreatorBase.cs
  52. 24
      src/Squidex.Domain.Apps.Read/History/IHistoryEventEntity.cs
  53. 21
      src/Squidex.Domain.Apps.Read/History/IHistoryEventsCreator.cs
  54. 19
      src/Squidex.Domain.Apps.Read/History/Repositories/IHistoryEventRepository.cs
  55. 34
      src/Squidex.Domain.Apps.Read/IAppProvider.cs
  56. 22
      src/Squidex.Domain.Apps.Read/IEntity.cs
  57. 17
      src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs
  58. 17
      src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs
  59. 17
      src/Squidex.Domain.Apps.Read/IEntityWithLastModifiedBy.cs
  60. 15
      src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs
  61. 17
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs
  62. 17
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs
  63. 17
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs
  64. 15
      src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs
  65. 22
      src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
  66. 29
      src/Squidex.Domain.Apps.Read/Rules/IRuleEventEntity.cs
  67. 35
      src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs
  68. 157
      src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs
  69. 73
      src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs
  70. 18
      src/Squidex.Domain.Apps.Read/Rules/RuleJobResult.cs
  71. 38
      src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
  72. 88
      src/Squidex.Domain.Apps.Read/Schemas/SchemaHistoryEventsCreator.cs
  73. 27
      src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj
  74. 87
      src/Squidex.Domain.Apps.Read/State/AppProvider.cs
  75. 70
      src/Squidex.Domain.Apps.Read/State/AppStateEventConsumer.cs
  76. 134
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrain.cs
  77. 75
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState.cs
  78. 117
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Apps.cs
  79. 65
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Rules.cs
  80. 162
      src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Schemas.cs
  81. 48
      src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrain.cs
  82. 30
      src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrainState.cs
  83. 38
      src/Squidex.Domain.Apps.Read/State/Grains/JsonAppEntity.cs
  84. 35
      src/Squidex.Domain.Apps.Read/State/Grains/JsonEntity.cs
  85. 36
      src/Squidex.Domain.Apps.Read/State/Grains/JsonRuleEntity.cs
  86. 63
      src/Squidex.Domain.Apps.Read/State/Grains/JsonSchemaEntity.cs
  87. 21
      src/Squidex.Domain.Apps.Write/AppAggregateCommand.cs
  88. 18
      src/Squidex.Domain.Apps.Write/AppCommand.cs
  89. 174
      src/Squidex.Domain.Apps.Write/Apps/AppCommandMiddleware.cs
  90. 260
      src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs
  91. 17
      src/Squidex.Domain.Apps.Write/Apps/Commands/AddLanguage.cs
  92. 19
      src/Squidex.Domain.Apps.Write/Apps/Commands/AssignContributor.cs
  93. 19
      src/Squidex.Domain.Apps.Write/Apps/Commands/AttachClient.cs
  94. 17
      src/Squidex.Domain.Apps.Write/Apps/Commands/ChangePlan.cs
  95. 30
      src/Squidex.Domain.Apps.Write/Apps/Commands/CreateApp.cs
  96. 15
      src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveContributor.cs
  97. 17
      src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveLanguage.cs
  98. 15
      src/Squidex.Domain.Apps.Write/Apps/Commands/RevokeClient.cs
  99. 21
      src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateClient.cs
  100. 24
      src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateLanguage.cs

86
Squidex.sln

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27004.2009 VisualStudioVersion = 15.0.27130.2003
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}"
EndProject EndProject
@ -12,22 +12,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure", "s
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Events", "src\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Events", "src\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Tests", "tests\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj", "{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}"
EndProject 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}" 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.MongoDb", "src\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj", "{6A811927-3C37-430A-90F4-503E37123956}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.MongoDb", "src\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj", "{6A811927-3C37-430A-90F4-503E37123956}"
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Redis", "src\Squidex.Infrastructure.Redis\Squidex.Infrastructure.Redis.csproj", "{D7166C56-178A-4457-B56A-C615C7450DEE}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.RabbitMq", "src\Squidex.Infrastructure.RabbitMq\Squidex.Infrastructure.RabbitMq.csproj", "{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.RabbitMq", "src\Squidex.Infrastructure.RabbitMq\Squidex.Infrastructure.RabbitMq.csproj", "{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343}"
@ -63,14 +53,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Mo
EndProject 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}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "tests\Benchmarks\Benchmarks.csproj", "{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}"
EndProject
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}" 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 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}" 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 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}" 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{A4823E14-C0E5-4A4D-B28F-27424C25C3C7}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -105,38 +95,6 @@ Global
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|Any CPU.Build.0 = Release|Any CPU {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|x64.ActiveCfg = Release|Any CPU
{25F66C64-058A-4D44-BC0C-F12A054F9A91}.Release|x86.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.ActiveCfg = Debug|Any CPU
{7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|Any CPU.Build.0 = 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 {7FD0A92B-7862-4BB1-932B-B52A9CACB56B}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -161,14 +119,6 @@ Global
{6A811927-3C37-430A-90F4-503E37123956}.Release|Any CPU.Build.0 = Release|Any CPU {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|x64.ActiveCfg = Release|Any CPU
{6A811927-3C37-430A-90F4-503E37123956}.Release|x86.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.ActiveCfg = Debug|Any CPU
{D7166C56-178A-4457-B56A-C615C7450DEE}.Debug|Any CPU.Build.0 = 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 {D7166C56-178A-4457-B56A-C615C7450DEE}.Debug|x64.ActiveCfg = Debug|Any CPU
@ -309,18 +259,6 @@ Global
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x64.Build.0 = Release|Any CPU {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.ActiveCfg = Release|Any CPU
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x86.Build.0 = 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.ActiveCfg = Debug|Any CPU
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
@ -357,6 +295,18 @@ Global
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x64.Build.0 = 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.ActiveCfg = Release|Any CPU
{7DA5B308-D950-4496-93D5-21D6C4D91644}.Release|x86.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -364,14 +314,9 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892} {25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892}
{A85201C6-6AF8-4B63-8365-08F741050438} = {C9809D59-6665-471E-AD87-5AC624C65892}
{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} {7FD0A92B-7862-4BB1-932B-B52A9CACB56B} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{FD0AFD44-7A93-4F9E-B5ED-72582392E435} = {C9809D59-6665-471E-AD87-5AC624C65892} {FD0AFD44-7A93-4F9E-B5ED-72582392E435} = {C9809D59-6665-471E-AD87-5AC624C65892}
{6A811927-3C37-430A-90F4-503E37123956} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {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} {D7166C56-178A-4457-B56A-C615C7450DEE} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343} = {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} {945871B1-77B8-43FB-B53C-27CF385AB756} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
@ -389,6 +334,7 @@ Global
{79FEF326-CA5E-4698-B2BA-C16A4580B4D5} = {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} {AA003372-CD8D-4DBC-962C-F61E0C93CF05} = {C9809D59-6665-471E-AD87-5AC624C65892}
{7DA5B308-D950-4496-93D5-21D6C4D91644} = {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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {02F2E872-3141-44F5-BD6A-33CD84E9FE08} SolutionGuid = {02F2E872-3141-44F5-BD6A-33CD84E9FE08}

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

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Apps
protected override string CollectionName() protected override string CollectionName()
{ {
return "Snapshots_Apps"; return "States_Apps";
} }
protected override async Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection) protected override async Task SetupCollectionAsync(IMongoCollection<MongoAppEntity> collection)

2
src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
protected override string CollectionName() protected override string CollectionName()
{ {
return "Snapshots_Assets"; return "States_Assets";
} }
protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection) protected override Task SetupCollectionAsync(IMongoCollection<MongoAssetEntity> collection)

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

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
protected override string CollectionName() protected override string CollectionName()
{ {
return "Snapshots_Contents"; return "States_Contents";
} }
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection) protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection)
@ -96,7 +96,6 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
try try
{ {
await Collection.InsertOneAsync(document); await Collection.InsertOneAsync(document);
await Collection.UpdateManyAsync(x => x.Id == value.Id && x.Version < value.Version, Update.Set(x => x.IsLatest, false)); await Collection.UpdateManyAsync(x => x.Id == value.Id && x.Version < value.Version, Update.Set(x => x.IsLatest, false));
} }
catch (MongoWriteException ex) catch (MongoWriteException ex)

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

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Rules
protected override string CollectionName() protected override string CollectionName()
{ {
return "Snapshots_Rules"; return "States_Rules";
} }
protected override async Task SetupCollectionAsync(IMongoCollection<MongoRuleEntity> collection) protected override async Task SetupCollectionAsync(IMongoCollection<MongoRuleEntity> collection)

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

@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Schemas
protected override string CollectionName() protected override string CollectionName()
{ {
return "Snapshots_Schemas"; return "States_Schemas";
} }
protected override async Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection) protected override async Task SetupCollectionAsync(IMongoCollection<MongoSchemaEntity> collection)

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

@ -1,77 +0,0 @@
// ==========================================================================
// AppEventDispatcher.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Linq;
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Events.Apps.Utils
{
public static class AppEventDispatcher
{
public static AppContributors Apply(this AppContributors contributors, AppContributorRemoved @event)
{
return contributors.Remove(@event.ContributorId);
}
public static AppContributors Apply(this AppContributors contributors, AppContributorAssigned @event)
{
return contributors.Assign(@event.ContributorId, @event.Permission);
}
public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageAdded @event)
{
return languagesConfig.Set(new LanguageConfig(@event.Language));
}
public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageRemoved @event)
{
return languagesConfig.Remove(@event.Language);
}
public static AppClients Apply(this AppClients clients, AppClientAttached @event)
{
return clients.Add(@event.Id, @event.Secret);
}
public static AppClients Apply(this AppClients clients, AppClientRevoked @event)
{
return clients.Revoke(@event.Id);
}
public static AppClients Apply(this AppClients clients, AppClientRenamed @event)
{
return clients.Rename(@event.Id, @event.Name);
}
public static AppClients Apply(this AppClients clients, AppClientUpdated @event)
{
return clients.Update(@event.Id, @event.Permission);
}
public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageUpdated @event)
{
var fallback = @event.Fallback;
if (fallback != null && fallback.Count > 0)
{
var existingLangauges = languagesConfig.OfType<LanguageConfig>().Select(x => x.Language);
fallback = fallback.Intersect(existingLangauges).ToList();
}
languagesConfig = languagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, fallback));
if (@event.IsMaster)
{
languagesConfig = languagesConfig.MakeMaster(@event.Language);
}
return languagesConfig;
}
}
}

45
src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs

@ -1,45 +0,0 @@
// ==========================================================================
// RuleEventDispatcher.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Events.Rules.Utils
{
public static class RuleEventDispatcher
{
public static Rule Create(RuleCreated @event)
{
return new Rule(@event.Trigger, @event.Action);
}
public static Rule Apply(this Rule rule, RuleUpdated @event)
{
if (@event.Trigger != null)
{
return rule.Update(@event.Trigger);
}
if (@event.Action != null)
{
return rule.Update(@event.Action);
}
return rule;
}
public static Rule Apply(this Rule rule, RuleEnabled @event)
{
return rule.Enable();
}
public static Rule Apply(this Rule rule, RuleDisabled @event)
{
return rule.Disable();
}
}
}

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

@ -1,133 +0,0 @@
// ==========================================================================
// SchemaEventDispatcher.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Events.Schemas.Utils
{
public static class SchemaEventDispatcher
{
public static Schema Create(SchemaCreated @event, FieldRegistry registry)
{
var schema = new Schema(@event.Name);
if (@event.Properties != null)
{
schema = schema.Update(@event.Properties);
}
if (@event.Fields != null)
{
var fieldId = 1;
foreach (var eventField in @event.Fields)
{
var partitioning =
string.Equals(eventField.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ?
Partitioning.Language :
Partitioning.Invariant;
var field = registry.CreateField(fieldId, eventField.Name, partitioning, eventField.Properties);
if (eventField.IsHidden)
{
field = field.Hide();
}
if (eventField.IsDisabled)
{
field = field.Disable();
}
if (eventField.IsLocked)
{
field = field.Lock();
}
schema = schema.AddField(field);
fieldId++;
}
}
return schema;
}
public static Schema Apply(this Schema schema, FieldAdded @event, FieldRegistry registry)
{
var partitioning =
string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ?
Partitioning.Language :
Partitioning.Invariant;
var field = registry.CreateField(@event.FieldId.Id, @event.Name, partitioning, @event.Properties);
schema = schema.DeleteField(@event.FieldId.Id);
schema = schema.AddField(field);
return schema;
}
public static Schema Apply(this Schema schema, FieldUpdated @event)
{
return schema.UpdateField(@event.FieldId.Id, @event.Properties);
}
public static Schema Apply(this Schema schema, FieldLocked @event)
{
return schema.LockField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, FieldHidden @event)
{
return schema.HideField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, FieldShown @event)
{
return schema.ShowField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, FieldDisabled @event)
{
return schema.DisableField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, FieldEnabled @event)
{
return schema.EnableField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, SchemaUpdated @event)
{
return schema.Update(@event.Properties);
}
public static Schema Apply(this Schema schema, SchemaFieldsReordered @event)
{
return schema.ReorderFields(@event.FieldIds);
}
public static Schema Apply(this Schema schema, FieldDeleted @event)
{
return schema.DeleteField(@event.FieldId.Id);
}
public static Schema Apply(this Schema schema, SchemaPublished @event)
{
return schema.Publish();
}
public static Schema Apply(this Schema schema, SchemaUnpublished @event)
{
return schema.Unpublish();
}
}
}

20
src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs

@ -1,20 +0,0 @@
// ==========================================================================
// AppEntityExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core;
namespace Squidex.Domain.Apps.Read.Apps
{
public static class AppEntityExtensions
{
public static PartitionResolver PartitionResolver(this IAppEntity entity)
{
return entity.LanguagesConfig.ToResolver();
}
}
}

145
src/Squidex.Domain.Apps.Read/Apps/AppHistoryEventsCreator.cs

@ -1,145 +0,0 @@
// ==========================================================================
// AppHistoryEventsCreator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
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
{
public class AppHistoryEventsCreator : HistoryEventsCreatorBase
{
public AppHistoryEventsCreator(TypeNameRegistry typeNameRegistry)
: base(typeNameRegistry)
{
AddEventMessage<AppContributorAssigned>(
"assigned {user:[Contributor]} as [Permission]");
AddEventMessage<AppContributorRemoved>(
"removed {user:[Contributor]} from app");
AddEventMessage<AppClientAttached>(
"added client {[Id]} to app");
AddEventMessage<AppClientRevoked>(
"revoked client {[Id]}");
AddEventMessage<AppClientUpdated>(
"updated client {[Id]}");
AddEventMessage<AppClientRenamed>(
"renamed client {[Id]} to {[Name]}");
AddEventMessage<AppLanguageAdded>(
"added language {[Language]}");
AddEventMessage<AppLanguageRemoved>(
"removed language {[Language]}");
AddEventMessage<AppLanguageUpdated>(
"updated language {[Language]}");
AddEventMessage<AppMasterLanguageSet>(
"changed master language to {[Language]}");
}
protected Task<HistoryEventToStore> On(AppContributorRemoved @event, EnvelopeHeaders headers)
{
const string channel = "settings.contributors";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Contributor", @event.ContributorId));
}
protected Task<HistoryEventToStore> On(AppContributorAssigned @event, EnvelopeHeaders headers)
{
const string channel = "settings.contributors";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission));
}
protected Task<HistoryEventToStore> On(AppClientAttached @event, EnvelopeHeaders headers)
{
const string channel = "settings.clients";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Id", @event.Id));
}
protected Task<HistoryEventToStore> On(AppClientRevoked @event, EnvelopeHeaders headers)
{
const string channel = "settings.clients";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Id", @event.Id));
}
protected Task<HistoryEventToStore> On(AppClientRenamed @event, EnvelopeHeaders headers)
{
const string channel = "settings.clients";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Id", @event.Id).AddParameter("Name", ClientName(@event)));
}
protected Task<HistoryEventToStore> On(AppLanguageAdded @event, EnvelopeHeaders headers)
{
const string channel = "settings.languages";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppLanguageRemoved @event, EnvelopeHeaders headers)
{
const string channel = "settings.languages";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppLanguageUpdated @event, EnvelopeHeaders headers)
{
const string channel = "settings.languages";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Language", @event.Language));
}
protected Task<HistoryEventToStore> On(AppMasterLanguageSet @event, EnvelopeHeaders headers)
{
const string channel = "settings.languages";
return Task.FromResult(
ForEvent(@event, channel)
.AddParameter("Language", @event.Language));
}
protected override Task<HistoryEventToStore> CreateEventCoreAsync(Envelope<IEvent> @event)
{
return this.DispatchFuncAsync(@event.Payload, @event.Headers, (HistoryEventToStore)null);
}
private static string ClientName(AppClientRenamed @event)
{
return !string.IsNullOrWhiteSpace(@event.Name) ? @event.Name : @event.Id;
}
}
}

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

@ -1,29 +0,0 @@
// ==========================================================================
// IAppEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Read.Apps
{
public interface IAppEntity : IEntity, IEntityWithVersion
{
string Etag { get; }
string Name { get; }
string PlanId { get; }
string PlanOwner { get; }
AppClients Clients { get; }
AppContributors Contributors { get; }
LanguagesConfig LanguagesConfig { get; }
}
}

25
src/Squidex.Domain.Apps.Read/Apps/Services/IAppLimitsPlan.cs

@ -1,25 +0,0 @@
// ==========================================================================
// IAppLimitsPlan.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public interface IAppLimitsPlan
{
string Id { get; }
string Name { get; }
string Costs { get; }
long MaxApiCalls { get; }
long MaxAssetSize { get; }
int MaxContributors { get; }
}
}

22
src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlanBillingManager.cs

@ -1,22 +0,0 @@
// ==========================================================================
// IAppPlanBillingManager.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public interface IAppPlanBillingManager
{
bool HasPortal { get; }
Task<IChangePlanResult> ChangePlanAsync(string userId, Guid appId, string appName, string planId);
Task<string> GetPortalLinkAsync(string userId);
}
}

27
src/Squidex.Domain.Apps.Read/Apps/Services/IAppPlansProvider.cs

@ -1,27 +0,0 @@
// ==========================================================================
// IAppPlansProvider.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public interface IAppPlansProvider
{
IEnumerable<IAppLimitsPlan> GetAvailablePlans();
bool IsConfiguredPlan(string planId);
IAppLimitsPlan GetPlanUpgradeForApp(IAppEntity app);
IAppLimitsPlan GetPlanUpgrade(string planId);
IAppLimitsPlan GetPlanForApp(IAppEntity app);
IAppLimitsPlan GetPlan(string planId);
}
}

14
src/Squidex.Domain.Apps.Read/Apps/Services/IChangePlanResult.cs

@ -1,14 +0,0 @@
// ==========================================================================
// IChangePlanResult.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public interface IChangePlanResult
{
}
}

30
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppLimitsPlan.cs

@ -1,30 +0,0 @@
// ==========================================================================
// ConfigAppLimitsPlan.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{
public sealed class ConfigAppLimitsPlan : IAppLimitsPlan
{
public string Id { get; set; }
public string Name { get; set; }
public string Costs { get; set; }
public long MaxApiCalls { get; set; }
public long MaxAssetSize { get; set; }
public int MaxContributors { get; set; }
public ConfigAppLimitsPlan Clone()
{
return (ConfigAppLimitsPlan)MemberwiseClone();
}
}
}

86
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/ConfigAppPlansProvider.cs

@ -1,86 +0,0 @@
// ==========================================================================
// ConfigAppLimitsProvider.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{
public sealed class ConfigAppPlansProvider : IAppPlansProvider
{
private static readonly ConfigAppLimitsPlan Infinite = new ConfigAppLimitsPlan
{
Id = "infinite",
Name = "Infinite",
MaxApiCalls = -1,
MaxAssetSize = -1,
MaxContributors = -1
};
private readonly Dictionary<string, ConfigAppLimitsPlan> plansById;
private readonly List<ConfigAppLimitsPlan> plansList;
public ConfigAppPlansProvider(IEnumerable<ConfigAppLimitsPlan> config)
{
Guard.NotNull(config, nameof(config));
plansList = config.Select(c => c.Clone()).OrderBy(x => x.MaxApiCalls).ToList();
plansById = plansList.ToDictionary(c => c.Id, StringComparer.OrdinalIgnoreCase);
}
public IEnumerable<IAppLimitsPlan> GetAvailablePlans()
{
return plansList;
}
public bool IsConfiguredPlan(string planId)
{
return planId != null && plansById.ContainsKey(planId);
}
public IAppLimitsPlan GetPlanForApp(IAppEntity app)
{
Guard.NotNull(app, nameof(app));
return GetPlan(app.PlanId);
}
public IAppLimitsPlan GetPlan(string planId)
{
return GetPlanCore(planId);
}
public IAppLimitsPlan GetPlanUpgradeForApp(IAppEntity app)
{
Guard.NotNull(app, nameof(app));
return GetPlanUpgrade(app.PlanId);
}
public IAppLimitsPlan GetPlanUpgrade(string planId)
{
var plan = GetPlanCore(planId);
var nextPlanIndex = plansList.IndexOf(plan);
if (nextPlanIndex >= 0 && nextPlanIndex < plansList.Count - 1)
{
return plansList[nextPlanIndex + 1];
}
return null;
}
private ConfigAppLimitsPlan GetPlanCore(string planId)
{
return plansById.GetOrDefault(planId ?? string.Empty) ?? plansById.Values.FirstOrDefault() ?? Infinite;
}
}
}

31
src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/NoopAppPlanBillingManager.cs

@ -1,31 +0,0 @@
// ==========================================================================
// NoopAppPlanBillingManager.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
{
public sealed class NoopAppPlanBillingManager : IAppPlanBillingManager
{
public bool HasPortal
{
get { return false; }
}
public Task<IChangePlanResult> ChangePlanAsync(string userId, Guid appId, string appName, string planId)
{
return Task.FromResult<IChangePlanResult>(PlanChangedResult.Instance);
}
public Task<string> GetPortalLinkAsync(string userId)
{
return Task.FromResult(string.Empty);
}
}
}

19
src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangeAsyncResult.cs

@ -1,19 +0,0 @@
// ==========================================================================
// PlanChangeAsyncResult.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public sealed class PlanChangeAsyncResult : IChangePlanResult
{
public static readonly PlanChangeAsyncResult Instance = new PlanChangeAsyncResult();
private PlanChangeAsyncResult()
{
}
}
}

19
src/Squidex.Domain.Apps.Read/Apps/Services/PlanChangedResult.cs

@ -1,19 +0,0 @@
// ==========================================================================
// PlanChangedResult.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public sealed class PlanChangedResult : IChangePlanResult
{
public static readonly PlanChangedResult Instance = new PlanChangedResult();
private PlanChangedResult()
{
}
}
}

25
src/Squidex.Domain.Apps.Read/Apps/Services/RedirectToCheckoutResult.cs

@ -1,25 +0,0 @@
// ==========================================================================
// RedirectToCheckoutResult.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Apps.Services
{
public sealed class RedirectToCheckoutResult : IChangePlanResult
{
public Uri Url { get; }
public RedirectToCheckoutResult(Uri url)
{
Guard.NotNull(url, nameof(url));
Url = url;
}
}
}

25
src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs

@ -1,25 +0,0 @@
// ==========================================================================
// IAssetEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.ValidateContent;
namespace Squidex.Domain.Apps.Read.Assets
{
public interface IAssetEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion,
IAssetInfo
{
string MimeType { get; }
long FileVersion { get; }
}
}

16
src/Squidex.Domain.Apps.Read/Assets/IAssetEventConsumer.cs

@ -1,16 +0,0 @@
// ==========================================================================
// IAssetEventConsumer.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.Assets
{
public interface IAssetEventConsumer : IEventConsumer
{
}
}

21
src/Squidex.Domain.Apps.Read/Assets/IAssetStatsEntity.cs

@ -1,21 +0,0 @@
// ==========================================================================
// IAssetStatsEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Read.Assets
{
public interface IAssetStatsEntity
{
DateTime Date { get; }
long TotalSize { get; }
long TotalCount { get; }
}
}

23
src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetRepository.cs

@ -1,23 +0,0 @@
// ==========================================================================
// IAssetRepository.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.Read.Assets.Repositories
{
public interface IAssetRepository
{
Task<IReadOnlyList<IAssetEntity>> QueryAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null, int take = 10, int skip = 0);
Task<IAssetEntity> FindAssetAsync(Guid id);
Task<long> CountAsync(Guid appId, HashSet<string> mimeTypes = null, HashSet<Guid> ids = null, string query = null);
}
}

21
src/Squidex.Domain.Apps.Read/Assets/Repositories/IAssetStatsRepository.cs

@ -1,21 +0,0 @@
// ==========================================================================
// IAssetStatsRepository.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.Read.Assets.Repositories
{
public interface IAssetStatsRepository
{
Task<IReadOnlyList<IAssetStatsEntity>> QueryAsync(Guid appId, DateTime fromDate, DateTime toDate);
Task<long> GetTotalSizeAsync(Guid appId);
}
}

30
src/Squidex.Domain.Apps.Read/CachingProviderBase.cs

@ -1,30 +0,0 @@
// ==========================================================================
// CachingProviderBase.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Microsoft.Extensions.Caching.Memory;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public abstract class CachingProviderBase
{
private readonly IMemoryCache cache;
protected IMemoryCache Cache
{
get { return cache; }
}
protected CachingProviderBase(IMemoryCache cache)
{
Guard.NotNull(cache, nameof(cache));
this.cache = cache;
}
}
}

49
src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs

@ -1,49 +0,0 @@
// ==========================================================================
// ContentHistoryEventsCreator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
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
{
public sealed class ContentHistoryEventsCreator : HistoryEventsCreatorBase
{
public ContentHistoryEventsCreator(TypeNameRegistry typeNameRegistry)
: base(typeNameRegistry)
{
AddEventMessage<ContentCreated>(
"created content item.");
AddEventMessage<ContentUpdated>(
"updated content item.");
AddEventMessage<ContentDeleted>(
"deleted content item.");
AddEventMessage<ContentStatusChanged>(
"changed status of content item to {[Status]}.");
}
protected override Task<HistoryEventToStore> CreateEventCoreAsync(Envelope<IEvent> @event)
{
var channel = $"contents.{@event.Headers.AggregateId()}";
var result = ForEvent(@event.Payload, channel);
if (@event.Payload is ContentStatusChanged contentStatusChanged)
{
result = result.AddParameter("Status", contentStatusChanged.Status);
}
return Task.FromResult(result);
}
}
}

216
src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs

@ -1,216 +0,0 @@
// ==========================================================================
// ContentQueryService.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.OData;
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.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
namespace Squidex.Domain.Apps.Read.Contents
{
public sealed class ContentQueryService : IContentQueryService
{
private readonly IContentRepository contentRepository;
private readonly IAppProvider appProvider;
private readonly IScriptEngine scriptEngine;
private readonly EdmModelBuilder modelBuilder;
public ContentQueryService(
IContentRepository contentRepository,
IAppProvider appProvider,
IScriptEngine scriptEngine,
EdmModelBuilder modelBuilder)
{
Guard.NotNull(contentRepository, nameof(contentRepository));
Guard.NotNull(scriptEngine, nameof(scriptEngine));
Guard.NotNull(modelBuilder, nameof(modelBuilder));
Guard.NotNull(appProvider, nameof(appProvider));
this.contentRepository = contentRepository;
this.appProvider = appProvider;
this.scriptEngine = scriptEngine;
this.modelBuilder = modelBuilder;
}
public async Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(user, nameof(user));
Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName));
var isFrontendClient = user.IsInClient("squidex-frontend");
var schema = await FindSchemaAsync(app, schemaIdOrName);
var content = await contentRepository.FindContentAsync(app, schema, id);
if (content == null || (content.Status != Status.Published && !isFrontendClient))
{
throw new DomainObjectNotFoundException(id.ToString(), typeof(ISchemaEntity));
}
content = TransformContent(user, schema, new List<IContentEntity> { content })[0];
return (schema, content);
}
public async Task<(ISchemaEntity Schema, long Total, IReadOnlyList<IContentEntity> Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(user, nameof(user));
Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName));
var schema = await FindSchemaAsync(app, schemaIdOrName);
var parsedQuery = ParseQuery(app, query, schema);
var status = ParseStatus(user, archived);
var taskForItems = contentRepository.QueryAsync(app, schema, status.ToArray(), parsedQuery);
var taskForCount = contentRepository.CountAsync(app, schema, status.ToArray(), parsedQuery);
await Task.WhenAll(taskForItems, taskForCount);
var list = TransformContent(user, schema, taskForItems.Result.ToList());
return (schema, taskForCount.Result, list);
}
public async Task<(ISchemaEntity Schema, long Total, IReadOnlyList<IContentEntity> Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> ids)
{
Guard.NotNull(ids, nameof(ids));
Guard.NotNull(app, nameof(app));
Guard.NotNull(user, nameof(user));
Guard.NotNullOrEmpty(schemaIdOrName, nameof(schemaIdOrName));
var schema = await FindSchemaAsync(app, schemaIdOrName);
var status = ParseStatus(user, archived);
var taskForItems = contentRepository.QueryAsync(app, schema, status.ToArray(), ids);
var taskForCount = contentRepository.CountAsync(app, schema, status.ToArray(), ids);
await Task.WhenAll(taskForItems, taskForCount);
var list = TransformContent(user, schema, taskForItems.Result.ToList());
return (schema, taskForCount.Result, list);
}
private List<IContentEntity> TransformContent(ClaimsPrincipal user, ISchemaEntity schema, List<IContentEntity> contents)
{
var scriptText = schema.ScriptQuery;
if (!string.IsNullOrWhiteSpace(scriptText))
{
for (var i = 0; i < contents.Count; i++)
{
var content = contents[i];
var contentData = scriptEngine.Transform(new ScriptContext { User = user, Data = content.Data, ContentId = content.Id }, scriptText);
contents[i] = SimpleMapper.Map(content, new Content { Data = contentData });
}
}
return contents;
}
private ODataUriParser ParseQuery(IAppEntity app, string query, ISchemaEntity schema)
{
try
{
var model = modelBuilder.BuildEdmModel(schema, app);
return model.ParseQuery(query);
}
catch (ODataException ex)
{
throw new ValidationException($"Failed to parse query: {ex.Message}", ex);
}
}
public async Task<ISchemaEntity> FindSchemaAsync(IAppEntity app, string schemaIdOrName)
{
Guard.NotNull(app, nameof(app));
ISchemaEntity schema = null;
if (Guid.TryParse(schemaIdOrName, out var id))
{
schema = await appProvider.GetSchemaAsync(app.Name, id);
}
if (schema == null)
{
schema = await appProvider.GetSchemaAsync(app.Name, schemaIdOrName);
}
if (schema == null)
{
throw new DomainObjectNotFoundException(schemaIdOrName, typeof(ISchemaEntity));
}
return schema;
}
private static List<Status> ParseStatus(ClaimsPrincipal user, bool archived)
{
var status = new List<Status>();
if (user.IsInClient("squidex-frontend"))
{
if (archived)
{
status.Add(Status.Archived);
}
else
{
status.Add(Status.Draft);
status.Add(Status.Published);
}
}
else
{
status.Add(Status.Published);
}
return status;
}
private sealed class Content : IContentEntity
{
public Guid Id { get; set; }
public Guid AppId { get; set; }
public long Version { get; set; }
public Instant Created { get; set; }
public Instant LastModified { get; set; }
public RefToken CreatedBy { get; set; }
public RefToken LastModifiedBy { get; set; }
public NamedContentData Data { get; set; }
public Status Status { get; set; }
}
}
}

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

@ -1,74 +0,0 @@
// ==========================================================================
// EdmModelBuilder.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.OData.Edm;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.GenerateEdmSchema;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.Edm
{
public class EdmModelBuilder : CachingProviderBase
{
public EdmModelBuilder(IMemoryCache cache)
: base(cache)
{
}
public virtual IEdmModel BuildEdmModel(ISchemaEntity schema, IAppEntity app)
{
Guard.NotNull(schema, nameof(schema));
var cacheKey = $"{schema.Id}_{schema.Version}_{app.Id}_{app.Version}";
var result = Cache.GetOrCreate<IEdmModel>(cacheKey, entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(60);
return BuildEdmModel(schema.SchemaDef, app.PartitionResolver());
});
return result;
}
private static EdmModel BuildEdmModel(Schema schema, PartitionResolver partitionResolver)
{
var model = new EdmModel();
var container = new EdmEntityContainer("Squidex", "Container");
var schemaType = schema.BuildEdmType(partitionResolver, x =>
{
model.AddElement(x);
return x;
});
var entityType = new EdmEntityType("Squidex", schema.Name);
entityType.AddStructuralProperty("data", new EdmComplexTypeReference(schemaType, false));
entityType.AddStructuralProperty("version", EdmPrimitiveTypeKind.Int32);
entityType.AddStructuralProperty("created", EdmPrimitiveTypeKind.DateTimeOffset);
entityType.AddStructuralProperty("createdBy", EdmPrimitiveTypeKind.String);
entityType.AddStructuralProperty("lastModified", EdmPrimitiveTypeKind.DateTimeOffset);
entityType.AddStructuralProperty("lastModifiedBy", EdmPrimitiveTypeKind.String);
model.AddElement(container);
model.AddElement(schemaType);
model.AddElement(entityType);
container.AddEntitySet("ContentSet", entityType);
return model;
}
}
}

39
src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelExtensions.cs

@ -1,39 +0,0 @@
// ==========================================================================
// EdmModelExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OData.UriParser;
namespace Squidex.Domain.Apps.Read.Contents.Edm
{
public static class EdmModelExtensions
{
public static ODataUriParser ParseQuery(this IEdmModel model, string query)
{
if (!model.EntityContainer.EntitySets().Any())
{
return null;
}
query = query ?? string.Empty;
var path = model.EntityContainer.EntitySets().First().Path.Path.Split('.').Last();
if (query.StartsWith("?", StringComparison.Ordinal))
{
query = query.Substring(1);
}
var parser = new ODataUriParser(model, new Uri($"{path}?{query}", UriKind.Relative));
return parser;
}
}
}

85
src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs

@ -1,85 +0,0 @@
// ==========================================================================
// CachingGraphQLService.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public sealed class CachingGraphQLService : CachingProviderBase, IGraphQLService
{
private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(60);
private readonly IContentQueryService contentQuery;
private readonly IGraphQLUrlGenerator urlGenerator;
private readonly IAssetRepository assetRepository;
private readonly IAppProvider appProvider;
public CachingGraphQLService(IMemoryCache cache,
IAppProvider appProvider,
IAssetRepository assetRepository,
IContentQueryService contentQuery,
IGraphQLUrlGenerator urlGenerator)
: base(cache)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(contentQuery, nameof(urlGenerator));
Guard.NotNull(contentQuery, nameof(contentQuery));
this.appProvider = appProvider;
this.assetRepository = assetRepository;
this.contentQuery = contentQuery;
this.urlGenerator = urlGenerator;
}
public async Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, ClaimsPrincipal user, GraphQLQuery query)
{
Guard.NotNull(app, nameof(app));
Guard.NotNull(query, nameof(query));
if (string.IsNullOrWhiteSpace(query.Query))
{
return (new object(), new object[0]);
}
var modelContext = await GetModelAsync(app);
var queryContext = new GraphQLQueryContext(app, assetRepository, contentQuery, user, urlGenerator);
return await modelContext.ExecuteAsync(queryContext, query);
}
private async Task<GraphQLModel> GetModelAsync(IAppEntity app)
{
var cacheKey = CreateCacheKey(app.Id, app.Etag);
var modelContext = Cache.Get<GraphQLModel>(cacheKey);
if (modelContext == null)
{
var allSchemas = await appProvider.GetSchemasAsync(app.Name);
modelContext = new GraphQLModel(app, allSchemas.Where(x => x.IsPublished), urlGenerator);
Cache.Set(cacheKey, modelContext, CacheDuration);
}
return modelContext;
}
private static object CreateCacheKey(Guid appId, string etag)
{
return $"GraphQLModel_{appId}_{etag}";
}
}
}

229
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs

@ -1,229 +0,0 @@
// ==========================================================================
// GraphQLContext.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 GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Contents.GraphQL.Types;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
using GraphQLSchema = GraphQL.Types.Schema;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public sealed class GraphQLModel : IGraphQLContext
{
private readonly Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>> fieldInfos;
private readonly Dictionary<Guid, ContentGraphType> schemaTypes = new Dictionary<Guid, ContentGraphType>();
private readonly Dictionary<Guid, ISchemaEntity> schemas;
private readonly PartitionResolver partitionResolver;
private readonly IAppEntity app;
private readonly IGraphType assetType;
private readonly IGraphType assetListType;
private readonly GraphQLSchema graphQLSchema;
public bool CanGenerateAssetSourceUrl { get; }
public GraphQLModel(IAppEntity app, IEnumerable<ISchemaEntity> schemas, IGraphQLUrlGenerator urlGenerator)
{
this.app = app;
CanGenerateAssetSourceUrl = urlGenerator.CanGenerateAssetSourceUrl;
partitionResolver = app.PartitionResolver();
assetType = new AssetGraphType(this);
assetListType = new ListGraphType(new NonNullGraphType(assetType));
fieldInfos = new Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>>
{
{
typeof(StringField),
field => ResolveDefault("String")
},
{
typeof(BooleanField),
field => ResolveDefault("Boolean")
},
{
typeof(NumberField),
field => ResolveDefault("Float")
},
{
typeof(DateTimeField),
field => ResolveDefault("Date")
},
{
typeof(JsonField),
field => ResolveDefault("Json")
},
{
typeof(TagsField),
field => ResolveDefault("String")
},
{
typeof(GeolocationField),
field => ResolveDefault("Geolocation")
},
{
typeof(AssetsField),
field => ResolveAssets(assetListType)
},
{
typeof(ReferencesField),
field => ResolveReferences(field)
}
};
this.schemas = schemas.ToDictionary(x => x.Id);
graphQLSchema = new GraphQLSchema { Query = new ContentQueryGraphType(this, this.schemas.Values) };
foreach (var schemaType in schemaTypes.Values)
{
schemaType.Initialize();
}
}
private static (IGraphType ResolveType, IFieldResolver Resolver) ResolveDefault(string name)
{
return (new NoopGraphType(name), new FuncFieldResolver<ContentFieldData, object>(c => c.Source.GetOrDefault(c.FieldName)));
}
public IFieldResolver ResolveAssetUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
return context.UrlGenerator.GenerateAssetUrl(app, c.Source);
});
return resolver;
}
public IFieldResolver ResolveAssetSourceUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
return context.UrlGenerator.GenerateAssetSourceUrl(app, c.Source);
});
return resolver;
}
public IFieldResolver ResolveAssetThumbnailUrl()
{
var resolver = new FuncFieldResolver<IAssetEntity, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
return context.UrlGenerator.GenerateAssetThumbnailUrl(app, c.Source);
});
return resolver;
}
public IFieldResolver ResolveContentUrl(ISchemaEntity schema)
{
var resolver = new FuncFieldResolver<IContentEntity, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
return context.UrlGenerator.GenerateContentUrl(app, schema, c.Source);
});
return resolver;
}
private static ValueTuple<IGraphType, IFieldResolver> ResolveAssets(IGraphType assetListType)
{
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
return context.GetReferencedAssetsAsync(contentIds);
});
return (assetListType, resolver);
}
private ValueTuple<IGraphType, IFieldResolver> ResolveReferences(Field field)
{
var schemaId = ((ReferencesField)field).Properties.SchemaId;
var schemaType = GetSchemaType(schemaId);
if (schemaType == null)
{
return (null, null);
}
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
return context.GetReferencedContentsAsync(schemaId, contentIds);
});
var schemaFieldType = new ListGraphType(new NonNullGraphType(GetSchemaType(schemaId)));
return (schemaFieldType, resolver);
}
public async Task<(object Data, object[] Errors)> ExecuteAsync(GraphQLQueryContext context, GraphQLQuery query)
{
Guard.NotNull(context, nameof(context));
var result = await new DocumentExecuter().ExecuteAsync(options =>
{
options.Query = query.Query;
options.Schema = graphQLSchema;
options.Inputs = query.Variables?.ToInputs() ?? new Inputs();
options.UserContext = context;
options.OperationName = query.OperationName;
}).ConfigureAwait(false);
return (result.Data, result.Errors?.Select(x => (object)new { x.Message, x.Locations }).ToArray());
}
public IFieldPartitioning ResolvePartition(Partitioning key)
{
return partitionResolver(key);
}
public IGraphType GetAssetType()
{
return assetType;
}
public (IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(Field field)
{
return fieldInfos[field.GetType()](field);
}
public IGraphType GetSchemaType(Guid schemaId)
{
var schema = schemas.GetOrDefault(schemaId);
return schema != null ? schemaTypes.GetOrAdd(schemaId, k => new ContentGraphType(schema, this)) : null;
}
}
}

23
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs

@ -1,23 +0,0 @@
// ==========================================================================
// GraphQLQuery.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json.Linq;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public class GraphQLQuery
{
public string OperationName { get; set; }
public string NamedQuery { get; set; }
public string Query { get; set; }
public JObject Variables { get; set; }
}
}

67
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQueryContext.cs

@ -1,67 +0,0 @@
// ==========================================================================
// GraphQLQueryContext.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Assets.Repositories;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public sealed class GraphQLQueryContext : QueryContext
{
public IGraphQLUrlGenerator UrlGenerator { get; }
public GraphQLQueryContext(IAppEntity app, IAssetRepository assetRepository, IContentQueryService contentQuery, ClaimsPrincipal user,
IGraphQLUrlGenerator urlGenerator)
: base(app, assetRepository, contentQuery, user)
{
UrlGenerator = urlGenerator;
}
public Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(JToken value)
{
var ids = ParseIds(value);
return GetReferencedAssetsAsync(ids);
}
public Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, JToken value)
{
var ids = ParseIds(value);
return GetReferencedContentsAsync(schemaId, ids);
}
private static ICollection<Guid> ParseIds(JToken value)
{
try
{
var result = new List<Guid>();
if (value is JArray)
{
foreach (var id in value)
{
result.Add(Guid.Parse(id.ToString()));
}
}
return result;
}
catch
{
return new List<Guid>();
}
}
}
}

38
src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLContext.cs

@ -1,38 +0,0 @@
// ==========================================================================
// SchemaGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public interface IGraphQLContext
{
bool CanGenerateAssetSourceUrl { get; }
IFieldPartitioning ResolvePartition(Partitioning key);
IGraphType GetAssetType();
IGraphType GetSchemaType(Guid schemaId);
IFieldResolver ResolveAssetUrl();
IFieldResolver ResolveAssetSourceUrl();
IFieldResolver ResolveAssetThumbnailUrl();
IFieldResolver ResolveContentUrl(ISchemaEntity schema);
(IGraphType ResolveType, IFieldResolver Resolver) GetGraphType(Field field);
}
}

19
src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs

@ -1,19 +0,0 @@
// ==========================================================================
// IGraphQLService.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Apps;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public interface IGraphQLService
{
Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, ClaimsPrincipal user, GraphQLQuery query);
}
}

27
src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLUrlGenerator.cs

@ -1,27 +0,0 @@
// ==========================================================================
// IGraphQLUrlGenerator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Schemas;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public interface IGraphQLUrlGenerator
{
bool CanGenerateAssetSourceUrl { get; }
string GenerateAssetUrl(IAppEntity app, IAssetEntity asset);
string GenerateAssetThumbnailUrl(IAppEntity app, IAssetEntity asset);
string GenerateAssetSourceUrl(IAppEntity app, IAssetEntity asset);
string GenerateContentUrl(IAppEntity app, ISchemaEntity schema, IContentEntity content);
}
}

170
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/AssetGraphType.cs

@ -1,170 +0,0 @@
// ==========================================================================
// AssetGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class AssetGraphType : ObjectGraphType<IAssetEntity>
{
public AssetGraphType(IGraphQLContext context)
{
Name = "AssetDto";
AddField(new FieldType
{
Name = "id",
Resolver = Resolver(x => x.Id.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The id of the asset."
});
AddField(new FieldType
{
Name = "version",
Resolver = Resolver(x => x.Version),
ResolvedType = new NonNullGraphType(new IntGraphType()),
Description = "The version of the asset."
});
AddField(new FieldType
{
Name = "created",
Resolver = Resolver(x => x.Created.ToDateTimeUtc()),
ResolvedType = new NonNullGraphType(new DateGraphType()),
Description = "The date and time when the asset has been created."
});
AddField(new FieldType
{
Name = "createdBy",
Resolver = Resolver(x => x.CreatedBy.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The user that has created the asset."
});
AddField(new FieldType
{
Name = "lastModified",
Resolver = Resolver(x => x.LastModified.ToDateTimeUtc()),
ResolvedType = new NonNullGraphType(new DateGraphType()),
Description = "The date and time when the asset has been modified last."
});
AddField(new FieldType
{
Name = "lastModifiedBy",
Resolver = Resolver(x => x.LastModifiedBy.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The user that has updated the asset last."
});
AddField(new FieldType
{
Name = "mimeType",
Resolver = Resolver(x => x.MimeType),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The mime type."
});
AddField(new FieldType
{
Name = "url",
Resolver = context.ResolveAssetUrl(),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The url to the asset."
});
AddField(new FieldType
{
Name = "thumbnailUrl",
Resolver = context.ResolveAssetThumbnailUrl(),
ResolvedType = new StringGraphType(),
Description = "The thumbnail url to the asset."
});
AddField(new FieldType
{
Name = "fileName",
Resolver = Resolver(x => x.FileName),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The file name."
});
AddField(new FieldType
{
Name = "fileType",
Resolver = Resolver(x => x.FileName.FileType()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = "The file type."
});
AddField(new FieldType
{
Name = "fileSize",
Resolver = Resolver(x => x.FileSize),
ResolvedType = new NonNullGraphType(new IntGraphType()),
Description = "The size of the file in bytes."
});
AddField(new FieldType
{
Name = "fileVersion",
Resolver = Resolver(x => x.FileVersion),
ResolvedType = new NonNullGraphType(new IntGraphType()),
Description = "The version of the file."
});
AddField(new FieldType
{
Name = "isImage",
Resolver = Resolver(x => x.IsImage),
ResolvedType = new NonNullGraphType(new BooleanGraphType()),
Description = "Determines of the created file is an image."
});
AddField(new FieldType
{
Name = "pixelWidth",
Resolver = Resolver(x => x.PixelWidth),
ResolvedType = new IntGraphType(),
Description = "The width of the image in pixels if the asset is an image."
});
AddField(new FieldType
{
Name = "pixelHeight",
Resolver = Resolver(x => x.PixelHeight),
ResolvedType = new IntGraphType(),
Description = "The height of the image in pixels if the asset is an image."
});
if (context.CanGenerateAssetSourceUrl)
{
AddField(new FieldType
{
Name = "sourceUrl",
Resolver = context.ResolveAssetSourceUrl(),
ResolvedType = new StringGraphType(),
Description = "The source url of the asset."
});
}
Description = "An asset";
}
private static IFieldResolver Resolver(Func<IAssetEntity, object> action)
{
return new FuncFieldResolver<IAssetEntity, object>(c => action(c.Source));
}
}
}

68
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -1,68 +0,0 @@
// ==========================================================================
// ContentDataGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
using Schema = Squidex.Domain.Apps.Core.Schemas.Schema;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData>
{
public ContentDataGraphType(Schema schema, IGraphQLContext context)
{
var schemaName = schema.Properties.Label.WithFallback(schema.Name);
Name = $"{schema.Name.ToPascalCase()}DataDto";
foreach (var field in schema.Fields.Where(x => !x.IsHidden))
{
var fieldInfo = context.GetGraphType(field);
if (fieldInfo.ResolveType != null)
{
var fieldName = field.RawProperties.Label.WithFallback(field.Name);
var fieldGraphType = new ObjectGraphType
{
Name = $"{schema.Name.ToPascalCase()}Data{field.Name.ToPascalCase()}Dto"
};
var partition = context.ResolvePartition(field.Partitioning);
foreach (var partitionItem in partition)
{
fieldGraphType.AddField(new FieldType
{
Name = partitionItem.Key,
Resolver = fieldInfo.Resolver,
ResolvedType = fieldInfo.ResolveType,
Description = field.RawProperties.Hints
});
}
fieldGraphType.Description = $"The structure of the {fieldName} of a {schemaName} content type.";
var fieldResolver = new FuncFieldResolver<NamedContentData, ContentFieldData>(c => c.Source.GetOrDefault(field.Name));
AddField(new FieldType
{
Name = field.Name.ToCamelCase(),
Resolver = fieldResolver,
ResolvedType = fieldGraphType
});
}
}
Description = $"The structure of a {schemaName} content type.";
}
}
}

112
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentGraphType.cs

@ -1,112 +0,0 @@
// ==========================================================================
// SchemaGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class ContentGraphType : ObjectGraphType<IContentEntity>
{
private readonly ISchemaEntity schema;
private readonly IGraphQLContext context;
public ContentGraphType(ISchemaEntity schema, IGraphQLContext context)
{
this.context = context;
this.schema = schema;
Name = $"{schema.Name.ToPascalCase()}Dto";
}
public void Initialize()
{
var schemaName = schema.SchemaDef.Properties.Label.WithFallback(schema.Name);
AddField(new FieldType
{
Name = "id",
Resolver = Resolver(x => x.Id.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = $"The id of the {schemaName} content."
});
AddField(new FieldType
{
Name = "version",
Resolver = Resolver(x => x.Version),
ResolvedType = new NonNullGraphType(new IntGraphType()),
Description = $"The version of the {schemaName} content."
});
AddField(new FieldType
{
Name = "created",
Resolver = Resolver(x => x.Created.ToDateTimeUtc()),
ResolvedType = new NonNullGraphType(new DateGraphType()),
Description = $"The date and time when the {schemaName} content has been created."
});
AddField(new FieldType
{
Name = "createdBy",
Resolver = Resolver(x => x.CreatedBy.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = $"The user that has created the {schemaName} content."
});
AddField(new FieldType
{
Name = "lastModified",
Resolver = Resolver(x => x.LastModified.ToDateTimeUtc()),
ResolvedType = new NonNullGraphType(new DateGraphType()),
Description = $"The date and time when the {schemaName} content has been modified last."
});
AddField(new FieldType
{
Name = "lastModifiedBy",
Resolver = Resolver(x => x.LastModifiedBy.ToString()),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = $"The user that has updated the {schemaName} content last."
});
AddField(new FieldType
{
Name = "url",
Resolver = context.ResolveContentUrl(schema),
ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = $"The url to the the {schemaName} content."
});
var dataType = new ContentDataGraphType(schema.SchemaDef, context);
if (dataType.Fields.Any())
{
AddField(new FieldType
{
Name = "data",
Resolver = Resolver(x => x.Data),
ResolvedType = new NonNullGraphType(dataType),
Description = $"The data of the {schemaName} content."
});
}
Description = $"The structure of a {schemaName} content type.";
}
private static IFieldResolver Resolver(Func<IContentEntity, object> action)
{
return new FuncFieldResolver<IContentEntity, object>(c => action(c.Source));
}
}
}

192
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs

@ -1,192 +0,0 @@
// ==========================================================================
// GraphModelType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class ContentQueryGraphType : ObjectGraphType
{
public ContentQueryGraphType(IGraphQLContext graphQLContext, IEnumerable<ISchemaEntity> schemas)
{
AddAssetFind(graphQLContext);
AddAssetsQuery(graphQLContext);
foreach (var schema in schemas)
{
var schemaName = schema.SchemaDef.Properties.Label.WithFallback(schema.SchemaDef.Name);
var schemaType = graphQLContext.GetSchemaType(schema.Id);
AddContentFind(schema, schemaType, schemaName);
AddContentQuery(schema, schemaType, schemaName);
}
Description = "The app queries.";
}
private void AddAssetFind(IGraphQLContext graphQLContext)
{
AddField(new FieldType
{
Name = "findAsset",
Arguments = new QueryArguments
{
new QueryArgument(typeof(StringGraphType))
{
Name = "id",
Description = "The id of the asset.",
DefaultValue = string.Empty
}
},
ResolvedType = graphQLContext.GetAssetType(),
Resolver = new FuncFieldResolver<object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var contentId = Guid.Parse(c.GetArgument("id", Guid.Empty.ToString()));
return context.FindAssetAsync(contentId);
}),
Description = "Find an asset by id."
});
}
private void AddContentFind(ISchemaEntity schema, IGraphType schemaType, string schemaName)
{
AddField(new FieldType
{
Name = $"find{schema.Name.ToPascalCase()}Content",
Arguments = new QueryArguments
{
new QueryArgument(typeof(StringGraphType))
{
Name = "id",
Description = $"The id of the {schemaName} content.",
DefaultValue = string.Empty
}
},
ResolvedType = schemaType,
Resolver = new FuncFieldResolver<object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var contentId = Guid.Parse(c.GetArgument("id", Guid.Empty.ToString()));
return context.FindContentAsync(schema.Id, contentId);
}),
Description = $"Find an {schemaName} content by id."
});
}
private void AddAssetsQuery(IGraphQLContext graphQLContext)
{
AddField(new FieldType
{
Name = "queryAssets",
Arguments = new QueryArguments
{
new QueryArgument(typeof(IntGraphType))
{
Name = "top",
Description = "Optional number of assets to take.",
DefaultValue = 20
},
new QueryArgument(typeof(IntGraphType))
{
Name = "skip",
Description = "Optional number of assets to skip.",
DefaultValue = 0
},
new QueryArgument(typeof(StringGraphType))
{
Name = "search",
Description = "Optional query.",
DefaultValue = string.Empty
}
},
ResolvedType = new ListGraphType(new NonNullGraphType(graphQLContext.GetAssetType())),
Resolver = new FuncFieldResolver<object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var argTop = c.GetArgument("top", 20);
var argSkip = c.GetArgument("skip", 0);
var argQuery = c.GetArgument("search", string.Empty);
return context.QueryAssetsAsync(argQuery, argSkip, argTop);
}),
Description = "Query assets items."
});
}
private void AddContentQuery(ISchemaEntity schema, IGraphType schemaType, string schemaName)
{
AddField(new FieldType
{
Name = $"query{schema.Name.ToPascalCase()}Contents",
Arguments = new QueryArguments
{
new QueryArgument(typeof(IntGraphType))
{
Name = "top",
Description = "Optional number of contents to take.",
DefaultValue = 20
},
new QueryArgument(typeof(IntGraphType))
{
Name = "skip",
Description = "Optional number of contents to skip.",
DefaultValue = 0
},
new QueryArgument(typeof(StringGraphType))
{
Name = "filter",
Description = "Optional OData filter.",
DefaultValue = string.Empty
},
new QueryArgument(typeof(StringGraphType))
{
Name = "search",
Description = "Optional OData full text search.",
DefaultValue = string.Empty
},
new QueryArgument(typeof(StringGraphType))
{
Name = "orderby",
Description = "Optional OData order definition.",
DefaultValue = string.Empty
}
},
ResolvedType = new ListGraphType(new NonNullGraphType(schemaType)),
Resolver = new FuncFieldResolver<object>(c =>
{
var context = (GraphQLQueryContext)c.UserContext;
var contentQuery = BuildODataQuery(c);
return context.QueryContentsAsync(schema.Id.ToString(), contentQuery);
}),
Description = $"Query {schemaName} content items."
});
}
private static string BuildODataQuery(ResolveFieldContext c)
{
var odataQuery = "?" +
string.Join("&",
c.Arguments
.Select(x => new { x.Key, Value = x.Value.ToString() }).Where(x => !string.IsNullOrWhiteSpace(x.Value))
.Select(x => $"${x.Key}={x.Value}"));
return odataQuery;
}
}
}

37
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/NoopGraphType.cs

@ -1,37 +0,0 @@
// ==========================================================================
// NoopGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using GraphQL.Language.AST;
using GraphQL.Types;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class NoopGraphType : ScalarGraphType
{
public NoopGraphType(string name)
{
Name = name;
}
public override object Serialize(object value)
{
return value;
}
public override object ParseValue(object value)
{
return value;
}
public override object ParseLiteral(IValue value)
{
throw new NotSupportedException();
}
}
}

25
src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs

@ -1,25 +0,0 @@
// ==========================================================================
// IContentEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Read.Contents
{
public interface IContentEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{
Status Status { get; }
NamedContentData Data { get; }
}
}

28
src/Squidex.Domain.Apps.Read/Contents/IContentQueryService.cs

@ -1,28 +0,0 @@
// ==========================================================================
// IContentQueryService.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas;
namespace Squidex.Domain.Apps.Read.Contents
{
public interface IContentQueryService
{
Task<(ISchemaEntity Schema, long Total, IReadOnlyList<IContentEntity> Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, HashSet<Guid> ids);
Task<(ISchemaEntity Schema, long Total, IReadOnlyList<IContentEntity> Items)> QueryWithCountAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, bool archived, string query);
Task<(ISchemaEntity Schema, IContentEntity Content)> FindContentAsync(IAppEntity app, string schemaIdOrName, ClaimsPrincipal user, Guid id);
Task<ISchemaEntity> FindSchemaAsync(IAppEntity app, string schemaIdOrName);
}
}

146
src/Squidex.Domain.Apps.Read/Contents/QueryContext.cs

@ -1,146 +0,0 @@
// ==========================================================================
// QueryContext.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents
{
public class QueryContext
{
private readonly ConcurrentDictionary<Guid, IContentEntity> cachedContents = new ConcurrentDictionary<Guid, IContentEntity>();
private readonly ConcurrentDictionary<Guid, IAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IAssetEntity>();
private readonly IContentQueryService contentQuery;
private readonly IAssetRepository assetRepository;
private readonly IAppEntity app;
private readonly ClaimsPrincipal user;
public QueryContext(
IAppEntity app,
IAssetRepository assetRepository,
IContentQueryService contentQuery,
ClaimsPrincipal user)
{
Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(contentQuery, nameof(contentQuery));
Guard.NotNull(app, nameof(app));
Guard.NotNull(user, nameof(user));
this.assetRepository = assetRepository;
this.contentQuery = contentQuery;
this.user = user;
this.app = app;
}
public async Task<IAssetEntity> FindAssetAsync(Guid id)
{
var asset = cachedAssets.GetOrDefault(id);
if (asset == null)
{
asset = await assetRepository.FindAssetAsync(id);
if (asset != null)
{
cachedAssets[asset.Id] = asset;
}
}
return asset;
}
public async Task<IContentEntity> FindContentAsync(Guid schemaId, Guid id)
{
var content = cachedContents.GetOrDefault(id);
if (content == null)
{
content = (await contentQuery.FindContentAsync(app, schemaId.ToString(), user, id)).Content;
if (content != null)
{
cachedContents[content.Id] = content;
}
}
return content;
}
public async Task<IReadOnlyList<IAssetEntity>> QueryAssetsAsync(string query, int skip = 0, int take = 10)
{
var assets = await assetRepository.QueryAsync(app.Id, null, null, query, take, skip);
foreach (var asset in assets)
{
cachedAssets[asset.Id] = asset;
}
return assets;
}
public async Task<IReadOnlyList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
{
var contents = await contentQuery.QueryWithCountAsync(app, schemaIdOrName, user, false, query);
foreach (var content in contents.Items)
{
cachedContents[content.Id] = content;
}
return contents.Items;
}
public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids)
{
Guard.NotNull(ids, nameof(ids));
var notLoadedAssets = new HashSet<Guid>(ids.Where(id => !cachedAssets.ContainsKey(id)));
if (notLoadedAssets.Count > 0)
{
var assets = await assetRepository.QueryAsync(app.Id, null, notLoadedAssets, null, int.MaxValue);
foreach (var asset in assets)
{
cachedAssets[asset.Id] = asset;
}
}
return ids.Select(id => cachedAssets.GetOrDefault(id)).Where(x => x != null).ToList();
}
public async Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, ICollection<Guid> ids)
{
Guard.NotNull(ids, nameof(ids));
var notLoadedContents = new HashSet<Guid>(ids.Where(id => !cachedContents.ContainsKey(id)));
if (notLoadedContents.Count > 0)
{
var contents = await contentQuery.QueryWithCountAsync(app, schemaId.ToString(), user, false, notLoadedContents);
foreach (var content in contents.Items)
{
cachedContents[content.Id] = content;
}
}
return ids.Select(id => cachedContents.GetOrDefault(id)).Where(x => x != null).ToList();
}
}
}

33
src/Squidex.Domain.Apps.Read/Contents/Repositories/IContentRepository.cs

@ -1,33 +0,0 @@
// ==========================================================================
// IContentRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.OData.UriParser;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Schemas;
namespace Squidex.Domain.Apps.Read.Contents.Repositories
{
public interface IContentRepository
{
Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids);
Task<IReadOnlyList<IContentEntity>> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery);
Task<IReadOnlyList<Guid>> QueryNotFoundAsync(Guid appId, Guid schemaId, IList<Guid> contentIds);
Task<long> CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, HashSet<Guid> ids);
Task<long> CountAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery);
Task<IContentEntity> FindContentAsync(IAppEntity app, ISchemaEntity schema, Guid id);
}
}

90
src/Squidex.Domain.Apps.Read/EntityMapper.cs

@ -1,90 +0,0 @@
// ==========================================================================
// EntityMapper.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read
{
public static class EntityMapper
{
public static T Create<T>(SquidexEvent @event, EnvelopeHeaders headers, Action<T> updater = null) where T : IEntity, new()
{
var entity = new T();
SetId(headers, entity);
SetVersion(headers, entity);
SetCreated(headers, entity);
SetCreatedBy(@event, entity);
SetAppId(@event, entity);
return entity.Update(@event, headers, updater);
}
public static T Update<T>(this T entity, SquidexEvent @event, EnvelopeHeaders headers, Action<T> updater = null) where T : IEntity, new()
{
SetVersion(headers, entity);
SetLastModified(headers, entity);
SetLastModifiedBy(@event, entity);
updater?.Invoke(entity);
return entity;
}
private static void SetId(EnvelopeHeaders headers, IEntity entity)
{
entity.Id = headers.AggregateId();
}
private static void SetCreated(EnvelopeHeaders headers, IEntity entity)
{
entity.Created = headers.Timestamp();
}
private static void SetLastModified(EnvelopeHeaders headers, IEntity entity)
{
entity.LastModified = headers.Timestamp();
}
private static void SetVersion(EnvelopeHeaders headers, IEntity entity)
{
if (entity is IUpdateableEntityWithVersion withVersion)
{
withVersion.Version = headers.EventStreamNumber();
}
}
private static void SetCreatedBy(SquidexEvent @event, IEntity entity)
{
if (entity is IUpdateableEntityWithCreatedBy withCreatedBy && withCreatedBy)
{
withCreatedBy.CreatedBy = @event.Actor;
}
}
private static void SetLastModifiedBy(SquidexEvent @event, IEntity entity)
{
if (entity is IUpdateableEntityWithLastModifiedBy withModifiedBy)
{
withModifiedBy.LastModifiedBy = @event.Actor;
}
}
private static void SetAppId(SquidexEvent @event, IEntity entity)
{
if (entity is IUpdateableEntityWithAppRef app && @event is AppEvent appEvent)
{
app.AppId = appEvent.AppId.Id;
}
}
}
}

44
src/Squidex.Domain.Apps.Read/History/HistoryEventToStore.cs

@ -1,44 +0,0 @@
// ==========================================================================
// HistoryEventToStore.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.History
{
public sealed class HistoryEventToStore
{
private readonly Dictionary<string, string> parameters = new Dictionary<string, string>();
public string Channel { get; }
public string Message { get; }
public IReadOnlyDictionary<string, string> Parameters
{
get { return parameters; }
}
public HistoryEventToStore(string channel, string message)
{
Guard.NotNullOrEmpty(channel, nameof(channel));
Guard.NotNullOrEmpty(message, nameof(message));
Channel = channel;
Message = message;
}
public HistoryEventToStore AddParameter(string key, object value)
{
parameters[key] = value.ToString();
return this;
}
}
}

66
src/Squidex.Domain.Apps.Read/History/HistoryEventsCreatorBase.cs

@ -1,66 +0,0 @@
// ==========================================================================
// HistoryEventsCreatorBase.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.History
{
public abstract class HistoryEventsCreatorBase : IHistoryEventsCreator
{
private readonly Dictionary<string, string> texts = new Dictionary<string, string>();
private readonly TypeNameRegistry typeNameRegistry;
public IReadOnlyDictionary<string, string> Texts
{
get { return texts; }
}
protected HistoryEventsCreatorBase(TypeNameRegistry typeNameRegistry)
{
Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry));
this.typeNameRegistry = typeNameRegistry;
}
protected void AddEventMessage<TEvent>(string message) where TEvent : IEvent
{
Guard.NotNullOrEmpty(message, nameof(message));
texts[typeNameRegistry.GetName<TEvent>()] = message;
}
protected bool HasEventText(IEvent @event)
{
var message = typeNameRegistry.GetName(@event.GetType());
return texts.ContainsKey(message);
}
protected HistoryEventToStore ForEvent(IEvent @event, string channel)
{
var message = typeNameRegistry.GetName(@event.GetType());
return new HistoryEventToStore(channel, message);
}
public Task<HistoryEventToStore> CreateEventAsync(Envelope<IEvent> @event)
{
if (HasEventText(@event.Payload))
{
return CreateEventCoreAsync(@event);
}
return Task.FromResult<HistoryEventToStore>(null);
}
protected abstract Task<HistoryEventToStore> CreateEventCoreAsync(Envelope<IEvent> @event);
}
}

24
src/Squidex.Domain.Apps.Read/History/IHistoryEventEntity.cs

@ -1,24 +0,0 @@
// ==========================================================================
// IHistoryEventEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.History
{
public interface IHistoryEventEntity : IEntity
{
Guid EventId { get; }
RefToken Actor { get; }
string Message { get; }
long Version { get; }
}
}

21
src/Squidex.Domain.Apps.Read/History/IHistoryEventsCreator.cs

@ -1,21 +0,0 @@
// ==========================================================================
// IHistoryEventsCreator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.History
{
public interface IHistoryEventsCreator
{
IReadOnlyDictionary<string, string> Texts { get; }
Task<HistoryEventToStore> CreateEventAsync(Envelope<IEvent> @event);
}
}

19
src/Squidex.Domain.Apps.Read/History/Repositories/IHistoryEventRepository.cs

@ -1,19 +0,0 @@
// ==========================================================================
// IHistoryEventRepository.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.Read.History.Repositories
{
public interface IHistoryEventRepository
{
Task<IReadOnlyList<IHistoryEventEntity>> QueryByChannelAsync(Guid appId, string channelPrefix, int count);
}
}

34
src/Squidex.Domain.Apps.Read/IAppProvider.cs

@ -1,34 +0,0 @@
// ==========================================================================
// IApps.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas;
namespace Squidex.Domain.Apps.Read
{
public interface IAppProvider
{
Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(string appName, Guid id);
Task<IAppEntity> GetAppAsync(string appName);
Task<ISchemaEntity> GetSchemaAsync(string appName, Guid id, bool provideDeleted = false);
Task<ISchemaEntity> GetSchemaAsync(string appName, string name, bool provideDeleted = false);
Task<List<ISchemaEntity>> GetSchemasAsync(string appName);
Task<List<IRuleEntity>> GetRulesAsync(string appName);
Task<List<IAppEntity>> GetUserApps(string userId);
}
}

22
src/Squidex.Domain.Apps.Read/IEntity.cs

@ -1,22 +0,0 @@
// ==========================================================================
// IEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
namespace Squidex.Domain.Apps.Read
{
public interface IEntity
{
Guid Id { get; set; }
Instant Created { get; set; }
Instant LastModified { get; set; }
}
}

17
src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IEntityWithAppRef.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithAppRef
{
Guid AppId { get; }
}
}

17
src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IEntityWithCreatedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithCreatedBy
{
RefToken CreatedBy { get; }
}
}

17
src/Squidex.Domain.Apps.Read/IEntityWithLastModifiedBy.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IEntityWithLastModifiedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithLastModifiedBy
{
RefToken LastModifiedBy { get; set; }
}
}

15
src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs

@ -1,15 +0,0 @@
// ==========================================================================
// IEntityWithVersion.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithVersion
{
long Version { get; }
}
}

17
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IUpdateableEntityWithAppRef.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithAppRef
{
Guid AppId { get; set; }
}
}

17
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IUpdateableEntityWithCreatedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithCreatedBy
{
RefToken CreatedBy { get; set; }
}
}

17
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs

@ -1,17 +0,0 @@
// ==========================================================================
// IUpdateableEntityWithLastModifiedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithLastModifiedBy
{
RefToken LastModifiedBy { get; set; }
}
}

15
src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs

@ -1,15 +0,0 @@
// ==========================================================================
// IUpdateableEntityWithVersion.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read
{
public interface IUpdateableEntityWithVersion
{
long Version { get; set; }
}
}

22
src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs

@ -1,22 +0,0 @@
// ==========================================================================
// IRuleEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules
{
public interface IRuleEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{
Rule RuleDef { get; }
}
}

29
src/Squidex.Domain.Apps.Read/Rules/IRuleEventEntity.cs

@ -1,29 +0,0 @@
// ==========================================================================
// IRuleEventEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules
{
public interface IRuleEventEntity : IEntity
{
RuleJob Job { get; }
Instant? NextAttempt { get; }
RuleJobResult JobResult { get; }
RuleResult Result { get; }
int NumCalls { get; }
string LastDump { get; }
}
}

35
src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs

@ -1,35 +0,0 @@
// ==========================================================================
// IRuleEventRepository.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules.Repositories
{
public interface IRuleEventRepository
{
Task EnqueueAsync(RuleJob job, Instant nextAttempt);
Task EnqueueAsync(Guid id, Instant nextAttempt);
Task MarkSentAsync(Guid jobId, string dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant? nextCall);
Task QueryPendingAsync(Instant now, Func<IRuleEventEntity, Task> callback, CancellationToken cancellationToken = default(CancellationToken));
Task<int> CountByAppAsync(Guid appId);
Task<IReadOnlyList<IRuleEventEntity>> QueryByAppAsync(Guid appId, int skip = 0, int take = 20);
Task<IRuleEventEntity> FindAsync(Guid id);
}
}

157
src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs

@ -1,157 +0,0 @@
// ==========================================================================
// RuleDequeuer.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Read.Rules.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Timers;
namespace Squidex.Domain.Apps.Read.Rules
{
public sealed class RuleDequeuer : DisposableObjectBase, IExternalSystem
{
private readonly ActionBlock<IRuleEventEntity> requestBlock;
private readonly IRuleEventRepository ruleEventRepository;
private readonly RuleService ruleService;
private readonly CompletionTimer timer;
private readonly ConcurrentDictionary<Guid, bool> executing = new ConcurrentDictionary<Guid, bool>();
private readonly IClock clock;
private readonly ISemanticLog log;
public RuleDequeuer(RuleService ruleService, IRuleEventRepository ruleEventRepository, ISemanticLog log, IClock clock)
{
Guard.NotNull(ruleEventRepository, nameof(ruleEventRepository));
Guard.NotNull(ruleService, nameof(ruleService));
Guard.NotNull(clock, nameof(clock));
Guard.NotNull(log, nameof(log));
this.ruleEventRepository = ruleEventRepository;
this.ruleService = ruleService;
this.clock = clock;
this.log = log;
requestBlock =
new ActionBlock<IRuleEventEntity>(HandleAsync,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 32, BoundedCapacity = 32 });
timer = new CompletionTimer(5000, QueryAsync);
}
protected override void DisposeObject(bool disposing)
{
if (disposing)
{
timer.StopAsync().Wait();
requestBlock.Complete();
requestBlock.Completion.Wait();
}
}
public void Connect()
{
}
public void Next()
{
timer.SkipCurrentDelay();
}
private async Task QueryAsync(CancellationToken cancellationToken)
{
try
{
var now = clock.GetCurrentInstant();
await ruleEventRepository.QueryPendingAsync(now, requestBlock.SendAsync, cancellationToken);
}
catch (Exception ex)
{
log.LogError(ex, w => w
.WriteProperty("action", "QueueWebhookEvents")
.WriteProperty("status", "Failed"));
}
}
public async Task HandleAsync(IRuleEventEntity @event)
{
if (!executing.TryAdd(@event.Id, false))
{
return;
}
try
{
var job = @event.Job;
var response = await ruleService.InvokeAsync(job.ActionName, job.ActionData);
var jobInvoke = ComputeJobInvoke(response.Result, @event, job);
var jobResult = ComputeJobResult(response.Result, jobInvoke);
await ruleEventRepository.MarkSentAsync(@event.Id, response.Dump, response.Result, jobResult, response.Elapsed, jobInvoke);
}
catch (Exception ex)
{
log.LogError(ex, w => w
.WriteProperty("action", "SendWebhookEvent")
.WriteProperty("status", "Failed"));
}
finally
{
executing.TryRemove(@event.Id, out var value);
}
}
private static RuleJobResult ComputeJobResult(RuleResult result, Instant? nextCall)
{
if (result != RuleResult.Success && !nextCall.HasValue)
{
return RuleJobResult.Failed;
}
else if (result != RuleResult.Success && nextCall.HasValue)
{
return RuleJobResult.Retry;
}
else
{
return RuleJobResult.Success;
}
}
private static Instant? ComputeJobInvoke(RuleResult result, IRuleEventEntity @event, RuleJob job)
{
if (result != RuleResult.Success)
{
switch (@event.NumCalls)
{
case 0:
return job.Created.Plus(Duration.FromMinutes(5));
case 1:
return job.Created.Plus(Duration.FromHours(1));
case 2:
return job.Created.Plus(Duration.FromHours(6));
case 3:
return job.Created.Plus(Duration.FromHours(12));
}
}
return null;
}
}
}

73
src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs

@ -1,73 +0,0 @@
// ==========================================================================
// RuleEnqueuer.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Read.Rules.Repositories;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.Rules
{
public sealed class RuleEnqueuer : IEventConsumer
{
private readonly IRuleEventRepository ruleEventRepository;
private readonly IAppProvider appProvider;
private readonly RuleService ruleService;
public string Name
{
get { return GetType().Name; }
}
public string EventsFilter
{
get { return ".*"; }
}
public RuleEnqueuer(
IRuleEventRepository ruleEventRepository, IAppProvider appProvider,
RuleService ruleService)
{
Guard.NotNull(ruleEventRepository, nameof(ruleEventRepository));
Guard.NotNull(ruleService, nameof(ruleService));
Guard.NotNull(appProvider, nameof(appProvider));
this.ruleEventRepository = ruleEventRepository;
this.ruleService = ruleService;
this.appProvider = appProvider;
}
public Task ClearAsync()
{
return TaskHelper.Done;
}
public async Task On(Envelope<IEvent> @event)
{
if (@event.Payload is AppEvent appEvent)
{
var rules = await appProvider.GetRulesAsync(appEvent.AppId.Name);
foreach (var ruleEntity in rules)
{
var job = ruleService.CreateJob(ruleEntity.RuleDef, @event);
if (job != null)
{
await ruleEventRepository.EnqueueAsync(job, job.Created);
}
}
}
}
}
}

18
src/Squidex.Domain.Apps.Read/Rules/RuleJobResult.cs

@ -1,18 +0,0 @@
// ==========================================================================
// RuleJobResult.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Read.Rules
{
public enum RuleJobResult
{
Pending,
Success,
Retry,
Failed
}
}

38
src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs

@ -1,38 +0,0 @@
// ==========================================================================
// ISchemaEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.Schemas
{
public interface ISchemaEntity :
IEntity,
IEntityWithAppRef,
IEntityWithCreatedBy,
IEntityWithLastModifiedBy,
IEntityWithVersion
{
string Name { get; }
bool IsPublished { get; }
bool IsDeleted { get; }
string ScriptQuery { get; }
string ScriptCreate { get; }
string ScriptUpdate { get; }
string ScriptDelete { get; }
string ScriptChange { get; }
Schema SchemaDef { get; }
}
}

88
src/Squidex.Domain.Apps.Read/Schemas/SchemaHistoryEventsCreator.cs

@ -1,88 +0,0 @@
// ==========================================================================
// SchemaHistoryEventsCreator.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Read.History;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.Schemas
{
public sealed class SchemaHistoryEventsCreator : HistoryEventsCreatorBase
{
public SchemaHistoryEventsCreator(TypeNameRegistry typeNameRegistry)
: base(typeNameRegistry)
{
AddEventMessage<SchemaCreated>(
"created schema {[Name]}");
AddEventMessage<SchemaUpdated>(
"updated schema {[Name]}");
AddEventMessage<SchemaDeleted>(
"deleted schema {[Name]}");
AddEventMessage<SchemaPublished>(
"published schema {[Name]}");
AddEventMessage<SchemaUnpublished>(
"unpublished schema {[Name]}");
AddEventMessage<SchemaFieldsReordered>(
"reordered fields of schema {[Name]}");
AddEventMessage<FieldAdded>(
"added field {[Field]} to schema {[Name]}");
AddEventMessage<FieldDeleted>(
"deleted field {[Field]} from schema {[Name]}");
AddEventMessage<FieldLocked>(
"has locked field {[Field]} of schema {[Name]}");
AddEventMessage<FieldHidden>(
"has hidden field {[Field]} of schema {[Name]}");
AddEventMessage<FieldShown>(
"has shown field {[Field]} of schema {[Name]}");
AddEventMessage<FieldDisabled>(
"disabled field {[Field]} of schema {[Name]}");
AddEventMessage<FieldEnabled>(
"disabled field {[Field]} of schema {[Name]}");
AddEventMessage<FieldUpdated>(
"has updated field {[Field]} of schema {[Name]}");
AddEventMessage<FieldDeleted>(
"deleted field {[Field]} of schema {[Name]}");
}
protected override Task<HistoryEventToStore> CreateEventCoreAsync(Envelope<IEvent> @event)
{
if (@event.Payload is SchemaEvent schemaEvent)
{
var channel = $"schemas.{schemaEvent.SchemaId.Name}";
var result = ForEvent(@event.Payload, channel).AddParameter("Name", schemaEvent.SchemaId.Name);
if (schemaEvent is FieldEvent fieldEvent)
{
result.AddParameter("Field", fieldEvent.FieldId.Name);
}
return Task.FromResult(result);
}
return Task.FromResult<HistoryEventToStore>(null);
}
}
}

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

@ -1,27 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj" />
<ProjectReference Include="..\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GraphQL" Version="0.17.3" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.0.0" />
<PackageReference Include="NodaTime" Version="2.2.3" />
<PackageReference Include="RefactoringEssentials" Version="5.4.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" />
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.8.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\Squidex.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>

87
src/Squidex.Domain.Apps.Read/State/AppProvider.cs

@ -1,87 +0,0 @@
// ==========================================================================
// 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.Read.Apps;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.State.Grains;
using Squidex.Infrastructure;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Read.State
{
public sealed class AppProvider : IAppProvider
{
private readonly IStateFactory factory;
public AppProvider(IStateFactory factory)
{
Guard.NotNull(factory, nameof(factory));
this.factory = factory;
}
public async Task<IAppEntity> GetAppAsync(string appName)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetAppAsync();
}
public async Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(string appName, Guid id)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetAppWithSchemaAsync(id);
}
public async Task<List<IRuleEntity>> GetRulesAsync(string appName)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetRulesAsync();
}
public async Task<ISchemaEntity> GetSchemaAsync(string appName, Guid id, bool provideDeleted = false)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetSchemaAsync(id, provideDeleted);
}
public async Task<ISchemaEntity> GetSchemaAsync(string appName, string name, bool provideDeleted = false)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetSchemaAsync(name, provideDeleted);
}
public async Task<List<ISchemaEntity>> GetSchemasAsync(string appName)
{
var app = await factory.GetSynchronizedAsync<AppStateGrain>(appName);
return await app.GetSchemasAsync();
}
public async Task<List<IAppEntity>> GetUserApps(string userId)
{
var appUser = await factory.GetSynchronizedAsync<AppUserGrain>(userId);
var appNames = await appUser.GetAppNamesAsync();
var tasks = appNames.Select(x => GetAppAsync(x));
var apps = await Task.WhenAll(tasks);
return apps.Where(a => a != null && a.Contributors.ContainsKey(userId)).ToList();
}
}
}

70
src/Squidex.Domain.Apps.Read/State/AppStateEventConsumer.cs

@ -1,70 +0,0 @@
// ==========================================================================
// AppStateEventConsumer.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Threading.Tasks;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.State.Grains;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.State
{
public sealed class AppStateEventConsumer : IEventConsumer
{
private readonly IStateFactory factory;
public string Name
{
get { return typeof(AppStateEventConsumer).Name; }
}
public string EventsFilter
{
get { return @"(^app-)|(^schema-)|(^rule\-)"; }
}
public AppStateEventConsumer(IStateFactory factory)
{
Guard.NotNull(factory, nameof(factory));
this.factory = factory;
}
public Task ClearAsync()
{
return TaskHelper.Done;
}
public async Task On(Envelope<IEvent> @event)
{
if (@event.Payload is AppEvent appEvent)
{
var appGrain = await factory.GetSynchronizedAsync<AppStateGrain>(appEvent.AppId.Name);
await appGrain.HandleAsync(@event);
}
if (@event.Payload is AppContributorAssigned contributorAssigned)
{
var userGrain = await factory.GetSynchronizedAsync<AppUserGrain>(contributorAssigned.ContributorId);
await userGrain.AddAppAsync(contributorAssigned.AppId.Name);
}
if (@event.Payload is AppContributorRemoved contributorRemoved)
{
var userGrain = await factory.GetSynchronizedAsync<AppUserGrain>(contributorRemoved.ContributorId);
await userGrain.RemoveAppAsync(contributorRemoved.AppId.Name);
}
}
}
}

134
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrain.cs

@ -1,134 +0,0 @@
// ==========================================================================
// AppStateGrain.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public class AppStateGrain : IStatefulObject
{
private readonly FieldRegistry fieldRegistry;
private IPersistence<AppStateGrainState> persistence;
private Exception exception;
private AppStateGrainState state;
public AppStateGrain(FieldRegistry fieldRegistry)
{
Guard.NotNull(fieldRegistry, nameof(fieldRegistry));
this.fieldRegistry = fieldRegistry;
}
public async Task ActivateAsync(string key, IStore store)
{
persistence = store.WithSnapshots<AppStateGrain, AppStateGrainState>(key, s => state = s);
try
{
await persistence.ReadAsync();
}
catch (Exception ex)
{
exception = ex;
}
if (state == null)
{
state = new AppStateGrainState();
}
state.SetRegistry(fieldRegistry);
}
public virtual Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid id)
{
var schema = state.FindSchema(x => x.Id == id && !x.IsDeleted);
return Task.FromResult((state.GetApp(), schema));
}
public virtual Task<IAppEntity> GetAppAsync()
{
var result = state.GetApp();
return Task.FromResult(result);
}
public virtual Task<List<IRuleEntity>> GetRulesAsync()
{
var result = state.FindRules();
return Task.FromResult(result);
}
public virtual Task<List<ISchemaEntity>> GetSchemasAsync()
{
var result = state.FindSchemas(x => !x.IsDeleted);
return Task.FromResult(result);
}
public virtual Task<ISchemaEntity> GetSchemaAsync(Guid id, bool provideDeleted = false)
{
var result = state.FindSchema(x => x.Id == id && (!x.IsDeleted || provideDeleted));
return Task.FromResult(result);
}
public virtual Task<ISchemaEntity> GetSchemaAsync(string name, bool provideDeleted = false)
{
var result = state.FindSchema(x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) && (!x.IsDeleted || provideDeleted));
return Task.FromResult(result);
}
public async virtual Task HandleAsync(Envelope<IEvent> message)
{
if (exception != null)
{
if (message.Payload is AppCreated)
{
exception = null;
}
else
{
throw exception;
}
}
if (message.Payload is AppEvent appEvent && (state.App == null || state.App.Id == appEvent.AppId.Id))
{
try
{
state = state.Apply(message);
await persistence.WriteSnapshotAsync(state);
}
catch (InconsistentStateException)
{
await persistence.ReadAsync();
state = state.Apply(message);
await persistence.WriteSnapshotAsync(state);
}
}
}
}
}

75
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState.cs

@ -1,75 +0,0 @@
// ==========================================================================
// AppStateGrainState.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed partial class AppStateGrainState : Cloneable<AppStateGrainState>
{
private FieldRegistry registry;
[JsonProperty]
public JsonAppEntity App { get; set; }
[JsonProperty]
public ImmutableDictionary<Guid, JsonRuleEntity> Rules { get; set; } = ImmutableDictionary<Guid, JsonRuleEntity>.Empty;
[JsonProperty]
public ImmutableDictionary<Guid, JsonSchemaEntity> Schemas { get; set; } = ImmutableDictionary<Guid, JsonSchemaEntity>.Empty;
public void SetRegistry(FieldRegistry registry)
{
this.registry = registry;
}
public IAppEntity GetApp()
{
return App;
}
public ISchemaEntity FindSchema(Func<JsonSchemaEntity, bool> filter)
{
return Schemas?.Values.FirstOrDefault(filter);
}
public List<ISchemaEntity> FindSchemas(Func<JsonSchemaEntity, bool> filter)
{
return Schemas?.Values.Where(filter).OfType<ISchemaEntity>().ToList() ?? new List<ISchemaEntity>();
}
public List<IRuleEntity> FindRules()
{
return Rules?.Values.OfType<IRuleEntity>().ToList() ?? new List<IRuleEntity>();
}
public AppStateGrainState Apply(Envelope<IEvent> envelope)
{
return Clone(c =>
{
c.DispatchAction(envelope.Payload, envelope.Headers);
if (c.App != null)
{
c.App.Etag = Guid.NewGuid().ToString();
}
});
}
}
}

117
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Apps.cs

@ -1,117 +0,0 @@
// ==========================================================================
// AppStateGrainState_Apps.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed partial class AppStateGrainState
{
public void On(AppCreated @event, EnvelopeHeaders headers)
{
App = EntityMapper.Create<JsonAppEntity>(@event, headers, a =>
{
SimpleMapper.Map(@event, a);
a.LanguagesConfig = LanguagesConfig.Build(Language.EN);
});
}
public void On(AppLanguageAdded @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.LanguagesConfig = a.LanguagesConfig.Apply(@event);
});
}
public void On(AppLanguageRemoved @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.LanguagesConfig = a.LanguagesConfig.Apply(@event);
});
}
public void On(AppLanguageUpdated @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.LanguagesConfig = a.LanguagesConfig.Apply(@event);
});
}
public void On(AppContributorAssigned @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Contributors = a.Contributors.Apply(@event);
});
}
public void On(AppContributorRemoved @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Contributors = a.Contributors.Apply(@event);
});
}
public void On(AppClientAttached @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Clients = a.Clients.Apply(@event);
});
}
public void On(AppClientUpdated @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Clients = a.Clients.Apply(@event);
});
}
public void On(AppClientRenamed @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Clients = a.Clients.Apply(@event);
});
}
public void On(AppClientRevoked @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
a.Clients = a.Clients.Apply(@event);
});
}
public void On(AppPlanChanged @event, EnvelopeHeaders headers)
{
UpdateApp(@event, headers, a =>
{
SimpleMapper.Map(@event, a);
});
}
private void UpdateApp(AppEvent @event, EnvelopeHeaders headers, Action<JsonAppEntity> updater = null)
{
App = App.Clone().Update(@event, headers, updater);
}
}
}

65
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Rules.cs

@ -1,65 +0,0 @@
// ==========================================================================
// AppStateGrainState_Rules.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Domain.Apps.Events.Rules.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed partial class AppStateGrainState
{
public void On(RuleCreated @event, EnvelopeHeaders headers)
{
var id = @event.RuleId;
Rules = Rules.SetItem(id, EntityMapper.Create<JsonRuleEntity>(@event, headers, r =>
{
r.RuleDef = RuleEventDispatcher.Create(@event);
}));
}
public void On(RuleUpdated @event, EnvelopeHeaders headers)
{
UpdateRule(@event, headers, r =>
{
r.RuleDef = r.RuleDef.Apply(@event);
});
}
public void On(RuleEnabled @event, EnvelopeHeaders headers)
{
UpdateRule(@event, headers, r =>
{
r.RuleDef = r.RuleDef.Apply(@event);
});
}
public void On(RuleDisabled @event, EnvelopeHeaders headers)
{
UpdateRule(@event, headers, r =>
{
r.RuleDef = r.RuleDef.Apply(@event);
});
}
public void On(RuleDeleted @event, EnvelopeHeaders headers)
{
Rules = Rules.Remove(@event.RuleId);
}
private void UpdateRule(RuleEvent @event, EnvelopeHeaders headers, Action<JsonRuleEntity> updater = null)
{
var id = @event.RuleId;
Rules = Rules.SetItem(id, x => x.Clone().Update(@event, headers, updater));
}
}
}

162
src/Squidex.Domain.Apps.Read/State/Grains/AppStateGrainState_Schemas.cs

@ -1,162 +0,0 @@
// ==========================================================================
// AppStateGrainState_Schemas.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Domain.Apps.Events.Schemas.Old;
using Squidex.Domain.Apps.Events.Schemas.Utils;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
#pragma warning disable CS0612 // Type or member is obsolete
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed partial class AppStateGrainState
{
public void On(SchemaCreated @event, EnvelopeHeaders headers)
{
var id = @event.SchemaId.Id;
Schemas = Schemas.SetItem(id, EntityMapper.Create<JsonSchemaEntity>(@event, headers, s =>
{
s.SchemaDef = SchemaEventDispatcher.Create(@event, registry);
SimpleMapper.Map(@event, s);
}));
}
public void On(SchemaPublished @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(SchemaUnpublished @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(ScriptsConfigured @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
SimpleMapper.Map(s, @event);
});
}
public void On(SchemaUpdated @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(SchemaFieldsReordered @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldAdded @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event, registry);
});
}
public void On(FieldUpdated @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldLocked @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldDisabled @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldEnabled @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldHidden @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldShown @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(FieldDeleted @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers, s =>
{
s.SchemaDef = s.SchemaDef.Apply(@event);
});
}
public void On(WebhookAdded @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers);
}
public void On(WebhookDeleted @event, EnvelopeHeaders headers)
{
UpdateSchema(@event, headers);
}
public void On(SchemaDeleted @event, EnvelopeHeaders headers)
{
Schemas = Schemas.Remove(@event.SchemaId.Id);
}
private void UpdateSchema(SchemaEvent @event, EnvelopeHeaders headers, Action<JsonSchemaEntity> updater = null)
{
var id = @event.SchemaId.Id;
Schemas = Schemas.SetItem(id, x => x.Clone().Update(@event, headers, updater));
}
}
}

48
src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrain.cs

@ -1,48 +0,0 @@
// ==========================================================================
// AppUserGrain.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed class AppUserGrain : IStatefulObject
{
private IPersistence<AppUserGrainState> persistence;
private AppUserGrainState state = new AppUserGrainState();
public Task ActivateAsync(string key, IStore store)
{
persistence = store.WithSnapshots<AppUserGrain, AppUserGrainState>(key, s => state = s);
return persistence.ReadAsync();
}
public Task AddAppAsync(string appName)
{
state = state.AddApp(appName);
return persistence.WriteSnapshotAsync(state);
}
public Task RemoveAppAsync(string appName)
{
state = state.RemoveApp(appName);
return persistence.WriteSnapshotAsync(state);
}
public Task<List<string>> GetAppNamesAsync()
{
return Task.FromResult(state.AppNames.ToList());
}
}
}

30
src/Squidex.Domain.Apps.Read/State/Grains/AppUserGrainState.cs

@ -1,30 +0,0 @@
// ==========================================================================
// AppUserGrainState.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Immutable;
using Newtonsoft.Json;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed class AppUserGrainState : Cloneable<AppUserGrainState>
{
[JsonProperty]
public ImmutableHashSet<string> AppNames { get; set; } = ImmutableHashSet<string>.Empty;
public AppUserGrainState AddApp(string appName)
{
return Clone(c => c.AppNames = c.AppNames.Add(appName));
}
public AppUserGrainState RemoveApp(string appName)
{
return Clone(c => c.AppNames = c.AppNames.Remove(appName));
}
}
}

38
src/Squidex.Domain.Apps.Read/State/Grains/JsonAppEntity.cs

@ -1,38 +0,0 @@
// ==========================================================================
// JsonAppEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Read.Apps;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed class JsonAppEntity : JsonEntity<JsonAppEntity>, IAppEntity
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string PlanId { get; set; }
[JsonProperty]
public string Etag { get; set; }
[JsonProperty]
public string PlanOwner { get; set; }
[JsonProperty]
public AppClients Clients { get; set; } = AppClients.Empty;
[JsonProperty]
public AppContributors Contributors { get; set; } = AppContributors.Empty;
[JsonProperty]
public LanguagesConfig LanguagesConfig { get; set; }
}
}

35
src/Squidex.Domain.Apps.Read/State/Grains/JsonEntity.cs

@ -1,35 +0,0 @@
// ==========================================================================
// JsonEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using NodaTime;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public abstract class JsonEntity<T> : Cloneable<T>, IUpdateableEntityWithVersion where T : Cloneable
{
[JsonProperty]
public Guid Id { get; set; }
[JsonProperty]
public Instant Created { get; set; }
[JsonProperty]
public Instant LastModified { get; set; }
[JsonProperty]
public long Version { get; set; }
public T Clone()
{
return Clone(x => { });
}
}
}

36
src/Squidex.Domain.Apps.Read/State/Grains/JsonRuleEntity.cs

@ -1,36 +0,0 @@
// ==========================================================================
// JsonRuleEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Read.Rules;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed class JsonRuleEntity :
JsonEntity<JsonRuleEntity>,
IRuleEntity,
IUpdateableEntityWithAppRef,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy
{
[JsonProperty]
public Guid AppId { get; set; }
[JsonProperty]
public RefToken CreatedBy { get; set; }
[JsonProperty]
public RefToken LastModifiedBy { get; set; }
[JsonProperty]
public Rule RuleDef { get; set; }
}
}

63
src/Squidex.Domain.Apps.Read/State/Grains/JsonSchemaEntity.cs

@ -1,63 +0,0 @@
// ==========================================================================
// JsonSchemaEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.State.Grains
{
public sealed class JsonSchemaEntity :
JsonEntity<JsonSchemaEntity>,
ISchemaEntity,
IUpdateableEntityWithAppRef,
IUpdateableEntityWithCreatedBy,
IUpdateableEntityWithLastModifiedBy
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public Guid AppId { get; set; }
[JsonProperty]
public RefToken CreatedBy { get; set; }
[JsonProperty]
public RefToken LastModifiedBy { get; set; }
[JsonProperty]
public bool IsDeleted { get; set; }
[JsonProperty]
public string ScriptQuery { get; set; }
[JsonProperty]
public string ScriptCreate { get; set; }
[JsonProperty]
public string ScriptUpdate { get; set; }
[JsonProperty]
public string ScriptDelete { get; set; }
[JsonProperty]
public string ScriptChange { get; set; }
[JsonProperty]
public Schema SchemaDef { get; set; }
[JsonIgnore]
public bool IsPublished
{
get { return SchemaDef.IsPublished; }
}
}
}

21
src/Squidex.Domain.Apps.Write/AppAggregateCommand.cs

@ -1,21 +0,0 @@
// ==========================================================================
// AppAggregateCommand.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write
{
public class AppAggregateCommand : AppCommand, IAggregateCommand
{
Guid IAggregateCommand.AggregateId
{
get { return AppId.Id; }
}
}
}

18
src/Squidex.Domain.Apps.Write/AppCommand.cs

@ -1,18 +0,0 @@
// ==========================================================================
// AppCommand.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write
{
public abstract class AppCommand : SquidexCommand
{
public NamedId<Guid> AppId { get; set; }
}
}

174
src/Squidex.Domain.Apps.Write/Apps/AppCommandMiddleware.cs

@ -1,174 +0,0 @@
// ==========================================================================
// AppCommandMiddleware.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
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.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Dispatching;
using Squidex.Shared.Users;
namespace Squidex.Domain.Apps.Write.Apps
{
public class AppCommandMiddleware : ICommandMiddleware
{
private readonly IAggregateHandler handler;
private readonly IAppProvider appProvider;
private readonly IAppPlansProvider appPlansProvider;
private readonly IAppPlanBillingManager appPlansBillingManager;
private readonly IUserResolver userResolver;
public AppCommandMiddleware(
IAggregateHandler handler,
IAppProvider appProvider,
IAppPlansProvider appPlansProvider,
IAppPlanBillingManager appPlansBillingManager,
IUserResolver userResolver)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(userResolver, nameof(userResolver));
Guard.NotNull(appPlansProvider, nameof(appPlansProvider));
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager));
this.handler = handler;
this.userResolver = userResolver;
this.appProvider = appProvider;
this.appPlansProvider = appPlansProvider;
this.appPlansBillingManager = appPlansBillingManager;
}
protected async Task On(CreateApp command, CommandContext context)
{
await handler.CreateAsync<AppDomainObject>(context, async a =>
{
await GuardApp.CanCreate(command, appProvider);
a.Create(command);
context.Complete(EntityCreatedResult.Create(a.Id, a.Version));
});
}
protected async Task On(AssignContributor command, CommandContext context)
{
await handler.UpdateAsync<AppDomainObject>(context, async a =>
{
await GuardAppContributors.CanAssign(a.Contributors, command, userResolver, appPlansProvider.GetPlan(a.Plan?.PlanId));
a.AssignContributor(command);
});
}
protected Task On(RemoveContributor command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppContributors.CanRemove(a.Contributors, command);
a.RemoveContributor(command);
});
}
protected Task On(AttachClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanAttach(a.Clients, command);
a.AttachClient(command);
});
}
protected Task On(UpdateClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanUpdate(a.Clients, command);
a.UpdateClient(command);
});
}
protected Task On(RevokeClient command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanRevoke(a.Clients, command);
a.RevokeClient(command);
});
}
protected Task On(AddLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanAdd(a.LanguagesConfig, command);
a.AddLanguage(command);
});
}
protected Task On(RemoveLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanRemove(a.LanguagesConfig, command);
a.RemoveLanguage(command);
});
}
protected Task On(UpdateLanguage command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanUpdate(a.LanguagesConfig, command);
a.UpdateLanguage(command);
});
}
protected Task On(ChangePlan command, CommandContext context)
{
return handler.UpdateAsync<AppDomainObject>(context, async a =>
{
GuardApp.CanChangePlan(command, a.Plan, appPlansProvider);
if (command.FromCallback)
{
a.ChangePlan(command);
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, a.Id, a.Name, command.PlanId);
if (result is PlanChangedResult)
{
a.ChangePlan(command);
}
context.Complete(result);
}
});
}
public async Task HandleAsync(CommandContext context, Func<Task> next)
{
if (!await this.DispatchActionAsync(context.Command, context))
{
await next();
}
}
}
}

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

@ -1,260 +0,0 @@
// ==========================================================================
// AppDomainObject.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Domain.Apps.Events.Apps.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
{
public class AppDomainObject : DomainObjectBase
{
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, CreateInitialOwner(appId, command)));
RaiseEvent(SimpleMapper.Map(command, CreateInitialLanguage(appId)));
return this;
}
public AppDomainObject UpdateClient(UpdateClient command)
{
ThrowIfNotCreated();
if (!string.IsNullOrWhiteSpace(command.Name))
{
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed()));
}
if (command.Permission.HasValue)
{
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value }));
}
return this;
}
public AppDomainObject AssignContributor(AssignContributor command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned()));
return this;
}
public AppDomainObject RemoveContributor(RemoveContributor command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
return this;
}
public AppDomainObject AttachClient(AttachClient command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached()));
return this;
}
public AppDomainObject RevokeClient(RevokeClient command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
return this;
}
public AppDomainObject AddLanguage(AddLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded()));
return this;
}
public AppDomainObject RemoveLanguage(RemoveLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved()));
return this;
}
public AppDomainObject UpdateLanguage(UpdateLanguage command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
return this;
}
public AppDomainObject ChangePlan(ChangePlan command)
{
ThrowIfNotCreated();
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged()));
return this;
}
private void RaiseEvent(AppEvent @event)
{
if (@event.AppId == null)
{
@event.AppId = new NamedId<Guid>(Id, name);
}
RaiseEvent(Envelope.Create(@event));
}
private static AppLanguageAdded CreateInitialLanguage(NamedId<Guid> id)
{
return new AppLanguageAdded { AppId = id, Language = Language.EN };
}
private static AppContributorAssigned CreateInitialOwner(NamedId<Guid> id, SquidexCommand command)
{
return new AppContributorAssigned { AppId = id, ContributorId = command.Actor.Identifier, Permission = AppContributorPermission.Owner };
}
private void ThrowIfNotCreated()
{
if (string.IsNullOrWhiteSpace(name))
{
throw new DomainException("App has not been created.");
}
}
private void ThrowIfCreated()
{
if (!string.IsNullOrWhiteSpace(name))
{
throw new DomainException("App has already been created.");
}
}
}
}

17
src/Squidex.Domain.Apps.Write/Apps/Commands/AddLanguage.cs

@ -1,17 +0,0 @@
// ==========================================================================
// AddLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class AddLanguage : AppAggregateCommand
{
public Language Language { get; set; }
}
}

19
src/Squidex.Domain.Apps.Write/Apps/Commands/AssignContributor.cs

@ -1,19 +0,0 @@
// ==========================================================================
// AssignContributor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class AssignContributor : AppAggregateCommand
{
public string ContributorId { get; set; }
public AppContributorPermission Permission { get; set; }
}
}

19
src/Squidex.Domain.Apps.Write/Apps/Commands/AttachClient.cs

@ -1,19 +0,0 @@
// ==========================================================================
// AttachClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class AttachClient : AppAggregateCommand
{
public string Id { get; set; }
public string Secret { get; } = RandomHash.New();
}
}

17
src/Squidex.Domain.Apps.Write/Apps/Commands/ChangePlan.cs

@ -1,17 +0,0 @@
// ==========================================================================
// ChangePlan.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class ChangePlan : AppAggregateCommand
{
public bool FromCallback { get; set; }
public string PlanId { get; set; }
}
}

30
src/Squidex.Domain.Apps.Write/Apps/Commands/CreateApp.cs

@ -1,30 +0,0 @@
// ==========================================================================
// CreateApp.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using Squidex.Infrastructure.Commands;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class CreateApp : SquidexCommand, IAggregateCommand
{
public string Name { get; set; }
public Guid AppId { get; set; }
Guid IAggregateCommand.AggregateId
{
get { return AppId; }
}
public CreateApp()
{
AppId = Guid.NewGuid();
}
}
}

15
src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveContributor.cs

@ -1,15 +0,0 @@
// ==========================================================================
// RemoveContributor.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class RemoveContributor : AppAggregateCommand
{
public string ContributorId { get; set; }
}
}

17
src/Squidex.Domain.Apps.Write/Apps/Commands/RemoveLanguage.cs

@ -1,17 +0,0 @@
// ==========================================================================
// RemoveLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class RemoveLanguage : AppAggregateCommand
{
public Language Language { get; set; }
}
}

15
src/Squidex.Domain.Apps.Write/Apps/Commands/RevokeClient.cs

@ -1,15 +0,0 @@
// ==========================================================================
// RevokeClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class RevokeClient : AppAggregateCommand
{
public string Id { get; set; }
}
}

21
src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateClient.cs

@ -1,21 +0,0 @@
// ==========================================================================
// RenameClient.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class UpdateClient : AppAggregateCommand
{
public string Id { get; set; }
public string Name { get; set; }
public AppClientPermission? Permission { get; set; }
}
}

24
src/Squidex.Domain.Apps.Write/Apps/Commands/UpdateLanguage.cs

@ -1,24 +0,0 @@
// ==========================================================================
// UpdateLanguage.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Write.Apps.Commands
{
public sealed class UpdateLanguage : AppAggregateCommand
{
public Language Language { get; set; }
public bool IsOptional { get; set; }
public bool IsMaster { get; set; }
public List<Language> Fallback { get; set; }
}
}

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

Loading…
Cancel
Save