diff --git a/.drone.yml b/.drone.yml
index b72edbb34..389a52a4a 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -21,7 +21,7 @@ pipeline:
secrets: [ docker_username, docker_password ]
when:
event: push
- branch: [master]
+ branch: [ master ]
build_release:
image: docker
diff --git a/Dockerfile b/Dockerfile
index c02ef4137..8135ffc5f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
#
# Stage 1, Prebuild
#
-FROM microsoft/aspnetcore-build:2.0.0-jessie as builder
+FROM microsoft/aspnetcore-build:2.0.3-jessie as builder
# Install runtime dependencies
RUN apt-get update \
@@ -45,7 +45,7 @@ RUN cp -a /tmp/node_modules /src/Squidex/ \
# Test Backend
RUN dotnet restore \
- && dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj \
+ && dotnet test tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj -v d \
&& dotnet test tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj \
&& dotnet test tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj \
@@ -57,7 +57,7 @@ RUN dotnet publish src/Squidex/Squidex.csproj --output /out/ --configuration Rel
#
# Stage 2, Build runtime
#
-FROM microsoft/aspnetcore:2.0.0-jessie
+FROM microsoft/aspnetcore:2.0.3-jessie
# Default AspNetCore directory
WORKDIR /app
@@ -66,5 +66,7 @@ WORKDIR /app
COPY --from=builder /out/ .
EXPOSE 80
+EXPOSE 33333
+EXPOSE 40000
ENTRYPOINT ["dotnet", "Squidex.dll"]
\ No newline at end of file
diff --git a/NuGet.Config b/NuGet.Config
index 3f0e00340..c982f5720 100644
--- a/NuGet.Config
+++ b/NuGet.Config
@@ -1,6 +1,7 @@
+
\ No newline at end of file
diff --git a/Squidex.sln b/Squidex.sln
index 81f85c0f9..21ac0f2e4 100644
--- a/Squidex.sln
+++ b/Squidex.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-VisualStudioVersion = 15.0.27004.2002
+VisualStudioVersion = 15.0.27004.2009
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex", "src\Squidex\Squidex.csproj", "{61F6BBCE-A080-4400-B194-70E2F5D2096E}"
EndProject
@@ -34,10 +34,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.Rabb
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure.GoogleCloud", "src\Squidex.Infrastructure.GoogleCloud\Squidex.Infrastructure.GoogleCloud.csproj", "{945871B1-77B8-43FB-B53C-27CF385AB756}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{B56EBCEC-9C50-46A7-848C-65502DE69C5C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "tests\Benchmarks\Benchmarks.csproj", "{D48A03DF-BCD3-4667-8747-2F251347E2B6}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{94207AA6-4923-4183-A558-E0F8196B8CA3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Migrate_01", "tools\Migrate_01\Migrate_01.csproj", "{B51126A8-0D75-4A79-867D-10724EC6AC84}"
@@ -67,6 +63,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Mo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "tests\Benchmarks\Benchmarks.csproj", "{9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -197,18 +195,6 @@ Global
{945871B1-77B8-43FB-B53C-27CF385AB756}.Release|x64.Build.0 = Release|Any CPU
{945871B1-77B8-43FB-B53C-27CF385AB756}.Release|x86.ActiveCfg = Release|Any CPU
{945871B1-77B8-43FB-B53C-27CF385AB756}.Release|x86.Build.0 = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|x64.ActiveCfg = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|x64.Build.0 = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|x86.ActiveCfg = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Debug|x86.Build.0 = Debug|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|Any CPU.Build.0 = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|x64.ActiveCfg = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|x64.Build.0 = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|x86.ActiveCfg = Release|Any CPU
- {D48A03DF-BCD3-4667-8747-2F251347E2B6}.Release|x86.Build.0 = Release|Any CPU
{B51126A8-0D75-4A79-867D-10724EC6AC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B51126A8-0D75-4A79-867D-10724EC6AC84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B51126A8-0D75-4A79-867D-10724EC6AC84}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -317,6 +303,18 @@ Global
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x64.Build.0 = Release|Any CPU
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x86.ActiveCfg = Release|Any CPU
{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}.Release|x86.Build.0 = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x64.Build.0 = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Debug|x86.Build.0 = Debug|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x64.ActiveCfg = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x64.Build.0 = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x86.ActiveCfg = Release|Any CPU
+ {9B4A55F4-D9A4-4FC3-8D85-02A9EF93FBAB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -335,7 +333,6 @@ Global
{D7166C56-178A-4457-B56A-C615C7450DEE} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{C1E5BBB6-6B6A-4DE5-B19D-0538304DE343} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
{945871B1-77B8-43FB-B53C-27CF385AB756} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF}
- {D48A03DF-BCD3-4667-8747-2F251347E2B6} = {B56EBCEC-9C50-46A7-848C-65502DE69C5C}
{B51126A8-0D75-4A79-867D-10724EC6AC84} = {94207AA6-4923-4183-A558-E0F8196B8CA3}
{5E75AB7D-6F01-4313-AFF1-7F7128FFD71F} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A}
{C9809D59-6665-471E-AD87-5AC624C65892} = {4C6B06C2-6D77-4E0E-AE32-D7050236433A}
diff --git a/Squidex.sln.DotSettings b/Squidex.sln.DotSettings
index 0e0d4202d..235489b1a 100644
--- a/Squidex.sln.DotSettings
+++ b/Squidex.sln.DotSettings
@@ -5,12 +5,25 @@
True
False
True
+
+ False
+ True
+
False
True
+
+
+ False
+ True
+
False
True
+
+
False
True
+
+
True
@@ -18,6 +31,8 @@
+
+
<?xml version="1.0" encoding="utf-16"?><Profile name="Header"><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile>
<?xml version="1.0" encoding="utf-16"?><Profile name="Namespaces"><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSUpdateFileHeader>True</CSUpdateFileHeader></Profile>
<?xml version="1.0" encoding="utf-16"?><Profile name="Typescript"><JsInsertSemicolon>True</JsInsertSemicolon><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CorrectVariableKindsDescriptor>True</CorrectVariableKindsDescriptor><VariablesToInnerScopesDescriptor>True</VariablesToInnerScopesDescriptor><StringToTemplatesDescriptor>True</StringToTemplatesDescriptor><RemoveRedundantQualifiersTs>True</RemoveRedundantQualifiersTs><OptimizeImportsTs>True</OptimizeImportsTs></Profile>
diff --git a/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg
new file mode 100644
index 000000000..c719a733a
Binary files /dev/null and b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg differ
diff --git a/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg.sha512 b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg.sha512
new file mode 100644
index 000000000..11c0b5549
--- /dev/null
+++ b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.2.0.0-beta1-fix.nupkg.sha512
@@ -0,0 +1 @@
+5W20j9jiNog4dHUEt+cCnePb8z6jFEMnkwO4XilajM7FCnen3KTnN/G8PAUGuQieSlTI9MRe0sRYcafLJl900w==
\ No newline at end of file
diff --git a/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.nuspec b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.nuspec
new file mode 100644
index 000000000..d685caf66
--- /dev/null
+++ b/libs/microsoft.orleans.orleanscodegenerator.build/2.0.0-beta1-fix/microsoft.orleans.orleanscodegenerator.build.nuspec
@@ -0,0 +1,23 @@
+
+
+
+ Microsoft.Orleans.OrleansCodeGenerator.Build
+ 2.0.0-beta1-fix
+ Microsoft Orleans Build-time Code Generator
+ Microsoft
+ Microsoft
+ false
+ true
+ https://github.com/dotnet/Orleans#license
+ https://github.com/dotnet/Orleans
+ https://raw.githubusercontent.com/dotnet/orleans/gh-pages/assets/logo_128.png
+ Microsoft Orleans build-time code generator to install in all grain interface & implementation projects.
+ © Microsoft Corporation. All rights reserved.
+ Orleans Cloud-Computing Actor-Model Actors Distributed-Systems C# .NET
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg
new file mode 100644
index 000000000..a90f02b9d
Binary files /dev/null and b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg differ
diff --git a/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg.sha512 b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg.sha512
new file mode 100644
index 000000000..6500d397d
--- /dev/null
+++ b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.2.0.0-beta3.nupkg.sha512
@@ -0,0 +1 @@
+mBHlGWl+bNTPP463JBEB/dftmdZKQRD8X72F7lsTFqYWddW5Ytp1gbzChCxW0d/Pt71KLF6XrVmyecbFlNdFBA==
\ No newline at end of file
diff --git a/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.nuspec b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.nuspec
new file mode 100644
index 000000000..cf2043a3f
--- /dev/null
+++ b/libs/orleansdashboard/2.0.0-beta3/orleansdashboard.nuspec
@@ -0,0 +1,25 @@
+
+
+
+ OrleansDashboard
+ 2.0.0-beta3
+ OrleansContrib
+ OrleansContrib
+ false
+ https://opensource.org/licenses/MIT
+ https://github.com/OrleansContrib/OrleansDashboard
+ http://dotnet.github.io/orleans/assets/logo.png
+ An admin dashboard for Microsoft Orleans
+ Copyright © 2017
+ orleans dashboard metrics monitor
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nuget.exe b/nuget.exe
new file mode 100644
index 000000000..ec1309c7a
Binary files /dev/null and b/nuget.exe differ
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs
index 28e17d013..9f6f0ed73 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClient.cs
@@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps
@@ -13,8 +14,8 @@ namespace Squidex.Domain.Apps.Core.Apps
public sealed class AppClient
{
private readonly string secret;
- private string name;
- private AppClientPermission permission;
+ private readonly string name;
+ private readonly AppClientPermission permission;
public string Name
{
@@ -42,18 +43,20 @@ namespace Squidex.Domain.Apps.Core.Apps
this.permission = permission;
}
- public void Update(AppClientPermission newPermission)
+ [Pure]
+ public AppClient Update(AppClientPermission newPermission)
{
Guard.Enum(newPermission, nameof(newPermission));
- permission = newPermission;
+ return new AppClient(name, secret, newPermission);
}
- public void Rename(string newName)
+ [Pure]
+ public AppClient Rename(string newName)
{
Guard.NotNullOrEmpty(newName, nameof(newName));
- name = newName;
+ return new AppClient(newName, secret, permission);
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs
index a92de925b..bd606ea0d 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs
@@ -6,32 +6,75 @@
// All rights reserved.
// ==========================================================================
+using System.Collections.Immutable;
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps
{
- public sealed class AppClients : DictionaryBase
+ public sealed class AppClients : DictionaryWrapper
{
- public void Add(string id, AppClient client)
+ public static readonly AppClients Empty = new AppClients();
+
+ private AppClients()
+ : base(ImmutableDictionary.Empty)
+ {
+ }
+
+ public AppClients(ImmutableDictionary inner)
+ : base(inner)
+ {
+ }
+
+ [Pure]
+ public AppClients Add(string id, AppClient client)
{
Guard.NotNullOrEmpty(id, nameof(id));
Guard.NotNull(client, nameof(client));
- Inner.Add(id, client);
+ return new AppClients(Inner.Add(id, client));
}
- public void Add(string id, string secret)
+ [Pure]
+ public AppClients Add(string id, string secret)
{
Guard.NotNullOrEmpty(id, nameof(id));
- Inner.Add(id, new AppClient(id, secret, AppClientPermission.Editor));
+ return new AppClients(Inner.Add(id, new AppClient(id, secret, AppClientPermission.Editor)));
}
- public void Revoke(string id)
+ [Pure]
+ public AppClients Revoke(string id)
{
Guard.NotNullOrEmpty(id, nameof(id));
- Inner.Remove(id);
+ return new AppClients(Inner.Remove(id));
+ }
+
+ [Pure]
+ public AppClients Rename(string id, string newName)
+ {
+ Guard.NotNullOrEmpty(id, nameof(id));
+
+ if (!TryGetValue(id, out var client))
+ {
+ return this;
+ }
+
+ return new AppClients(Inner.SetItem(id, client.Rename(newName)));
+ }
+
+ [Pure]
+ public AppClients Update(string id, AppClientPermission permission)
+ {
+ Guard.NotNullOrEmpty(id, nameof(id));
+
+ if (!TryGetValue(id, out var client))
+ {
+ return this;
+ }
+
+ return new AppClients(Inner.SetItem(id, client.Update(permission)));
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs
index 9c4ce924d..ffdd82c3f 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs
@@ -6,25 +6,41 @@
// All rights reserved.
// ==========================================================================
+using System.Collections.Immutable;
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Apps
{
- public sealed class AppContributors : DictionaryBase
+ public sealed class AppContributors : DictionaryWrapper
{
- public void Assign(string contributorId, AppContributorPermission permission)
+ public static readonly AppContributors Empty = new AppContributors();
+
+ private AppContributors()
+ : base(ImmutableDictionary.Empty)
+ {
+ }
+
+ public AppContributors(ImmutableDictionary inner)
+ : base(inner)
+ {
+ }
+
+ [Pure]
+ public AppContributors Assign(string contributorId, AppContributorPermission permission)
{
Guard.NotNullOrEmpty(contributorId, nameof(contributorId));
Guard.Enum(permission, nameof(permission));
- Inner[contributorId] = permission;
+ return new AppContributors(Inner.SetItem(contributorId, permission));
}
- public void Remove(string contributorId)
+ [Pure]
+ public AppContributors Remove(string contributorId)
{
Guard.NotNullOrEmpty(contributorId, nameof(contributorId));
- Inner.Remove(contributorId);
+ return new AppContributors(Inner.Remove(contributorId));
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
index 16570c87b..1aa4d4eca 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsConverter.cs
@@ -6,7 +6,9 @@
// All rights reserved.
// ==========================================================================
+using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@@ -26,18 +28,11 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
- protected override AppClients ReadValue(JsonReader reader, JsonSerializer serializer)
+ protected override AppClients ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize>(reader);
- var clients = new AppClients();
-
- foreach (var client in json)
- {
- clients.Add(client.Key, client.Value.ToClient());
- }
-
- return clients;
+ return new AppClients(json.ToImmutableDictionary(x => x.Key, x => x.Value.ToClient()));
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
index efa326377..76baa1710 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsConverter.cs
@@ -6,7 +6,9 @@
// All rights reserved.
// ==========================================================================
+using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@@ -26,18 +28,11 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
- protected override AppContributors ReadValue(JsonReader reader, JsonSerializer serializer)
+ protected override AppContributors ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize>(reader);
- var contributors = new AppContributors();
-
- foreach (var contributor in json)
- {
- contributors.Assign(contributor.Key, contributor.Value);
- }
-
- return contributors;
+ return new AppContributors(json.ToImmutableDictionary());
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs
index f23beded4..8ec216f76 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/JsonLanguagesConfig.cs
@@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
if (Master != null)
{
- result.MakeMaster(Master);
+ result = result.MakeMaster(Master);
}
return result;
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs
index 98dd331cc..d720dfca8 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguagesConfigConverter.cs
@@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json
serializer.Serialize(writer, json);
}
- protected override LanguagesConfig ReadValue(JsonReader reader, JsonSerializer serializer)
+ protected override LanguagesConfig ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize(reader);
diff --git a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs
index d9c2c24e9..aa3a29666 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs
@@ -10,83 +10,108 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Diagnostics.Contracts;
using System.Linq;
using Squidex.Infrastructure;
-#pragma warning disable IDE0016 // Use 'throw' expression
-
namespace Squidex.Domain.Apps.Core.Apps
{
public sealed class LanguagesConfig : IFieldPartitioning
{
- private State state;
+ public static readonly LanguagesConfig Empty = new LanguagesConfig(ImmutableDictionary.Empty, null, false);
+ public static readonly LanguagesConfig English = LanguagesConfig.Build(Language.EN);
+
+ private readonly ImmutableDictionary languages;
+ private readonly LanguageConfig master;
public LanguageConfig Master
{
- get { return state.Master; }
+ get { return master; }
}
IFieldPartitionItem IFieldPartitioning.Master
{
- get { return state.Master; }
+ get { return master; }
}
IEnumerator IEnumerable.GetEnumerator()
{
- return state.Languages.Values.GetEnumerator();
+ return languages.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
- return state.Languages.Values.GetEnumerator();
+ return languages.Values.GetEnumerator();
}
public int Count
{
- get { return state.Languages.Count; }
+ get { return languages.Count; }
}
- private LanguagesConfig(ICollection configs)
+ private LanguagesConfig(ImmutableDictionary languages, LanguageConfig master, bool checkMaster = true)
{
- Guard.NotNull(configs, nameof(configs));
+ if (checkMaster)
+ {
+ this.master = master ?? throw new InvalidOperationException("Config has no master language.");
+ }
+
+ foreach (var languageConfig in languages.Values)
+ {
+ foreach (var fallback in languageConfig.LanguageFallbacks)
+ {
+ if (!languages.ContainsKey(fallback))
+ {
+ var message = $"Config for language '{languageConfig.Language.Iso2Code}' contains unsupported fallback language '{fallback.Iso2Code}'";
- state = new State(configs.ToImmutableDictionary(x => x.Language), configs.FirstOrDefault());
+ throw new InvalidOperationException(message);
+ }
+ }
+ }
+
+ this.languages = languages;
}
- public static LanguagesConfig Build(params LanguageConfig[] configs)
+ public static LanguagesConfig Build(ICollection configs)
{
Guard.NotNull(configs, nameof(configs));
- return new LanguagesConfig(configs);
+ return new LanguagesConfig(configs.ToImmutableDictionary(x => x.Language), configs.FirstOrDefault());
}
- public static LanguagesConfig Build(params Language[] languages)
+ public static LanguagesConfig Build(params LanguageConfig[] configs)
{
- Guard.NotNull(languages, nameof(languages));
+ return Build(configs?.ToList());
+ }
- return new LanguagesConfig(languages.Select(x => new LanguageConfig(x, false)).ToList());
+ public static LanguagesConfig Build(params Language[] languages)
+ {
+ return Build(languages?.Select(x => new LanguageConfig(x)).ToList());
}
- public void MakeMaster(Language language)
+ [Pure]
+ public LanguagesConfig MakeMaster(Language language)
{
Guard.NotNull(language, nameof(language));
- state = new State(state.Languages, state.Languages[language]);
+ return new LanguagesConfig(languages, languages[language]);
}
- public void Set(LanguageConfig config)
+ [Pure]
+ public LanguagesConfig Set(LanguageConfig config)
{
Guard.NotNull(config, nameof(config));
- state = new State(state.Languages.SetItem(config.Language, config), state.Master?.Language == config.Language ? config : state.Master);
+ return new LanguagesConfig(languages.SetItem(config.Language, config), Master?.Language == config.Language ? config : Master);
}
- public void Remove(Language language)
+ [Pure]
+ public LanguagesConfig Remove(Language language)
{
Guard.NotNull(language, nameof(language));
var newLanguages =
- state.Languages.Values.Where(x => x.Language != language)
+ languages.Values.Where(x => x.Language != language)
.Select(config =>
{
return new LanguageConfig(
@@ -97,26 +122,26 @@ namespace Squidex.Domain.Apps.Core.Apps
.ToImmutableDictionary(x => x.Language);
var newMaster =
- state.Master.Language != language ?
- state.Master :
+ Master.Language != language ?
+ Master :
newLanguages.Values.FirstOrDefault();
- state = new State(newLanguages, newMaster);
+ return new LanguagesConfig(newLanguages, newMaster);
}
public bool Contains(Language language)
{
- return language != null && state.Languages.ContainsKey(language);
+ return language != null && languages.ContainsKey(language);
}
public bool TryGetConfig(Language language, out LanguageConfig config)
{
- return state.Languages.TryGetValue(language, out config);
+ return languages.TryGetValue(language, out config);
}
public bool TryGetItem(string key, out IFieldPartitionItem item)
{
- if (Language.IsValidLanguage(key) && state.Languages.TryGetValue(key, out var value))
+ if (Language.IsValidLanguage(key) && languages.TryGetValue(key, out var value))
{
item = value;
@@ -130,38 +155,6 @@ namespace Squidex.Domain.Apps.Core.Apps
}
}
- private sealed class State
- {
- public ImmutableDictionary Languages { get; }
-
- public LanguageConfig Master { get; }
-
- public State(ImmutableDictionary languages, LanguageConfig master)
- {
- foreach (var languageConfig in languages.Values)
- {
- foreach (var fallback in languageConfig.LanguageFallbacks)
- {
- if (!languages.ContainsKey(fallback))
- {
- var message = $"Config for language '{languageConfig.Language.Iso2Code}' contains unsupported fallback language '{fallback.Iso2Code}'";
-
- throw new InvalidOperationException(message);
- }
- }
- }
-
- Languages = languages;
-
- if (master == null)
- {
- throw new InvalidOperationException("Config has no master language.");
- }
-
- this.Master = master;
- }
- }
-
public PartitionResolver ToResolver()
{
return partitioning =>
diff --git a/src/Squidex.Domain.Apps.Core.Model/DictionaryBase.cs b/src/Squidex.Domain.Apps.Core.Model/DictionaryWrapper{TKey,TValue}.cs
similarity index 78%
rename from src/Squidex.Domain.Apps.Core.Model/DictionaryBase.cs
rename to src/Squidex.Domain.Apps.Core.Model/DictionaryWrapper{TKey,TValue}.cs
index 6b3734c6a..8922cfa48 100644
--- a/src/Squidex.Domain.Apps.Core.Model/DictionaryBase.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/DictionaryWrapper{TKey,TValue}.cs
@@ -8,12 +8,13 @@
using System.Collections;
using System.Collections.Generic;
+using System.Collections.Immutable;
namespace Squidex.Domain.Apps.Core
{
- public abstract class DictionaryBase : IReadOnlyDictionary
+ public abstract class DictionaryWrapper : IReadOnlyDictionary
{
- private readonly Dictionary inner = new Dictionary();
+ private readonly ImmutableDictionary inner;
public TValue this[TKey key]
{
@@ -35,11 +36,16 @@ namespace Squidex.Domain.Apps.Core
get { return inner.Count; }
}
- protected Dictionary Inner
+ protected ImmutableDictionary Inner
{
get { return inner; }
}
+ protected DictionaryWrapper(ImmutableDictionary inner)
+ {
+ this.inner = inner;
+ }
+
public bool ContainsKey(TKey key)
{
return inner.ContainsKey(key);
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs
index 09718ad8f..974855340 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Actions/WebhookAction.cs
@@ -14,9 +14,36 @@ namespace Squidex.Domain.Apps.Core.Rules.Actions
[TypeName(nameof(WebhookAction))]
public sealed class WebhookAction : RuleAction
{
- public Uri Url { get; set; }
+ private Uri url;
+ private string sharedSecret;
- public string SharedSecret { get; set; }
+ public Uri Url
+ {
+ get
+ {
+ return url;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ url = value;
+ }
+ }
+
+ public string SharedSecret
+ {
+ get
+ {
+ return sharedSecret;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ sharedSecret = value;
+ }
+ }
public override T Accept(IRuleActionVisitor visitor)
{
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs
index 8c999b7ae..3b4d284bd 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleActionVisitor.cs
@@ -10,7 +10,7 @@ using Squidex.Domain.Apps.Core.Rules.Actions;
namespace Squidex.Domain.Apps.Core.Rules
{
- public interface IRuleActionVisitor
+ public interface IRuleActionVisitor
{
T Visit(WebhookAction action);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs
index b91c7e604..ccbb1c4c6 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/IRuleTriggerVisitor.cs
@@ -10,7 +10,7 @@ using Squidex.Domain.Apps.Core.Rules.Triggers;
namespace Squidex.Domain.Apps.Core.Rules
{
- public interface IRuleTriggerVisitor
+ public interface IRuleTriggerVisitor
{
T Visit(ContentChangedTrigger trigger);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs
index cd3e9707f..176820ef5 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Json/RuleConverter.cs
@@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
+using System;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
@@ -13,14 +14,14 @@ namespace Squidex.Domain.Apps.Core.Rules.Json
{
public sealed class RuleConverter : JsonClassConverter
{
- protected override Rule ReadValue(JsonReader reader, JsonSerializer serializer)
+ protected override void WriteValue(JsonWriter writer, Rule value, JsonSerializer serializer)
{
- return serializer.Deserialize(reader).ToRule();
+ serializer.Serialize(writer, new JsonRule(value));
}
- protected override void WriteValue(JsonWriter writer, Rule value, JsonSerializer serializer)
+ protected override Rule ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
- serializer.Serialize(writer, new JsonRule(value));
+ return serializer.Deserialize(reader).ToRule();
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs
index 8595d84a8..7bec58f87 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs
@@ -7,11 +7,12 @@
// ==========================================================================
using System;
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules
{
- public sealed class Rule
+ public sealed class Rule : Cloneable
{
private RuleTrigger trigger;
private RuleAction action;
@@ -41,17 +42,26 @@ namespace Squidex.Domain.Apps.Core.Rules
this.action = action;
}
- public void Enable()
+ [Pure]
+ public Rule Enable()
{
- this.isEnabled = true;
+ return Clone(clone =>
+ {
+ clone.isEnabled = true;
+ });
}
- public void Disable()
+ [Pure]
+ public Rule Disable()
{
- this.isEnabled = false;
+ return Clone(clone =>
+ {
+ clone.isEnabled = false;
+ });
}
- public void Update(RuleTrigger newTrigger)
+ [Pure]
+ public Rule Update(RuleTrigger newTrigger)
{
Guard.NotNull(newTrigger, nameof(newTrigger));
@@ -60,10 +70,14 @@ namespace Squidex.Domain.Apps.Core.Rules
throw new ArgumentException("New trigger has another type.", nameof(newTrigger));
}
- trigger = newTrigger;
+ return Clone(clone =>
+ {
+ clone.trigger = newTrigger;
+ });
}
- public void Update(RuleAction newAction)
+ [Pure]
+ public Rule Update(RuleAction newAction)
{
Guard.NotNull(newAction, nameof(newAction));
@@ -72,7 +86,10 @@ namespace Squidex.Domain.Apps.Core.Rules
throw new ArgumentException("New action has another type.", nameof(newAction));
}
- action = newAction;
+ return Clone(clone =>
+ {
+ clone.action = newAction;
+ });
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
index 483abdff9..d2a5ccfc8 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs
@@ -6,9 +6,11 @@
// All rights reserved.
// ==========================================================================
+using Squidex.Infrastructure;
+
namespace Squidex.Domain.Apps.Core.Rules
{
- public abstract class RuleAction
+ public abstract class RuleAction : Freezable
{
public abstract T Accept(IRuleActionVisitor visitor);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
index 15284960c..9f996553f 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs
@@ -6,9 +6,11 @@
// All rights reserved.
// ==========================================================================
+using Squidex.Infrastructure;
+
namespace Squidex.Domain.Apps.Core.Rules
{
- public abstract class RuleTrigger
+ public abstract class RuleTrigger : Freezable
{
public abstract T Accept(IRuleTriggerVisitor visitor);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs b/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
index cc11d969c..7f8691725 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTrigger.cs
@@ -6,7 +6,7 @@
// All rights reserved.
// ==========================================================================
-using System.Collections.Generic;
+using System.Collections.Immutable;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Rules.Triggers
@@ -14,7 +14,21 @@ namespace Squidex.Domain.Apps.Core.Rules.Triggers
[TypeName(nameof(ContentChangedTrigger))]
public sealed class ContentChangedTrigger : RuleTrigger
{
- public List Schemas { get; set; }
+ private ImmutableList schemas;
+
+ public ImmutableList Schemas
+ {
+ get
+ {
+ return schemas;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ schemas = value;
+ }
+ }
public override T Accept(IRuleTriggerVisitor visitor)
{
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
index cf1ad40a6..c4de1e7f1 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs
@@ -13,13 +13,45 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(AssetsField))]
public sealed class AssetsFieldProperties : FieldProperties
{
- public int? MinItems { get; set; }
+ private int? minItems;
+ private int? maxItems;
- public int? MaxItems { get; set; }
+ public int? MinItems
+ {
+ get
+ {
+ return minItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ minItems = value;
+ }
+ }
+
+ public int? MaxItems
+ {
+ get
+ {
+ return maxItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ maxItems = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new AssetsField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs
index f773ce620..c6817f309 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs
@@ -13,13 +13,45 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(BooleanField))]
public sealed class BooleanFieldProperties : FieldProperties
{
- public bool? DefaultValue { get; set; }
+ private BooleanFieldEditor editor;
+ private bool? defaultValue;
- public BooleanFieldEditor Editor { get; set; }
+ public bool? DefaultValue
+ {
+ get
+ {
+ return defaultValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ defaultValue = value;
+ }
+ }
+
+ public BooleanFieldEditor Editor
+ {
+ get
+ {
+ return editor;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ editor = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new BooleanField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs
index 9c03a46c8..382fc8430 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs
@@ -14,19 +14,90 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(DateTimeField))]
public sealed class DateTimeFieldProperties : FieldProperties
{
- public Instant? MaxValue { get; set; }
+ private DateTimeFieldEditor editor;
+ private DateTimeCalculatedDefaultValue? calculatedDefaultValue;
+ private Instant? maxValue;
+ private Instant? minValue;
+ private Instant? defaultValue;
- public Instant? MinValue { get; set; }
+ public Instant? MaxValue
+ {
+ get
+ {
+ return maxValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ maxValue = value;
+ }
+ }
+
+ public Instant? MinValue
+ {
+ get
+ {
+ return minValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public Instant? DefaultValue { get; set; }
+ minValue = value;
+ }
+ }
- public DateTimeFieldEditor Editor { get; set; }
+ public Instant? DefaultValue
+ {
+ get
+ {
+ return defaultValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public DateTimeCalculatedDefaultValue? CalculatedDefaultValue { get; set; }
+ defaultValue = value;
+ }
+ }
+
+ public DateTimeCalculatedDefaultValue? CalculatedDefaultValue
+ {
+ get
+ {
+ return calculatedDefaultValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ calculatedDefaultValue = value;
+ }
+ }
+
+ public DateTimeFieldEditor Editor
+ {
+ get
+ {
+ return editor;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ editor = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new DateTimeField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Field.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Field.cs
index 2f9edf612..d10500fc1 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Field.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Field.cs
@@ -6,11 +6,12 @@
// All rights reserved.
// ==========================================================================
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
- public abstract class Field
+ public abstract class Field : Cloneable
{
private readonly long fieldId;
private readonly Partitioning partitioning;
@@ -63,32 +64,52 @@ namespace Squidex.Domain.Apps.Core.Schemas
this.partitioning = partitioning;
}
- public void Lock()
+ [Pure]
+ public Field Lock()
{
- isLocked = true;
+ return Clone(clone =>
+ {
+ clone.isLocked = true;
+ });
}
- public void Hide()
+ [Pure]
+ public Field Hide()
{
- isHidden = true;
+ return Clone(clone =>
+ {
+ clone.isHidden = true;
+ });
}
- public void Show()
+ [Pure]
+ public Field Show()
{
- isHidden = false;
+ return Clone(clone =>
+ {
+ clone.isHidden = false;
+ });
}
- public void Disable()
+ [Pure]
+ public Field Disable()
{
- isDisabled = true;
+ return Clone(clone =>
+ {
+ clone.isDisabled = true;
+ });
}
- public void Enable()
+ [Pure]
+ public Field Enable()
{
- isDisabled = false;
+ return Clone(clone =>
+ {
+ clone.isDisabled = false;
+ });
}
- public abstract void Update(FieldProperties newProperties);
+ public abstract Field Update(FieldProperties newProperties);
public abstract T Accept(IFieldVisitor visitor);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
index c975e4ef9..aa530df70 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs
@@ -10,12 +10,54 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
public abstract class FieldProperties : NamedElementPropertiesBase
{
- public bool IsRequired { get; set; }
+ private bool isRequired;
+ private bool isListField;
+ private string placeholder;
- public bool IsListField { get; set; }
+ public bool IsRequired
+ {
+ get
+ {
+ return isRequired;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public string Placeholder { get; set; }
+ isRequired = value;
+ }
+ }
+
+ public bool IsListField
+ {
+ get
+ {
+ return isListField;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ isListField = value;
+ }
+ }
+
+ public string Placeholder
+ {
+ get
+ {
+ return placeholder;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ placeholder = value;
+ }
+ }
public abstract T Accept(IFieldPropertiesVisitor visitor);
+
+ public abstract Field CreateField(long id, string name, Partitioning partitioning);
}
}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs
index 710581ef1..5c6cecb70 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs
@@ -17,29 +17,7 @@ namespace Squidex.Domain.Apps.Core.Schemas
private delegate Field FactoryFunction(long id, string name, Partitioning partitioning, FieldProperties properties);
private readonly TypeNameRegistry typeNameRegistry;
- private readonly Dictionary fieldsByPropertyType = new Dictionary();
-
- private sealed class Registered
- {
- private readonly FactoryFunction fieldFactory;
- private readonly Type propertiesType;
-
- public Type PropertiesType
- {
- get { return propertiesType; }
- }
-
- public Registered(FactoryFunction fieldFactory, Type propertiesType)
- {
- this.fieldFactory = fieldFactory;
- this.propertiesType = propertiesType;
- }
-
- public Field CreateField(long id, string name, Partitioning partitioning, FieldProperties properties)
- {
- return fieldFactory(id, name, partitioning, properties);
- }
- }
+ private readonly Dictionary fieldsByPropertyType = new Dictionary();
public FieldRegistry(TypeNameRegistry typeNameRegistry)
{
@@ -47,69 +25,39 @@ namespace Squidex.Domain.Apps.Core.Schemas
this.typeNameRegistry = typeNameRegistry;
- Add(
- (id, name, partitioning, properties) =>
- new BooleanField(id, name, partitioning, (BooleanFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new NumberField(id, name, partitioning, (NumberFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new StringField(id, name, partitioning, (StringFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new JsonField(id, name, partitioning, (JsonFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new AssetsField(id, name, partitioning, (AssetsFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new GeolocationField(id, name, partitioning, (GeolocationFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new ReferencesField(id, name, partitioning, (ReferencesFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new DateTimeField(id, name, partitioning, (DateTimeFieldProperties)properties));
-
- Add(
- (id, name, partitioning, properties) =>
- new TagsField(id, name, partitioning, (TagsFieldProperties)properties));
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
+ RegisterField();
typeNameRegistry.MapObsolete(typeof(ReferencesFieldProperties), "DateTime");
typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "References");
}
- private void Add(FactoryFunction fieldFactory)
+ private void RegisterField()
{
- Guard.NotNull(fieldFactory, nameof(fieldFactory));
-
- typeNameRegistry.Map(typeof(TFieldProperties));
-
- var registered = new Registered(fieldFactory, typeof(TFieldProperties));
+ typeNameRegistry.Map(typeof(T));
- fieldsByPropertyType[registered.PropertiesType] = registered;
+ fieldsByPropertyType[typeof(T)] = (id, name, partitioning, properties) => properties.CreateField(id, name, partitioning);
}
public Field CreateField(long id, string name, Partitioning partitioning, FieldProperties properties)
{
Guard.NotNull(properties, nameof(properties));
- var registered = fieldsByPropertyType.GetOrDefault(properties.GetType());
+ var factory = fieldsByPropertyType.GetOrDefault(properties.GetType());
- if (registered == null)
+ if (factory == null)
{
throw new InvalidOperationException($"The field property '{properties.GetType()}' is not supported.");
}
- return registered.CreateField(id, name, partitioning, properties);
+ return factory(id, name, partitioning, properties);
}
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Field{T}.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Field{T}.cs
index edc05dbb2..3a9e2d18a 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Field{T}.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Field{T}.cs
@@ -1,5 +1,5 @@
// ==========================================================================
-// Field_Generic.cs
+// Field{T}.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@@ -7,7 +7,9 @@
// ==========================================================================
using System;
+using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
+
namespace Squidex.Domain.Apps.Core.Schemas
{
public abstract class Field : Field where T : FieldProperties, new()
@@ -30,13 +32,19 @@ namespace Squidex.Domain.Apps.Core.Schemas
Guard.NotNull(properties, nameof(properties));
this.properties = properties;
+ this.properties.Freeze();
}
- public override void Update(FieldProperties newProperties)
+ [Pure]
+ public override Field Update(FieldProperties newProperties)
{
var typedProperties = ValidateProperties(newProperties);
- properties = typedProperties;
+ return Clone>(clone =>
+ {
+ clone.properties = typedProperties;
+ clone.properties.Freeze();
+ });
}
private T ValidateProperties(FieldProperties newProperties)
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs
index 5e4b34d4e..e01fddcb4 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs
@@ -13,11 +13,30 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(GeolocationField))]
public sealed class GeolocationFieldProperties : FieldProperties
{
- public GeolocationFieldEditor Editor { get; set; }
+ private GeolocationFieldEditor editor;
+
+ public GeolocationFieldEditor Editor
+ {
+ get
+ {
+ return editor;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ editor = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new GeolocationField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs
index f2c6695bd..c1c723cf0 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs
@@ -14,6 +14,8 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
{
public sealed class JsonSchemaModel
{
+ private static readonly Field[] Empty = new Field[0];
+
[JsonProperty]
public string Name { get; set; }
@@ -54,46 +56,40 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
public Schema ToSchema(FieldRegistry fieldRegistry)
{
- var schema = new Schema(Name);
+ Field[] fields = Empty;
if (Fields != null)
{
- foreach (var fieldModel in Fields)
+ fields = new Field[Fields.Count];
+
+ for (var i = 0; i < fields.Length; i++)
{
+ var fieldModel = Fields[i];
+
var parititonKey = new Partitioning(fieldModel.Partitioning);
var field = fieldRegistry.CreateField(fieldModel.Id, fieldModel.Name, parititonKey, fieldModel.Properties);
if (fieldModel.IsDisabled)
{
- field.Disable();
+ field = field.Disable();
}
if (fieldModel.IsLocked)
{
- field.Lock();
+ field = field.Lock();
}
if (fieldModel.IsHidden)
{
- field.Hide();
+ field = field.Hide();
}
- schema.AddField(field);
+ fields[i] = field;
}
}
- if (IsPublished)
- {
- schema.Publish();
- }
-
- if (Properties != null)
- {
- schema.Update(Properties);
- }
-
- return schema;
+ return new Schema(Name, fields, Properties, IsPublished);
}
}
}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
index 317afa717..6db657dc2 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/SchemaConverter.cs
@@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
+using System;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
@@ -28,7 +29,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json
serializer.Serialize(writer, new JsonSchemaModel(value));
}
- protected override Schema ReadValue(JsonReader reader, JsonSerializer serializer)
+ protected override Schema ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
return serializer.Deserialize(reader).ToSchema(fieldRegistry);
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs
index c432be09a..5c4ffd918 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs
@@ -17,5 +17,10 @@ namespace Squidex.Domain.Apps.Core.Schemas
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new JsonField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs
index 7f5a9d572..ca23aec3d 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs
@@ -6,12 +6,41 @@
// All rights reserved.
// ==========================================================================
+using Squidex.Infrastructure;
+
namespace Squidex.Domain.Apps.Core.Schemas
{
- public abstract class NamedElementPropertiesBase
+ public abstract class NamedElementPropertiesBase : Freezable
{
- public string Label { get; set; }
+ private string label;
+ private string hints;
+
+ public string Label
+ {
+ get
+ {
+ return label;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ label = value;
+ }
+ }
+
+ public string Hints
+ {
+ get
+ {
+ return hints;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public string Hints { get; set; }
+ hints = value;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
index ce1703faa..f7d6af843 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs
@@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
+using System.Collections.Immutable;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
@@ -13,19 +14,90 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(NumberField))]
public sealed class NumberFieldProperties : FieldProperties
{
- public double? MaxValue { get; set; }
+ private double? maxValue;
+ private double? minValue;
+ private double? defaultValue;
+ private ImmutableList allowedValues;
+ private NumberFieldEditor editor;
- public double? MinValue { get; set; }
+ public double? MaxValue
+ {
+ get
+ {
+ return maxValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ maxValue = value;
+ }
+ }
+
+ public double? MinValue
+ {
+ get
+ {
+ return minValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public double? DefaultValue { get; set; }
+ minValue = value;
+ }
+ }
- public double[] AllowedValues { get; set; }
+ public double? DefaultValue
+ {
+ get
+ {
+ return defaultValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public NumberFieldEditor Editor { get; set; }
+ defaultValue = value;
+ }
+ }
+
+ public ImmutableList AllowedValues
+ {
+ get
+ {
+ return allowedValues;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ allowedValues = value;
+ }
+ }
+
+ public NumberFieldEditor Editor
+ {
+ get
+ {
+ return editor;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ editor = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new NumberField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
index 8a4f76192..3faa12a26 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs
@@ -14,15 +14,60 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(ReferencesField))]
public sealed class ReferencesFieldProperties : FieldProperties
{
- public int? MinItems { get; set; }
+ private int? minItems;
+ private int? maxItems;
+ private Guid schemaId;
- public int? MaxItems { get; set; }
+ public int? MinItems
+ {
+ get
+ {
+ return minItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ minItems = value;
+ }
+ }
- public Guid SchemaId { get; set; }
+ public int? MaxItems
+ {
+ get
+ {
+ return maxItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ maxItems = value;
+ }
+ }
+
+ public Guid SchemaId
+ {
+ get
+ {
+ return schemaId;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ schemaId = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new ReferencesField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs
index 137e49783..44404f001 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs
@@ -8,18 +8,20 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.Contracts;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
{
- public sealed class Schema
+ public sealed class Schema : Cloneable
{
private readonly string name;
- private readonly List fieldsOrdered = new List();
- private readonly Dictionary fieldsById = new Dictionary();
- private readonly Dictionary fieldsByName = new Dictionary();
- private SchemaProperties properties = new SchemaProperties();
+ private ImmutableArray fieldsOrdered = ImmutableArray.Empty;
+ private ImmutableDictionary fieldsById;
+ private ImmutableDictionary fieldsByName;
+ private SchemaProperties properties;
private bool isPublished;
public string Name
@@ -39,12 +41,42 @@ namespace Squidex.Domain.Apps.Core.Schemas
public IReadOnlyDictionary FieldsById
{
- get { return fieldsById; }
+ get
+ {
+ if (fieldsById == null)
+ {
+ if (fieldsOrdered.Length == 0)
+ {
+ fieldsById = ImmutableDictionary.Empty;
+ }
+ else
+ {
+ fieldsById = fieldsOrdered.ToImmutableDictionary(x => x.Id);
+ }
+ }
+
+ return fieldsById;
+ }
}
public IReadOnlyDictionary FieldsByName
{
- get { return fieldsByName; }
+ get
+ {
+ if (fieldsByName == null)
+ {
+ if (fieldsOrdered.Length == 0)
+ {
+ fieldsByName = ImmutableDictionary.Empty;
+ }
+ else
+ {
+ fieldsByName = fieldsOrdered.ToImmutableDictionary(x => x.Name);
+ }
+ }
+
+ return fieldsByName;
+ }
}
public SchemaProperties Properties
@@ -52,69 +84,177 @@ namespace Squidex.Domain.Apps.Core.Schemas
get { return properties; }
}
- public void Publish()
+ public Schema(string name, SchemaProperties properties = null)
{
- isPublished = true;
- }
+ Guard.NotNullOrEmpty(name, nameof(name));
- public void Unpublish()
- {
- isPublished = false;
+ this.name = name;
+
+ this.properties = properties ?? new SchemaProperties();
+ this.properties.Freeze();
}
- public Schema(string name)
+ public Schema(string name, Field[] fields, SchemaProperties properties, bool isPublished)
+ : this(name, properties)
{
Guard.NotNullOrEmpty(name, nameof(name));
+ Guard.NotNull(fields, nameof(fields));
- this.name = name;
+ this.isPublished = isPublished;
+
+ fieldsOrdered = ImmutableArray.Create(fields);
+ }
+
+ protected override void OnCloned()
+ {
+ fieldsById = null;
+ fieldsByName = null;
}
- public void Update(SchemaProperties newProperties)
+ [Pure]
+ public Schema Update(SchemaProperties newProperties)
{
Guard.NotNull(newProperties, nameof(newProperties));
- properties = newProperties;
+ return Clone(clone =>
+ {
+ clone.properties = newProperties;
+ clone.properties.Freeze();
+ });
+ }
+
+ [Pure]
+ public Schema UpdateField(long fieldId, FieldProperties newProperties)
+ {
+ return UpdateField(fieldId, field =>
+ {
+ return field.Update(newProperties);
+ });
}
- public void DeleteField(long fieldId)
+ [Pure]
+ public Schema LockField(long fieldId)
{
- if (!fieldsById.TryGetValue(fieldId, out var field))
+ return UpdateField(fieldId, field =>
{
- return;
+ return field.Lock();
+ });
+ }
+
+ [Pure]
+ public Schema DisableField(long fieldId)
+ {
+ return UpdateField(fieldId, field =>
+ {
+ return field.Disable();
+ });
+ }
+
+ [Pure]
+ public Schema EnableField(long fieldId)
+ {
+ return UpdateField(fieldId, field =>
+ {
+ return field.Enable();
+ });
+ }
+
+ [Pure]
+ public Schema HideField(long fieldId)
+ {
+ return UpdateField(fieldId, field =>
+ {
+ return field.Hide();
+ });
+ }
+
+ [Pure]
+ public Schema ShowField(long fieldId)
+ {
+ return UpdateField(fieldId, field =>
+ {
+ return field.Show();
+ });
+ }
+
+ [Pure]
+ public Schema Publish()
+ {
+ return Clone(clone =>
+ {
+ clone.isPublished = true;
+ });
+ }
+
+ [Pure]
+ public Schema Unpublish()
+ {
+ return Clone(clone =>
+ {
+ clone.isPublished = false;
+ });
+ }
+
+ [Pure]
+ public Schema DeleteField(long fieldId)
+ {
+ if (!FieldsById.TryGetValue(fieldId, out var field))
+ {
+ return this;
}
- fieldsById.Remove(fieldId);
- fieldsByName.Remove(field.Name);
- fieldsOrdered.Remove(field);
+ return Clone(clone =>
+ {
+ clone.fieldsOrdered = fieldsOrdered.Remove(field);
+ });
}
- public void ReorderFields(List ids)
+ [Pure]
+ public Schema ReorderFields(List ids)
{
Guard.NotNull(ids, nameof(ids));
- if (ids.Count != fieldsOrdered.Count || ids.Any(x => !fieldsById.ContainsKey(x)))
+ if (ids.Count != fieldsOrdered.Length || ids.Any(x => !FieldsById.ContainsKey(x)))
{
throw new ArgumentException("Ids must cover all fields.", nameof(ids));
}
- var fields = fieldsOrdered.ToList();
-
- fieldsOrdered.Clear();
- fieldsOrdered.AddRange(fields.OrderBy(f => ids.IndexOf(f.Id)));
+ return Clone(clone =>
+ {
+ clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToImmutableArray();
+ });
}
- public void AddField(Field field)
+ [Pure]
+ public Schema AddField(Field field)
{
Guard.NotNull(field, nameof(field));
- if (fieldsByName.ContainsKey(field.Name) || fieldsById.ContainsKey(field.Id))
+ if (FieldsByName.ContainsKey(field.Name) || FieldsById.ContainsKey(field.Id))
{
throw new ArgumentException($"A field with name '{field.Name}' and id {field.Id} already exists.", nameof(field));
}
- fieldsById.Add(field.Id, field);
- fieldsByName.Add(field.Name, field);
- fieldsOrdered.Add(field);
+ return Clone(clone =>
+ {
+ clone.fieldsOrdered = clone.fieldsOrdered.Add(field);
+ });
+ }
+
+ [Pure]
+ public Schema UpdateField(long fieldId, Func updater)
+ {
+ Guard.NotNull(updater, nameof(updater));
+
+ if (!FieldsById.TryGetValue(fieldId, out var field))
+ {
+ return this;
+ }
+
+ return Clone(clone =>
+ {
+ clone.fieldsOrdered = clone.fieldsOrdered.Replace(field, updater(field));
+ });
}
}
}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
index 5263cc2e7..80c2d6cbc 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs
@@ -6,6 +6,7 @@
// All rights reserved.
// ==========================================================================
+using System.Collections.Immutable;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Schemas
@@ -13,23 +14,120 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(StringField))]
public sealed class StringFieldProperties : FieldProperties
{
- public int? MinLength { get; set; }
+ private int? minLength;
+ private int? maxLength;
+ private string pattern;
+ private string patternMessage;
+ private string defaultValue;
+ private ImmutableList allowedValues;
+ private StringFieldEditor editor;
- public int? MaxLength { get; set; }
+ public int? MinLength
+ {
+ get
+ {
+ return minLength;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public string DefaultValue { get; set; }
+ minLength = value;
+ }
+ }
- public string Pattern { get; set; }
+ public int? MaxLength
+ {
+ get
+ {
+ return maxLength;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public string PatternMessage { get; set; }
+ maxLength = value;
+ }
+ }
- public string[] AllowedValues { get; set; }
+ public string DefaultValue
+ {
+ get
+ {
+ return defaultValue;
+ }
+ set
+ {
+ ThrowIfFrozen();
- public StringFieldEditor Editor { get; set; }
+ defaultValue = value;
+ }
+ }
+
+ public string Pattern
+ {
+ get
+ {
+ return pattern;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ pattern = value;
+ }
+ }
+
+ public string PatternMessage
+ {
+ get
+ {
+ return patternMessage;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ patternMessage = value;
+ }
+ }
+
+ public ImmutableList AllowedValues
+ {
+ get
+ {
+ return allowedValues;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ allowedValues = value;
+ }
+ }
+
+ public StringFieldEditor Editor
+ {
+ get
+ {
+ return editor;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ editor = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new StringField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
index 9e10e634b..579c65ca5 100644
--- a/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
+++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs
@@ -13,13 +13,45 @@ namespace Squidex.Domain.Apps.Core.Schemas
[TypeName(nameof(TagsField))]
public sealed class TagsFieldProperties : FieldProperties
{
- public int? MinItems { get; set; }
+ private int? minItems;
+ private int? maxItems;
- public int? MaxItems { get; set; }
+ public int? MinItems
+ {
+ get
+ {
+ return minItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ minItems = value;
+ }
+ }
+
+ public int? MaxItems
+ {
+ get
+ {
+ return maxItems;
+ }
+ set
+ {
+ ThrowIfFrozen();
+
+ maxItems = value;
+ }
+ }
public override T Accept(IFieldPropertiesVisitor visitor)
{
return visitor.Visit(this);
}
+
+ public override Field CreateField(long id, string name, Partitioning partitioning)
+ {
+ return new TagsField(id, name, partitioning, this);
+ }
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
index f70c8b9f0..86c6c1781 100644
--- a/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
+++ b/src/Squidex.Domain.Apps.Core.Operations/ExtractReferenceIds/ReferencesExtractor.cs
@@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
public static IEnumerable Visit(AssetsField field, JToken value)
{
- IEnumerable result = null;
+ IEnumerable result;
try
{
result = value?.ToObject>();
@@ -47,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.ExtractReferenceIds
private static IEnumerable Visit(ReferencesField field, JToken value)
{
- IEnumerable result = null;
+ IEnumerable result;
try
{
result = value?.ToObject>() ?? Enumerable.Empty();
diff --git a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
index aae33d8ca..afc865466 100644
--- a/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
+++ b/src/Squidex.Domain.Apps.Core.Operations/HandleRules/Actions/WebhookActionHandler.cs
@@ -20,7 +20,7 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.Http;
-namespace Squidex.Domain.Apps.Core.HandleRules.ActionHandlers
+namespace Squidex.Domain.Apps.Core.HandleRules.Actions
{
public sealed class WebhookActionHandler : RuleActionHandler
{
diff --git a/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs b/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
index 6ab62dc73..70f1db7d9 100644
--- a/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
+++ b/src/Squidex.Domain.Apps.Core.Operations/Scripting/JintScriptEngine.cs
@@ -116,11 +116,11 @@ namespace Squidex.Domain.Apps.Core.Scripting
}
catch (ParserException ex)
{
- throw new ValidationException($"Failed to execute script with javascript syntaxs error.", new ValidationError(ex.Message));
+ throw new ValidationException("Failed to execute script with javascript syntaxs error.", new ValidationError(ex.Message));
}
catch (JavaScriptException ex)
{
- throw new ValidationException($"Failed to execute script with javascript error.", new ValidationError(ex.Message));
+ throw new ValidationException("Failed to execute script with javascript error.", new ValidationError(ex.Message));
}
}
@@ -171,7 +171,7 @@ namespace Squidex.Domain.Apps.Core.Scripting
{
var errors = !string.IsNullOrWhiteSpace(message) ? new[] { new ValidationError(message) } : null;
- throw new ValidationException($"Script rejected the operation.", errors);
+ throw new ValidationException("Script rejected the operation.", errors);
}));
}
}
diff --git a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
index e5d74893a..e57722909 100644
--- a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
+++ b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
@@ -16,9 +16,9 @@
-
-
-
+
+
+
diff --git a/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs b/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs
index e6b4a9998..5da60f86e 100644
--- a/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs
+++ b/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs
@@ -13,53 +13,47 @@ namespace Squidex.Domain.Apps.Events.Apps.Utils
{
public static class AppEventDispatcher
{
- public static void Apply(this AppContributors contributors, AppContributorRemoved @event)
+ public static AppContributors Apply(this AppContributors contributors, AppContributorRemoved @event)
{
- contributors.Remove(@event.ContributorId);
+ return contributors.Remove(@event.ContributorId);
}
- public static void Apply(this AppContributors contributors, AppContributorAssigned @event)
+ public static AppContributors Apply(this AppContributors contributors, AppContributorAssigned @event)
{
- contributors.Assign(@event.ContributorId, @event.Permission);
+ return contributors.Assign(@event.ContributorId, @event.Permission);
}
- public static void Apply(this LanguagesConfig languagesConfig, AppLanguageAdded @event)
+ public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageAdded @event)
{
- languagesConfig.Set(new LanguageConfig(@event.Language));
+ return languagesConfig.Set(new LanguageConfig(@event.Language));
}
- public static void Apply(this LanguagesConfig languagesConfig, AppLanguageRemoved @event)
+ public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageRemoved @event)
{
- languagesConfig.Remove(@event.Language);
+ return languagesConfig.Remove(@event.Language);
}
- public static void Apply(this AppClients clients, AppClientAttached @event)
+ public static AppClients Apply(this AppClients clients, AppClientAttached @event)
{
- clients.Add(@event.Id, @event.Secret);
+ return clients.Add(@event.Id, @event.Secret);
}
- public static void Apply(this AppClients clients, AppClientRevoked @event)
+ public static AppClients Apply(this AppClients clients, AppClientRevoked @event)
{
- clients.Revoke(@event.Id);
+ return clients.Revoke(@event.Id);
}
- public static void Apply(this AppClients clients, AppClientRenamed @event)
+ public static AppClients Apply(this AppClients clients, AppClientRenamed @event)
{
- if (clients.TryGetValue(@event.Id, out var client))
- {
- client.Rename(@event.Name);
- }
+ return clients.Rename(@event.Id, @event.Name);
}
- public static void Apply(this AppClients clients, AppClientUpdated @event)
+ public static AppClients Apply(this AppClients clients, AppClientUpdated @event)
{
- if (clients.TryGetValue(@event.Id, out var client))
- {
- client.Update(@event.Permission);
- }
+ return clients.Update(@event.Id, @event.Permission);
}
- public static void Apply(this LanguagesConfig languagesConfig, AppLanguageUpdated @event)
+ public static LanguagesConfig Apply(this LanguagesConfig languagesConfig, AppLanguageUpdated @event)
{
var fallback = @event.Fallback;
@@ -70,12 +64,14 @@ namespace Squidex.Domain.Apps.Events.Apps.Utils
fallback = fallback.Intersect(existingLangauges).ToList();
}
- languagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, fallback));
+ languagesConfig = languagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, fallback));
if (@event.IsMaster)
{
- languagesConfig.MakeMaster(@event.Language);
+ languagesConfig = languagesConfig.MakeMaster(@event.Language);
}
+
+ return languagesConfig;
}
}
}
diff --git a/src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs b/src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs
index 8de79c181..e85f4eecf 100644
--- a/src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs
+++ b/src/Squidex.Domain.Apps.Events/Rules/Utils/RuleEventDispatcher.cs
@@ -17,27 +17,29 @@ namespace Squidex.Domain.Apps.Events.Rules.Utils
return new Rule(@event.Trigger, @event.Action);
}
- public static void Apply(this Rule rule, RuleUpdated @event)
+ public static Rule Apply(this Rule rule, RuleUpdated @event)
{
if (@event.Trigger != null)
{
- rule.Update(@event.Trigger);
+ return rule.Update(@event.Trigger);
}
if (@event.Action != null)
{
- rule.Update(@event.Action);
+ return rule.Update(@event.Action);
}
+
+ return rule;
}
- public static void Apply(this Rule rule, RuleEnabled @event)
+ public static Rule Apply(this Rule rule, RuleEnabled @event)
{
- rule.Enable();
+ return rule.Enable();
}
- public static void Apply(this Rule rule, RuleDisabled @event)
+ public static Rule Apply(this Rule rule, RuleDisabled @event)
{
- rule.Disable();
+ return rule.Disable();
}
}
}
diff --git a/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs b/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
index 0ff340140..7deea8e63 100644
--- a/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
+++ b/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs
@@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
if (@event.Properties != null)
{
- schema.Update(@event.Properties);
+ schema = schema.Update(@event.Properties);
}
if (@event.Fields != null)
@@ -38,20 +38,20 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
if (eventField.IsHidden)
{
- field.Hide();
+ field = field.Hide();
}
if (eventField.IsDisabled)
{
- field.Disable();
+ field = field.Disable();
}
if (eventField.IsLocked)
{
- field.Lock();
+ field = field.Lock();
}
- schema.AddField(field);
+ schema = schema.AddField(field);
fieldId++;
}
@@ -60,91 +60,74 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils
return schema;
}
- public static void Apply(this Schema schema, FieldAdded @event, FieldRegistry registry)
+ 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 fieldId = @event.FieldId.Id;
- var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties);
+ var field = registry.CreateField(@event.FieldId.Id, @event.Name, partitioning, @event.Properties);
- schema.DeleteField(fieldId);
- schema.AddField(field);
+ schema = schema.DeleteField(@event.FieldId.Id);
+ schema = schema.AddField(field);
+
+ return schema;
}
- public static void Apply(this Schema schema, FieldUpdated @event)
+ public static Schema Apply(this Schema schema, FieldUpdated @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Update(@event.Properties);
- }
+ return schema.UpdateField(@event.FieldId.Id, @event.Properties);
}
- public static void Apply(this Schema schema, FieldLocked @event)
+ public static Schema Apply(this Schema schema, FieldLocked @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Lock();
- }
+ return schema.LockField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, FieldHidden @event)
+ public static Schema Apply(this Schema schema, FieldHidden @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Hide();
- }
+ return schema.HideField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, FieldShown @event)
+ public static Schema Apply(this Schema schema, FieldShown @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Show();
- }
+ return schema.ShowField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, FieldDisabled @event)
+ public static Schema Apply(this Schema schema, FieldDisabled @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Disable();
- }
+ return schema.DisableField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, FieldEnabled @event)
+ public static Schema Apply(this Schema schema, FieldEnabled @event)
{
- if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field))
- {
- field.Enable();
- }
+ return schema.EnableField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, SchemaUpdated @event)
+ public static Schema Apply(this Schema schema, SchemaUpdated @event)
{
- schema.Update(@event.Properties);
+ return schema.Update(@event.Properties);
}
- public static void Apply(this Schema schema, SchemaFieldsReordered @event)
+ public static Schema Apply(this Schema schema, SchemaFieldsReordered @event)
{
- schema.ReorderFields(@event.FieldIds);
+ return schema.ReorderFields(@event.FieldIds);
}
- public static void Apply(this Schema schema, FieldDeleted @event)
+ public static Schema Apply(this Schema schema, FieldDeleted @event)
{
- schema.DeleteField(@event.FieldId.Id);
+ return schema.DeleteField(@event.FieldId.Id);
}
- public static void Apply(this Schema schema, SchemaPublished @event)
+ public static Schema Apply(this Schema schema, SchemaPublished @event)
{
- schema.Publish();
+ return schema.Publish();
}
- public static void Apply(this Schema schema, SchemaUnpublished @event)
+ public static Schema Apply(this Schema schema, SchemaUnpublished @event)
{
- schema.Unpublish();
+ return schema.Unpublish();
}
}
}
diff --git a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
index 08e97944f..9e69912e3 100644
--- a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
+++ b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs
deleted file mode 100644
index 46e1f5e9e..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// ==========================================================================
-// MongoAppEntity.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using MongoDB.Bson.Serialization.Attributes;
-using Squidex.Domain.Apps.Core;
-using Squidex.Domain.Apps.Core.Apps;
-using Squidex.Domain.Apps.Read.Apps;
-using Squidex.Infrastructure.MongoDb;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Apps
-{
- public sealed class MongoAppEntity : MongoEntity, IAppEntity
- {
- [BsonRequired]
- [BsonElement]
- public string Name { get; set; }
-
- [BsonRequired]
- [BsonElement]
- public long Version { get; set; }
-
- [BsonIgnoreIfDefault]
- [BsonElement]
- public string PlanId { get; set; }
-
- [BsonIgnoreIfDefault]
- [BsonElement]
- public string PlanOwner { get; set; }
-
- [BsonIgnoreIfDefault]
- [BsonElement]
- public string[] ContributorIds { get; set; }
-
- [BsonRequired]
- [BsonElement]
- [BsonJson]
- public AppClients Clients { get; set; }
-
- [BsonRequired]
- [BsonElement]
- [BsonJson]
- public AppContributors Contributors { get; set; }
-
- [BsonRequired]
- [BsonElement]
- [BsonJson]
- public LanguagesConfig LanguagesConfig { get; set; }
-
- public PartitionResolver PartitionResolver
- {
- get { return LanguagesConfig.ToResolver(); }
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs
deleted file mode 100644
index f55b7e494..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-// ==========================================================================
-// MongoAppRepository.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MongoDB.Driver;
-using Squidex.Domain.Apps.Read.Apps;
-using Squidex.Domain.Apps.Read.Apps.Repositories;
-using Squidex.Infrastructure.MongoDb;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Apps
-{
- public partial class MongoAppRepository : MongoRepositoryBase, IAppRepository, IAppEventConsumer
- {
- public MongoAppRepository(IMongoDatabase database)
- : base(database)
- {
- }
-
- protected override string CollectionName()
- {
- return "Projections_Apps";
- }
-
- protected override async Task SetupCollectionAsync(IMongoCollection collection)
- {
- await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name));
- await collection.Indexes.CreateOneAsync(Index.Ascending(x => x.ContributorIds));
- }
-
- protected override MongoCollectionSettings CollectionSettings()
- {
- return new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority };
- }
-
- public async Task> QueryAllAsync(string subjectId)
- {
- var appEntities =
- await Collection.Find(s => s.ContributorIds.Contains(subjectId))
- .ToListAsync();
-
- return appEntities;
- }
-
- public async Task FindAppAsync(Guid id)
- {
- var appEntity =
- await Collection.Find(s => s.Id == id)
- .FirstOrDefaultAsync();
-
- return appEntity;
- }
-
- public async Task FindAppAsync(string name)
- {
- var appEntity =
- await Collection.Find(s => s.Name == name)
- .FirstOrDefaultAsync();
-
- return appEntity;
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs
deleted file mode 100644
index 51c627fec..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-// ==========================================================================
-// MongoAppRepository_EventHandling.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using Squidex.Domain.Apps.Core.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.CQRS.Events;
-using Squidex.Infrastructure.Dispatching;
-using Squidex.Infrastructure.Reflection;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Apps
-{
- public partial class MongoAppRepository
- {
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return "^app-"; }
- }
-
- public Task On(Envelope @event)
- {
- return this.DispatchActionAsync(@event.Payload, @event.Headers);
- }
-
- protected Task On(AppCreated @event, EnvelopeHeaders headers)
- {
- return Collection.CreateAsync(@event, headers, a =>
- {
- SimpleMapper.Map(@event, a);
-
- a.Clients = new AppClients();
- a.Contributors = new AppContributors();
-
- a.LanguagesConfig = LanguagesConfig.Build(Language.EN);
- });
- }
-
- protected Task On(AppPlanChanged @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- SimpleMapper.Map(@event, a);
- });
- }
-
- protected Task On(AppClientAttached @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Clients.Apply(@event);
- });
- }
-
- protected Task On(AppClientRevoked @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Clients.Apply(@event);
- });
- }
-
- protected Task On(AppClientRenamed @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Clients.Apply(@event);
- });
- }
-
- protected Task On(AppClientUpdated @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Clients.Apply(@event);
- });
- }
-
- protected Task On(AppContributorRemoved @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Contributors.Apply(@event);
- });
- }
-
- protected Task On(AppContributorAssigned @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.Contributors.Apply(@event);
- });
- }
-
- protected Task On(AppLanguageAdded @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.LanguagesConfig.Apply(@event);
- });
- }
-
- protected Task On(AppLanguageRemoved @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.LanguagesConfig.Apply(@event);
- });
- }
-
- protected Task On(AppLanguageUpdated @event, EnvelopeHeaders headers)
- {
- return UpdateAppAsync(@event, headers, a =>
- {
- a.LanguagesConfig.Apply(@event);
- });
- }
-
- private async Task UpdateAppAsync(AppEvent @event, EnvelopeHeaders headers, Action updater)
- {
- await Collection.UpdateAsync(@event, headers, a =>
- {
- updater(a);
-
- a.ContributorIds = a.Contributors.Keys.ToArray();
- });
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs
index 11c54cc36..10d6f31ff 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Assets/MongoAssetEntity.cs
@@ -14,7 +14,12 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.Assets
{
- public sealed class MongoAssetEntity : MongoEntity, IAssetEntity
+ public sealed class MongoAssetEntity :
+ MongoEntity,
+ IAssetEntity,
+ IUpdateableEntityWithVersion,
+ IUpdateableEntityWithCreatedBy,
+ IUpdateableEntityWithLastModifiedBy
{
[BsonRequired]
[BsonElement]
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs
index 141e33e6f..070c535ec 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs
@@ -19,7 +19,11 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.Contents
{
- public sealed class MongoContentEntity : IContentEntity, IMongoEntity
+ public sealed class MongoContentEntity :
+ IContentEntity,
+ IUpdateableEntityWithVersion,
+ IUpdateableEntityWithCreatedBy,
+ IUpdateableEntityWithLastModifiedBy
{
private NamedContentData data;
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs
index 5ca896110..b0d763553 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs
@@ -19,7 +19,6 @@ using Squidex.Domain.Apps.Read.Contents;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Read.Schemas;
-using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
@@ -29,7 +28,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
{
private const string Prefix = "Projections_Content_";
private readonly IMongoDatabase database;
- private readonly ISchemaProvider schemas;
+ private readonly IAppProvider appProvider;
protected static FilterDefinitionBuilder Filter
{
@@ -63,13 +62,13 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
}
}
- public MongoContentRepository(IMongoDatabase database, ISchemaProvider schemas)
+ public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
{
Guard.NotNull(database, nameof(database));
- Guard.NotNull(schemas, nameof(schemas));
+ Guard.NotNull(appProvider, nameof(appProvider));
this.database = database;
- this.schemas = schemas;
+ this.appProvider = appProvider;
}
public async Task> QueryAsync(IAppEntity app, ISchemaEntity schema, Status[] status, ODataUriParser odataQuery)
@@ -177,11 +176,11 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
return contentEntity;
}
- private async Task ForSchemaAsync(Guid appId, Guid schemaId, Func, ISchemaEntity, Task> action)
+ private async Task ForSchemaAsync(NamedId appId, Guid schemaId, Func, ISchemaEntity, Task> action)
{
- var collection = GetCollection(appId);
+ var collection = GetCollection(appId.Id);
- var schema = await schemas.FindSchemaByIdAsync(schemaId, true);
+ var schema = await appProvider.GetSchemaAsync(appId.Name, schemaId, true);
if (schema == null)
{
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
index 077fa6cd1..fabf77afc 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs
@@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
protected Task On(ContentCreated @event, EnvelopeHeaders headers)
{
- return ForSchemaAsync(@event.AppId.Id, @event.SchemaId.Id, (collection, schema) =>
+ return ForSchemaAsync(@event.AppId, @event.SchemaId.Id, (collection, schema) =>
{
return collection.CreateAsync(@event, headers, content =>
{
@@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents
protected Task On(ContentUpdated @event, EnvelopeHeaders headers)
{
- return ForSchemaAsync(@event.AppId.Id, @event.SchemaId.Id, (collection, schema) =>
+ return ForSchemaAsync(@event.AppId, @event.SchemaId.Id, (collection, schema) =>
{
var idData = @event.Data?.ToIdModel(schema.SchemaDef, true);
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs
index b0563ae70..e09f4206e 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventEntity.cs
@@ -14,7 +14,10 @@ using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Read.MongoDb.History
{
- public sealed class MongoHistoryEventEntity : MongoEntity, IAppRefEntity, IEntityWithCreatedBy
+ public sealed class MongoHistoryEventEntity : MongoEntity,
+ IEntityWithAppRef,
+ IUpdateableEntityWithVersion,
+ IUpdateableEntityWithCreatedBy
{
[BsonRequired]
[BsonElement]
@@ -40,7 +43,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.History
[BsonElement]
public Dictionary Parameters { get; set; }
- RefToken IEntityWithCreatedBy.CreatedBy
+ RefToken IUpdateableEntityWithCreatedBy.CreatedBy
{
get
{
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventRepository.cs
index 53368fd51..6b0464bc3 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventRepository.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/History/MongoHistoryEventRepository.cs
@@ -83,10 +83,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.History
if (message != null)
{
- await Collection.CreateAsync((SquidexEvent)@event.Payload, @event.Headers, entity =>
+ var appEvent = (AppEvent)@event.Payload;
+
+ await Collection.CreateAsync(appEvent, @event.Headers, entity =>
{
entity.Id = Guid.NewGuid();
+ entity.AppId = appEvent.AppId.Id;
+
entity.Version = @event.Headers.EventStreamNumber();
entity.Channel = message.Channel;
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/History/ParsedHistoryEvent.cs b/src/Squidex.Domain.Apps.Read.MongoDb/History/ParsedHistoryEvent.cs
index 461af2374..e3520bd43 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/History/ParsedHistoryEvent.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/History/ParsedHistoryEvent.cs
@@ -12,6 +12,8 @@ using NodaTime;
using Squidex.Domain.Apps.Read.History;
using Squidex.Infrastructure;
+#pragma warning disable RECS0029 // Warns about property or indexer setters and event adders or removers that do not use the value parameter
+
namespace Squidex.Domain.Apps.Read.MongoDb.History
{
internal sealed class ParsedHistoryEvent : IHistoryEventEntity
@@ -22,26 +24,29 @@ namespace Squidex.Domain.Apps.Read.MongoDb.History
public Guid Id
{
get { return inner.Id; }
+ set { }
}
- public Guid EventId
+ public Instant Created
{
- get { return inner.Id; }
+ get { return inner.Created; }
+ set { }
}
- public RefToken Actor
+ public Instant LastModified
{
- get { return inner.Actor; }
+ get { return inner.LastModified; }
+ set { }
}
- public Instant Created
+ public RefToken Actor
{
- get { return inner.Created; }
+ get { return inner.Actor; }
}
- public Instant LastModified
+ public Guid EventId
{
- get { return inner.LastModified; }
+ get { return inner.Id; }
}
public long Version
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/MongoCollectionExtensions.cs b/src/Squidex.Domain.Apps.Read.MongoDb/MongoCollectionExtensions.cs
index c9cc5cd8c..4a15254dc 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/MongoCollectionExtensions.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/MongoCollectionExtensions.cs
@@ -18,25 +18,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb
{
public static class MongoCollectionExtensions
{
- public static Task CreateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IMongoEntity, new()
+ public static Task CreateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IEntity, new()
{
- var entity = EntityMapper.Create(@event, headers);
-
- updater(entity);
+ var entity = EntityMapper.Create(@event, headers, updater);
return collection.InsertOneIfNotExistsAsync(entity);
}
- public static async Task CreateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Func updater) where T : class, IMongoEntity, new()
- {
- var entity = EntityMapper.Create(@event, headers);
-
- await updater(entity);
-
- await collection.InsertOneIfNotExistsAsync(entity);
- }
-
- public static async Task UpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IMongoEntity, new()
+ public static async Task UpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IEntity, new()
{
var entity =
await collection.Find(t => t.Id == headers.AggregateId())
@@ -50,7 +39,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb
await collection.UpdateAsync(@event, headers, entity, updater);
}
- public static async Task TryUpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IMongoEntity, new()
+ public static async Task TryUpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IEntity, new()
{
var entity =
await collection.Find(t => t.Id == headers.AggregateId())
@@ -76,11 +65,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb
return false;
}
- private static async Task UpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, T entity, Action updater) where T : class, IMongoEntity, new()
+ private static async Task UpdateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, T entity, Action updater) where T : class, IEntity, new()
{
- EntityMapper.Update(@event, headers, entity);
-
- updater(entity);
+ entity.Update(@event, headers, updater);
await collection.ReplaceOneAsync(t => t.Id == entity.Id, entity);
}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventEntity.cs
index f060b9294..e48aa1543 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventEntity.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventEntity.cs
@@ -38,10 +38,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
[BsonElement]
public Instant? NextAttempt { get; set; }
- [BsonRequired]
- [BsonElement]
- public bool IsSending { get; set; }
-
[BsonRequired]
[BsonElement]
public RuleResult Result { get; set; }
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventRepository.cs
index db39b6675..99b22d125 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventRepository.cs
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleEventRepository.cs
@@ -36,14 +36,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
protected override Task SetupCollectionAsync(IMongoCollection collection)
{
return Task.WhenAll(
- collection.Indexes.CreateOneAsync(Index.Ascending(x => x.NextAttempt).Descending(x => x.IsSending)),
+ collection.Indexes.CreateOneAsync(Index.Ascending(x => x.NextAttempt)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Descending(x => x.Created)),
collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Expires), new CreateIndexOptions { ExpireAfter = TimeSpan.Zero }));
}
public Task QueryPendingAsync(Instant now, Func callback, CancellationToken cancellationToken = default(CancellationToken))
{
- return Collection.Find(x => x.NextAttempt < now && !x.IsSending).ForEachAsync(callback, cancellationToken);
+ return Collection.Find(x => x.NextAttempt < now).ForEachAsync(callback, cancellationToken);
}
public async Task> QueryByAppAsync(Guid appId, int skip = 0, int take = 20)
@@ -81,18 +81,12 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Rules
return Collection.InsertOneIfNotExistsAsync(entity);
}
- public Task MarkSendingAsync(Guid jobId)
- {
- return Collection.UpdateOneAsync(x => x.Id == jobId, Update.Set(x => x.IsSending, true));
- }
-
public Task MarkSentAsync(Guid jobId, string dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant? nextAttempt)
{
return Collection.UpdateOneAsync(x => x.Id == jobId,
Update.Set(x => x.Result, result)
.Set(x => x.LastDump, dump)
.Set(x => x.JobResult, jobResult)
- .Set(x => x.IsSending, false)
.Set(x => x.NextAttempt, nextAttempt)
.Inc(x => x.NumCalls, 1));
}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository.cs
deleted file mode 100644
index ab9ff0204..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-// ==========================================================================
-// MongoRuleRepository.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using MongoDB.Bson;
-using MongoDB.Driver;
-using Squidex.Domain.Apps.Read.Rules;
-using Squidex.Domain.Apps.Read.Rules.Repositories;
-using Squidex.Infrastructure;
-using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.MongoDb;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Rules
-{
- public partial class MongoRuleRepository : MongoRepositoryBase, IRuleRepository, IEventConsumer
- {
- private static readonly List EmptyRules = new List();
- private readonly SemaphoreSlim lockObject = new SemaphoreSlim(1);
- private Dictionary> inMemoryRules;
-
- public MongoRuleRepository(IMongoDatabase database)
- : base(database)
- {
- }
-
- protected override string CollectionName()
- {
- return "Projections_Rules";
- }
-
- protected override Task SetupCollectionAsync(IMongoCollection collection)
- {
- return Task.WhenAll(collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId)));
- }
-
- public async Task> QueryByAppAsync(Guid appId)
- {
- var entities =
- await Collection.Find(x => x.AppId == appId)
- .ToListAsync();
-
- return entities.OfType().ToList();
- }
-
- public async Task> QueryCachedByAppAsync(Guid appId)
- {
- await EnsureRulesLoadedAsync();
-
- return inMemoryRules.GetOrDefault(appId)?.ToList() ?? EmptyRules;
- }
-
- private async Task EnsureRulesLoadedAsync()
- {
- if (inMemoryRules == null)
- {
- try
- {
- await lockObject.WaitAsync();
-
- if (inMemoryRules == null)
- {
- inMemoryRules = new Dictionary>();
-
- var webhooks =
- await Collection.Find(new BsonDocument())
- .ToListAsync();
-
- foreach (var webhook in webhooks)
- {
- inMemoryRules.GetOrAddNew(webhook.AppId).Add(webhook);
- }
- }
- }
- finally
- {
- lockObject.Release();
- }
- }
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository_EventHandling.cs
deleted file mode 100644
index 26be2002a..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Rules/MongoRuleRepository_EventHandling.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-// ==========================================================================
-// MongoRuleRepository_EventHandling.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System.Threading.Tasks;
-using MongoDB.Driver;
-using Squidex.Domain.Apps.Events.Rules;
-using Squidex.Domain.Apps.Events.Rules.Utils;
-using Squidex.Infrastructure;
-using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.Dispatching;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Rules
-{
- public partial class MongoRuleRepository
- {
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return "^rule-"; }
- }
-
- public Task On(Envelope @event)
- {
- return this.DispatchActionAsync(@event.Payload, @event.Headers);
- }
-
- protected async Task On(RuleCreated @event, EnvelopeHeaders headers)
- {
- await EnsureRulesLoadedAsync();
-
- await Collection.CreateAsync(@event, headers, w =>
- {
- w.Rule = RuleEventDispatcher.Create(@event);
-
- inMemoryRules.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
- inMemoryRules.GetOrAddNew(w.AppId).Add(w);
- });
- }
-
- protected async Task On(RuleUpdated @event, EnvelopeHeaders headers)
- {
- await EnsureRulesLoadedAsync();
-
- await Collection.UpdateAsync(@event, headers, w =>
- {
- w.Rule.Apply(@event);
-
- inMemoryRules.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
- inMemoryRules.GetOrAddNew(w.AppId).Add(w);
- });
- }
-
- protected async Task On(RuleEnabled @event, EnvelopeHeaders headers)
- {
- await EnsureRulesLoadedAsync();
-
- await Collection.UpdateAsync(@event, headers, w =>
- {
- w.Rule.Apply(@event);
-
- inMemoryRules.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
- inMemoryRules.GetOrAddNew(w.AppId).Add(w);
- });
- }
-
- protected async Task On(RuleDisabled @event, EnvelopeHeaders headers)
- {
- await EnsureRulesLoadedAsync();
-
- await Collection.UpdateAsync(@event, headers, w =>
- {
- w.Rule.Apply(@event);
-
- inMemoryRules.GetOrAddNew(w.AppId).RemoveAll(x => x.Id == w.Id);
- inMemoryRules.GetOrAddNew(w.AppId).Add(w);
- });
- }
-
- protected async Task On(RuleDeleted @event, EnvelopeHeaders headers)
- {
- await EnsureRulesLoadedAsync();
-
- inMemoryRules.GetOrAddNew(@event.AppId.Id).RemoveAll(x => x.Id == @event.RuleId);
-
- await Collection.DeleteManyAsync(x => x.Id == @event.RuleId);
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs
deleted file mode 100644
index f17434021..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// ==========================================================================
-// MongoSchemaRepository.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using MongoDB.Driver;
-using Squidex.Domain.Apps.Core.Schemas;
-using Squidex.Domain.Apps.Read.Schemas;
-using Squidex.Domain.Apps.Read.Schemas.Repositories;
-using Squidex.Infrastructure;
-using Squidex.Infrastructure.MongoDb;
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
-{
- public partial class MongoSchemaRepository : MongoRepositoryBase, ISchemaRepository, ISchemaEventConsumer
- {
- private readonly FieldRegistry registry;
-
- public MongoSchemaRepository(IMongoDatabase database, FieldRegistry registry)
- : base(database)
- {
- Guard.NotNull(registry, nameof(registry));
-
- this.registry = registry;
- }
-
- protected override string CollectionName()
- {
- return "Projections_Schemas";
- }
-
- protected override Task SetupCollectionAsync(IMongoCollection collection)
- {
- return Task.WhenAll(
- collection.Indexes.CreateOneAsync(Index.Ascending(x => x.Name)),
- collection.Indexes.CreateOneAsync(Index.Ascending(x => x.AppId).Ascending(x => x.IsDeleted).Ascending(x => x.Name)));
- }
-
- public async Task> QueryAllAsync(Guid appId)
- {
- var schemaEntities =
- await Collection.Find(s => s.AppId == appId && !s.IsDeleted)
- .ToListAsync();
-
- return schemaEntities.OfType().ToList();
- }
-
- public async Task FindSchemaAsync(Guid appId, string name)
- {
- var schemaEntity =
- await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name)
- .FirstOrDefaultAsync();
-
- return schemaEntity;
- }
-
- public async Task FindSchemaAsync(Guid schemaId)
- {
- var schemaEntity =
- await Collection.Find(s => s.Id == schemaId)
- .FirstOrDefaultAsync();
-
- return schemaEntity;
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
deleted file mode 100644
index 772620b38..000000000
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-// ==========================================================================
-// MongoSchemaRepository_EventHandling.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Threading.Tasks;
-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.CQRS.Events;
-using Squidex.Infrastructure.Dispatching;
-using Squidex.Infrastructure.Reflection;
-
-#pragma warning disable CS0612 // Type or member is obsolete
-
-namespace Squidex.Domain.Apps.Read.MongoDb.Schemas
-{
- public partial class MongoSchemaRepository
- {
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return "^schema-"; }
- }
-
- public Task On(Envelope @event)
- {
- return this.DispatchActionAsync(@event.Payload, @event.Headers);
- }
-
- protected Task On(SchemaCreated @event, EnvelopeHeaders headers)
- {
- return Collection.CreateAsync(@event, headers, s =>
- {
- s.SchemaDef = SchemaEventDispatcher.Create(@event, registry);
-
- SimpleMapper.Map(@event, s);
- });
- }
-
- protected Task On(FieldDeleted @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldLocked @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldHidden @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldShown @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldDisabled @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldEnabled @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldUpdated @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(SchemaFieldsReordered @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(SchemaUpdated @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(SchemaPublished @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(SchemaUnpublished @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event);
- });
- }
-
- protected Task On(FieldAdded @event, EnvelopeHeaders headers)
- {
- return UpdateSchemaAsync(@event, headers, s =>
- {
- s.SchemaDef.Apply(@event, registry);
- });
- }
-
- protected Task On(ScriptsConfigured @event, EnvelopeHeaders headers)
- {
- return Collection.UpdateAsync(@event, headers, s =>
- {
- SimpleMapper.Map(@event, s);
- });
- }
-
- protected Task On(SchemaDeleted @event, EnvelopeHeaders headers)
- {
- return Collection.UpdateAsync(@event, headers, s =>
- {
- s.IsDeleted = true;
- });
- }
-
- protected Task On(WebhookAdded @event, EnvelopeHeaders headers)
- {
- return Collection.UpdateAsync(@event, headers, s =>
- {
- /* NOOP */
- });
- }
-
- protected Task On(WebhookDeleted @event, EnvelopeHeaders headers)
- {
- return Collection.UpdateAsync(@event, headers, s =>
- {
- /* NOOP */
- });
- }
-
- private Task UpdateSchemaAsync(SquidexEvent @event, EnvelopeHeaders headers, Action updater)
- {
- return Collection.UpdateAsync(@event, headers, s =>
- {
- updater(s);
-
- s.IsPublished = s.SchemaDef.IsPublished;
- });
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj
index 3040f8ead..b8e390b69 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj
+++ b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/src/Squidex/Config/Identity/AuthenticationExtensions.cs b/src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs
similarity index 53%
rename from src/Squidex/Config/Identity/AuthenticationExtensions.cs
rename to src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs
index 69fe4f294..39fe7824b 100644
--- a/src/Squidex/Config/Identity/AuthenticationExtensions.cs
+++ b/src/Squidex.Domain.Apps.Read/Apps/AppEntityExtensions.cs
@@ -1,22 +1,20 @@
// ==========================================================================
-// AuthenticationExtensions.cs
+// AppEntityExtensions.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
-using Microsoft.AspNetCore.Builder;
+using Squidex.Domain.Apps.Core;
-namespace Squidex.Config.Identity
+namespace Squidex.Domain.Apps.Read.Apps
{
- public static class AuthenticationExtensions
+ public static class AppEntityExtensions
{
- public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app)
+ public static PartitionResolver PartitionResolver(this IAppEntity entity)
{
- app.UseAuthentication();
-
- return app;
+ return entity.LanguagesConfig.ToResolver();
}
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs b/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
index 0eaba9695..df1743d69 100644
--- a/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs
@@ -6,13 +6,14 @@
// All rights reserved.
// ==========================================================================
-using Squidex.Domain.Apps.Core;
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; }
@@ -24,7 +25,5 @@ namespace Squidex.Domain.Apps.Read.Apps
AppContributors Contributors { get; }
LanguagesConfig LanguagesConfig { get; }
-
- PartitionResolver PartitionResolver { get; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Apps/Repositories/IAppRepository.cs b/src/Squidex.Domain.Apps.Read/Apps/Repositories/IAppRepository.cs
deleted file mode 100644
index 67fef0148..000000000
--- a/src/Squidex.Domain.Apps.Read/Apps/Repositories/IAppRepository.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// ==========================================================================
-// IAppRepository.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace Squidex.Domain.Apps.Read.Apps.Repositories
-{
- public interface IAppRepository
- {
- Task> QueryAllAsync(string subjectId);
-
- Task FindAppAsync(Guid appId);
-
- Task FindAppAsync(string name);
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/CachingAppProvider.cs b/src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/CachingAppProvider.cs
deleted file mode 100644
index becf14af4..000000000
--- a/src/Squidex.Domain.Apps.Read/Apps/Services/Implementations/CachingAppProvider.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-// ==========================================================================
-// CachingAppProvider.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Caching.Memory;
-using Squidex.Domain.Apps.Events;
-using Squidex.Domain.Apps.Read.Apps.Repositories;
-using Squidex.Infrastructure;
-using Squidex.Infrastructure.Caching;
-using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.Tasks;
-
-namespace Squidex.Domain.Apps.Read.Apps.Services.Implementations
-{
- public class CachingAppProvider : CachingProviderBase, IAppProvider, IEventConsumer
- {
- private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(30);
- private readonly IAppRepository repository;
-
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return string.Empty; }
- }
-
- public CachingAppProvider(IMemoryCache cache, IAppRepository repository)
- : base(cache)
- {
- Guard.NotNull(cache, nameof(cache));
-
- this.repository = repository;
- }
-
- public async Task FindAppByIdAsync(Guid appId)
- {
- var cacheKey = BuildIdCacheKey(appId);
-
- if (!Cache.TryGetValue(cacheKey, out IAppEntity result))
- {
- result = await repository.FindAppAsync(appId);
-
- Cache.Set(cacheKey, result, CacheDuration);
-
- if (result != null)
- {
- Cache.Set(BuildNameCacheKey(result.Name), result, CacheDuration);
- }
- }
-
- return result;
- }
-
- public async Task FindAppByNameAsync(string name)
- {
- Guard.NotNullOrEmpty(name, nameof(name));
-
- var cacheKey = BuildNameCacheKey(name);
-
- if (!Cache.TryGetValue(cacheKey, out IAppEntity result))
- {
- result = await repository.FindAppAsync(name);
-
- Cache.Set(cacheKey, result, CacheDuration);
-
- if (result != null)
- {
- Cache.Set(BuildIdCacheKey(result.Id), result, CacheDuration);
- }
- }
-
- return result;
- }
-
- public Task On(Envelope @event)
- {
- void Remove(NamedId id)
- {
- var cacheKeyById = BuildIdCacheKey(id.Id);
- var cacheKeyByName = BuildNameCacheKey(id.Name);
-
- Cache.Remove(cacheKeyById);
- Cache.Remove(cacheKeyByName);
-
- Cache.Invalidate(cacheKeyById);
- Cache.Invalidate(cacheKeyByName);
- }
-
- if (@event.Payload is AppEvent appEvent)
- {
- Remove(appEvent.AppId);
- }
-
- return TaskHelper.Done;
- }
-
- private static string BuildNameCacheKey(string name)
- {
- return $"App_Ids_{name}";
- }
-
- private static string BuildIdCacheKey(Guid schemaId)
- {
- return $"App_Names_{schemaId}";
- }
-
- public Task ClearAsync()
- {
- return TaskHelper.Done;
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs b/src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
index 46b0e4ea9..ae175abb6 100644
--- a/src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/Assets/IAssetEntity.cs
@@ -8,7 +8,7 @@
namespace Squidex.Domain.Apps.Read.Assets
{
- public interface IAssetEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
+ public interface IAssetEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{
string MimeType { get; }
diff --git a/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs
index 6ffea3aea..866917d8b 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/ContentHistoryEventsCreator.cs
@@ -29,14 +29,21 @@ namespace Squidex.Domain.Apps.Read.Contents
"deleted content item.");
AddEventMessage(
- "change status of content item to {[Status]}.");
+ "changed status of content item to {[Status]}.");
}
protected override Task CreateEventCoreAsync(Envelope @event)
{
var channel = $"contents.{@event.Headers.AggregateId()}";
- return Task.FromResult(ForEvent(@event.Payload, channel));
+ var result = ForEvent(@event.Payload, channel);
+
+ if (@event.Payload is ContentStatusChanged contentStatusChanged)
+ {
+ result = result.AddParameter("Status", contentStatusChanged.Status);
+ }
+
+ return Task.FromResult(result);
}
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs
index f2a8263c3..46bb4848e 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/ContentQueryService.cs
@@ -20,7 +20,6 @@ using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Contents.Edm;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.Schemas;
-using Squidex.Domain.Apps.Read.Schemas.Services;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
@@ -30,23 +29,23 @@ namespace Squidex.Domain.Apps.Read.Contents
public sealed class ContentQueryService : IContentQueryService
{
private readonly IContentRepository contentRepository;
- private readonly ISchemaProvider schemas;
+ private readonly IAppProvider appProvider;
private readonly IScriptEngine scriptEngine;
private readonly EdmModelBuilder modelBuilder;
public ContentQueryService(
IContentRepository contentRepository,
- ISchemaProvider schemas,
+ IAppProvider appProvider,
IScriptEngine scriptEngine,
EdmModelBuilder modelBuilder)
{
Guard.NotNull(contentRepository, nameof(contentRepository));
Guard.NotNull(scriptEngine, nameof(scriptEngine));
Guard.NotNull(modelBuilder, nameof(modelBuilder));
- Guard.NotNull(schemas, nameof(schemas));
+ Guard.NotNull(appProvider, nameof(appProvider));
this.contentRepository = contentRepository;
- this.schemas = schemas;
+ this.appProvider = appProvider;
this.scriptEngine = scriptEngine;
this.modelBuilder = modelBuilder;
}
@@ -156,12 +155,12 @@ namespace Squidex.Domain.Apps.Read.Contents
if (Guid.TryParse(schemaIdOrName, out var id))
{
- schema = await schemas.FindSchemaByIdAsync(id);
+ schema = await appProvider.GetSchemaAsync(app.Name, id);
}
if (schema == null)
{
- schema = await schemas.FindSchemaByNameAsync(app.Id, schemaIdOrName);
+ schema = await appProvider.GetSchemaAsync(app.Name, schemaIdOrName);
}
if (schema == null)
diff --git a/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs b/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs
index 7f3fd63ce..696d5282d 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs
@@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Read.Contents.Edm
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(60);
- return BuildEdmModel(schema.SchemaDef, app.PartitionResolver);
+ return BuildEdmModel(schema.SchemaDef, app.PartitionResolver());
});
return result;
diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs
index f20d7cf4c..17d31bb7d 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs
@@ -11,65 +11,36 @@ using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
-using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets.Repositories;
-using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Squidex.Infrastructure;
-using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
- public sealed class CachingGraphQLService : CachingProviderBase, IGraphQLService, IEventConsumer
+ 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 ISchemaRepository schemaRepository;
-
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return "^(schema-)|(apps-)"; }
- }
+ private readonly IAppProvider appProvider;
public CachingGraphQLService(IMemoryCache cache,
+ IAppProvider appProvider,
IAssetRepository assetRepository,
IContentQueryService contentQuery,
- IGraphQLUrlGenerator urlGenerator,
- ISchemaRepository schemaRepository)
+ IGraphQLUrlGenerator urlGenerator)
: base(cache)
{
- Guard.NotNull(schemaRepository, nameof(schemaRepository));
+ 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;
- this.schemaRepository = schemaRepository;
- }
-
- public Task ClearAsync()
- {
- return TaskHelper.Done;
- }
-
- public Task On(Envelope @event)
- {
- if (@event.Payload is AppEvent appEvent)
- {
- Cache.Remove(CreateCacheKey(appEvent.AppId.Id));
- }
-
- return TaskHelper.Done;
}
public async Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, ClaimsPrincipal user, GraphQLQuery query)
@@ -90,13 +61,13 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
private async Task GetModelAsync(IAppEntity app)
{
- var cacheKey = CreateCacheKey(app.Id);
+ var cacheKey = CreateCacheKey(app.Id, app.Etag);
var modelContext = Cache.Get(cacheKey);
if (modelContext == null)
{
- var allSchemas = await schemaRepository.QueryAllAsync(app.Id);
+ var allSchemas = await appProvider.GetSchemasAsync(app.Name);
modelContext = new GraphQLModel(app, allSchemas.Where(x => x.IsPublished), urlGenerator);
@@ -106,9 +77,9 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
return modelContext;
}
- private static object CreateCacheKey(Guid appId)
+ private static object CreateCacheKey(Guid appId, string etag)
{
- return $"GraphQLModel_{appId}";
+ return $"GraphQLModel_{appId}_{etag}";
}
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
index 50c64f64f..292cc138b 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
@@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
CanGenerateAssetSourceUrl = urlGenerator.CanGenerateAssetSourceUrl;
- partitionResolver = app.PartitionResolver;
+ partitionResolver = app.PartitionResolver();
assetType = new AssetGraphType(this);
assetListType = new ListGraphType(new NonNullGraphType(assetType));
diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs
index d8ee25be8..476230a3a 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLQuery.cs
@@ -6,6 +6,8 @@
// All rights reserved.
// ==========================================================================
+using Newtonsoft.Json.Linq;
+
namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{
public class GraphQLQuery
@@ -16,6 +18,6 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
public string Query { get; set; }
- public string Variables { get; set; }
+ public JObject Variables { get; set; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs b/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
index dc34ea65e..fb704d635 100644
--- a/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs
@@ -11,7 +11,7 @@ using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Read.Contents
{
- public interface IContentEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
+ public interface IContentEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{
Status Status { get; }
diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/EntityMapper.cs b/src/Squidex.Domain.Apps.Read/EntityMapper.cs
similarity index 65%
rename from src/Squidex.Domain.Apps.Read.MongoDb/EntityMapper.cs
rename to src/Squidex.Domain.Apps.Read/EntityMapper.cs
index 0557c28f4..525f94ff2 100644
--- a/src/Squidex.Domain.Apps.Read.MongoDb/EntityMapper.cs
+++ b/src/Squidex.Domain.Apps.Read/EntityMapper.cs
@@ -6,15 +6,15 @@
// All rights reserved.
// ==========================================================================
+using System;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.MongoDb;
-namespace Squidex.Domain.Apps.Read.MongoDb
+namespace Squidex.Domain.Apps.Read
{
public static class EntityMapper
{
- public static T Create(SquidexEvent @event, EnvelopeHeaders headers) where T : IMongoEntity, new()
+ public static T Create(SquidexEvent @event, EnvelopeHeaders headers, Action updater = null) where T : IEntity, new()
{
var entity = new T();
@@ -26,60 +26,62 @@ namespace Squidex.Domain.Apps.Read.MongoDb
SetAppId(@event, entity);
- return Update(@event, headers, entity);
+ return entity.Update(@event, headers, updater);
}
- public static T Update(SquidexEvent @event, EnvelopeHeaders headers, T entity) where T : IMongoEntity, new()
+ public static T Update(this T entity, SquidexEvent @event, EnvelopeHeaders headers, Action 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, IMongoEntity entity)
+ private static void SetId(EnvelopeHeaders headers, IEntity entity)
{
entity.Id = headers.AggregateId();
}
- private static void SetCreated(EnvelopeHeaders headers, IMongoEntity entity)
+ private static void SetCreated(EnvelopeHeaders headers, IEntity entity)
{
entity.Created = headers.Timestamp();
}
- private static void SetLastModified(EnvelopeHeaders headers, IMongoEntity entity)
+ private static void SetLastModified(EnvelopeHeaders headers, IEntity entity)
{
entity.LastModified = headers.Timestamp();
}
- private static void SetVersion(EnvelopeHeaders headers, IMongoEntity entity)
+ private static void SetVersion(EnvelopeHeaders headers, IEntity entity)
{
- if (entity is IEntityWithVersion withVersion)
+ if (entity is IUpdateableEntityWithVersion withVersion)
{
withVersion.Version = headers.EventStreamNumber();
}
}
- private static void SetCreatedBy(SquidexEvent @event, IMongoEntity entity)
+ private static void SetCreatedBy(SquidexEvent @event, IEntity entity)
{
- if (entity is IEntityWithCreatedBy withCreatedBy)
+ if (entity is IUpdateableEntityWithCreatedBy withCreatedBy)
{
withCreatedBy.CreatedBy = @event.Actor;
}
}
- private static void SetLastModifiedBy(SquidexEvent @event, IMongoEntity entity)
+ private static void SetLastModifiedBy(SquidexEvent @event, IEntity entity)
{
- if (entity is IEntityWithLastModifiedBy withModifiedBy)
+ if (entity is IUpdateableEntityWithLastModifiedBy withModifiedBy)
{
withModifiedBy.LastModifiedBy = @event.Actor;
}
}
- private static void SetAppId(SquidexEvent @event, IMongoEntity entity)
+ private static void SetAppId(SquidexEvent @event, IEntity entity)
{
- if (entity is IAppRefEntity app && @event is AppEvent appEvent)
+ if (entity is IUpdateableEntityWithAppRef app && @event is AppEvent appEvent)
{
app.AppId = appEvent.AppId.Id;
}
diff --git a/src/Squidex.Domain.Apps.Read/IAppProvider.cs b/src/Squidex.Domain.Apps.Read/IAppProvider.cs
new file mode 100644
index 000000000..296f8ff2d
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/IAppProvider.cs
@@ -0,0 +1,34 @@
+// ==========================================================================
+// 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 GetAppAsync(string appName);
+
+ Task GetSchemaAsync(string appName, Guid id, bool provideDeleted = false);
+
+ Task GetSchemaAsync(string appName, string name, bool provideDeleted = false);
+
+ Task> GetSchemasAsync(string appName);
+
+ Task> GetRulesAsync(string appName);
+
+ Task> GetUserApps(string userId);
+ }
+}
diff --git a/src/Squidex.Domain.Apps.Read/IEntity.cs b/src/Squidex.Domain.Apps.Read/IEntity.cs
index a93d2980a..89aea8bed 100644
--- a/src/Squidex.Domain.Apps.Read/IEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/IEntity.cs
@@ -13,10 +13,10 @@ namespace Squidex.Domain.Apps.Read
{
public interface IEntity
{
- Guid Id { get; }
+ Guid Id { get; set; }
- Instant Created { get; }
+ Instant Created { get; set; }
- Instant LastModified { get; }
+ Instant LastModified { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs b/src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs
new file mode 100644
index 000000000..1070538d1
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/IEntityWithAppRef.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// IEntityWithAppRef.cs
+// Squidex Headless CMS
+// ==========================================================================
+// Copyright (c) Squidex Group
+// All rights reserved.
+// ==========================================================================
+
+using System;
+
+namespace Squidex.Domain.Apps.Read
+{
+ public interface IEntityWithAppRef : IEntity
+ {
+ Guid AppId { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs b/src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs
index e06701152..4661a15ee 100644
--- a/src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs
+++ b/src/Squidex.Domain.Apps.Read/IEntityWithCreatedBy.cs
@@ -12,6 +12,6 @@ namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithCreatedBy
{
- RefToken CreatedBy { get; set; }
+ RefToken CreatedBy { get; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs b/src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs
index b80ed6b08..860918deb 100644
--- a/src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs
+++ b/src/Squidex.Domain.Apps.Read/IEntityWithVersion.cs
@@ -10,6 +10,6 @@ namespace Squidex.Domain.Apps.Read
{
public interface IEntityWithVersion
{
- long Version { get; set; }
+ long Version { get; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/IAppRefEntity.cs b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs
similarity index 82%
rename from src/Squidex.Domain.Apps.Read/IAppRefEntity.cs
rename to src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs
index f92f52ffc..316962a96 100644
--- a/src/Squidex.Domain.Apps.Read/IAppRefEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithAppRef.cs
@@ -1,5 +1,5 @@
// ==========================================================================
-// IAppRefEntity.cs
+// IUpdateableEntityWithAppRef.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
@@ -10,8 +10,8 @@ using System;
namespace Squidex.Domain.Apps.Read
{
- public interface IAppRefEntity : IEntity
+ public interface IUpdateableEntityWithAppRef
{
Guid AppId { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs
similarity index 63%
rename from src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs
rename to src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs
index f94bdee68..3675937c4 100644
--- a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEventConsumer.cs
+++ b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithCreatedBy.cs
@@ -1,16 +1,17 @@
// ==========================================================================
-// ISchemaEventConsumer.cs
+// IUpdateableEntityWithCreatedBy.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
-using Squidex.Infrastructure.CQRS.Events;
+using Squidex.Infrastructure;
-namespace Squidex.Domain.Apps.Read.Schemas
+namespace Squidex.Domain.Apps.Read
{
- public interface ISchemaEventConsumer : IEventConsumer
+ public interface IUpdateableEntityWithCreatedBy
{
+ RefToken CreatedBy { get; set; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs
new file mode 100644
index 000000000..d1aedc9f4
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithLastModifiedBy.cs
@@ -0,0 +1,17 @@
+// ==========================================================================
+// 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; }
+ }
+}
diff --git a/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs
new file mode 100644
index 000000000..8d08b4c6a
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/IUpdateableEntityWithVersion.cs
@@ -0,0 +1,15 @@
+// ==========================================================================
+// IUpdateableEntityWithVersion.cs
+// Squidex Headless CMS
+// ==========================================================================
+// Copyright (c) Squidex Group
+// All rights reserved.
+// ==========================================================================
+
+namespace Squidex.Domain.Apps.Read
+{
+ public interface IUpdateableEntityWithVersion
+ {
+ long Version { get; set; }
+ }
+}
diff --git a/src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs b/src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
index 6de2db9dd..424febe38 100644
--- a/src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/Rules/IRuleEntity.cs
@@ -10,8 +10,8 @@ using Squidex.Domain.Apps.Core.Rules;
namespace Squidex.Domain.Apps.Read.Rules
{
- public interface IRuleEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
+ public interface IRuleEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{
- Rule Rule { get; }
+ Rule RuleDef { get; }
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs b/src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs
index 256aa9b71..2b05e6741 100644
--- a/src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs
+++ b/src/Squidex.Domain.Apps.Read/Rules/Repositories/IRuleEventRepository.cs
@@ -22,8 +22,6 @@ namespace Squidex.Domain.Apps.Read.Rules.Repositories
Task EnqueueAsync(Guid id, Instant nextAttempt);
- Task MarkSendingAsync(Guid jobId);
-
Task MarkSentAsync(Guid jobId, string dump, RuleResult result, RuleJobResult jobResult, TimeSpan elapsed, Instant? nextCall);
Task QueryPendingAsync(Instant now, Func callback, CancellationToken cancellationToken = default(CancellationToken));
diff --git a/src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs b/src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs
index 3f6f6e7c6..b738dfeef 100644
--- a/src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs
+++ b/src/Squidex.Domain.Apps.Read/Rules/RuleDequeuer.cs
@@ -7,11 +7,13 @@
// ==========================================================================
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;
@@ -22,10 +24,10 @@ namespace Squidex.Domain.Apps.Read.Rules
public sealed class RuleDequeuer : DisposableObjectBase, IExternalSystem
{
private readonly ActionBlock requestBlock;
- private readonly TransformBlock blockBlock;
private readonly IRuleEventRepository ruleEventRepository;
private readonly RuleService ruleService;
private readonly CompletionTimer timer;
+ private readonly ConcurrentDictionary executing = new ConcurrentDictionary();
private readonly IClock clock;
private readonly ISemanticLog log;
@@ -44,15 +46,9 @@ namespace Squidex.Domain.Apps.Read.Rules
this.log = log;
requestBlock =
- new ActionBlock(MakeRequestAsync,
+ new ActionBlock(HandleAsync,
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 32, BoundedCapacity = 32 });
- blockBlock =
- new TransformBlock(x => BlockAsync(x),
- new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1, BoundedCapacity = 1 });
-
- blockBlock.LinkTo(requestBlock, new DataflowLinkOptions { PropagateCompletion = true });
-
timer = new CompletionTimer(5000, QueryAsync);
}
@@ -62,7 +58,7 @@ namespace Squidex.Domain.Apps.Read.Rules
{
timer.StopAsync().Wait();
- blockBlock.Complete();
+ requestBlock.Complete();
requestBlock.Completion.Wait();
}
}
@@ -82,7 +78,7 @@ namespace Squidex.Domain.Apps.Read.Rules
{
var now = clock.GetCurrentInstant();
- await ruleEventRepository.QueryPendingAsync(now, blockBlock.SendAsync, cancellationToken);
+ await ruleEventRepository.QueryPendingAsync(now, requestBlock.SendAsync, cancellationToken);
}
catch (Exception ex)
{
@@ -92,78 +88,70 @@ namespace Squidex.Domain.Apps.Read.Rules
}
}
- private async Task BlockAsync(IRuleEventEntity @event)
+ public async Task HandleAsync(IRuleEventEntity @event)
{
- try
+ if (!executing.TryAdd(@event.Id, false))
{
- await ruleEventRepository.MarkSendingAsync(@event.Id);
-
- return @event;
+ return;
}
- catch (Exception ex)
- {
- log.LogError(ex, w => w
- .WriteProperty("action", "BlockWebhookEvent")
- .WriteProperty("status", "Failed"));
-
- throw;
- }
- }
- private async Task MakeRequestAsync(IRuleEventEntity @event)
- {
try
{
var job = @event.Job;
var response = await ruleService.InvokeAsync(job.ActionName, job.ActionData);
- Instant? nextCall = null;
-
- if (response.Result != RuleResult.Success)
- {
- switch (@event.NumCalls)
- {
- case 0:
- nextCall = job.Created.Plus(Duration.FromMinutes(5));
- break;
- case 1:
- nextCall = job.Created.Plus(Duration.FromHours(1));
- break;
- case 2:
- nextCall = job.Created.Plus(Duration.FromHours(6));
- break;
- case 3:
- nextCall = job.Created.Plus(Duration.FromHours(12));
- break;
- }
- }
-
- RuleJobResult jobResult;
+ var jobInvoke = ComputeJobInvoke(response.Result, @event, job);
+ var jobResult = ComputeJobResult(response.Result, jobInvoke);
- if (response.Result != RuleResult.Success && !nextCall.HasValue)
- {
- jobResult = RuleJobResult.Failed;
- }
- else if (response.Result != RuleResult.Success && nextCall.HasValue)
- {
- jobResult = RuleJobResult.Retry;
- }
- else
- {
- jobResult = RuleJobResult.Success;
- }
-
- await ruleEventRepository.MarkSentAsync(@event.Id, response.Dump, response.Result, jobResult, response.Elapsed, nextCall);
+ 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;
+ }
+ }
- throw;
+ 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;
}
}
}
diff --git a/src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs b/src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs
index 01870edb7..5a3a492d4 100644
--- a/src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs
+++ b/src/Squidex.Domain.Apps.Read/Rules/RuleEnqueuer.cs
@@ -19,7 +19,7 @@ namespace Squidex.Domain.Apps.Read.Rules
public sealed class RuleEnqueuer : IEventConsumer
{
private readonly IRuleEventRepository ruleEventRepository;
- private readonly IRuleRepository ruleRepository;
+ private readonly IAppProvider appProvider;
private readonly RuleService ruleService;
public string Name
@@ -33,17 +33,18 @@ namespace Squidex.Domain.Apps.Read.Rules
}
public RuleEnqueuer(
- IRuleEventRepository ruleEventRepository,
- IRuleRepository ruleRepository,
+ IRuleEventRepository ruleEventRepository, IAppProvider appProvider,
RuleService ruleService)
{
Guard.NotNull(ruleEventRepository, nameof(ruleEventRepository));
- Guard.NotNull(ruleRepository, nameof(ruleRepository));
Guard.NotNull(ruleService, nameof(ruleService));
+ Guard.NotNull(appProvider, nameof(appProvider));
+
this.ruleEventRepository = ruleEventRepository;
- this.ruleRepository = ruleRepository;
this.ruleService = ruleService;
+
+ this.appProvider = appProvider;
}
public Task ClearAsync()
@@ -55,11 +56,11 @@ namespace Squidex.Domain.Apps.Read.Rules
{
if (@event.Payload is AppEvent appEvent)
{
- var rules = await ruleRepository.QueryCachedByAppAsync(appEvent.AppId.Id);
+ var rules = await appProvider.GetRulesAsync(appEvent.AppId.Name);
foreach (var ruleEntity in rules)
{
- var job = ruleService.CreateJob(ruleEntity.Rule, @event);
+ var job = ruleService.CreateJob(ruleEntity.RuleDef, @event);
if (job != null)
{
diff --git a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
index 17348a1b4..90caafd74 100644
--- a/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
+++ b/src/Squidex.Domain.Apps.Read/Schemas/ISchemaEntity.cs
@@ -10,7 +10,7 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Read.Schemas
{
- public interface ISchemaEntity : IAppRefEntity, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
+ public interface ISchemaEntity : IEntityWithAppRef, IEntityWithCreatedBy, IEntityWithLastModifiedBy, IEntityWithVersion
{
string Name { get; }
diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs b/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs
deleted file mode 100644
index 147e4d1a9..000000000
--- a/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// ==========================================================================
-// ISchemaRepository.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.Schemas.Repositories
-{
- public interface ISchemaRepository
- {
- Task> QueryAllAsync(Guid appId);
-
- Task FindSchemaAsync(Guid appId, string name);
-
- Task FindSchemaAsync(Guid schemaId);
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Services/ISchemaProvider.cs b/src/Squidex.Domain.Apps.Read/Schemas/Services/ISchemaProvider.cs
deleted file mode 100644
index 6d50efe1a..000000000
--- a/src/Squidex.Domain.Apps.Read/Schemas/Services/ISchemaProvider.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-// ==========================================================================
-// ISchemaProvider.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Threading.Tasks;
-
-namespace Squidex.Domain.Apps.Read.Schemas.Services
-{
- public interface ISchemaProvider
- {
- Task FindSchemaByIdAsync(Guid id, bool provideDeleted = false);
-
- Task FindSchemaByNameAsync(Guid appId, string name);
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs b/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
deleted file mode 100644
index ab0a609bd..000000000
--- a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-// ==========================================================================
-// CachingSchemaProvider.cs
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex Group
-// All rights reserved.
-// ==========================================================================
-
-using System;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Caching.Memory;
-using Squidex.Domain.Apps.Events;
-using Squidex.Domain.Apps.Read.Schemas.Repositories;
-using Squidex.Infrastructure;
-using Squidex.Infrastructure.Caching;
-using Squidex.Infrastructure.CQRS.Events;
-using Squidex.Infrastructure.Tasks;
-
-namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations
-{
- public class CachingSchemaProvider : CachingProviderBase, ISchemaProvider, IEventConsumer
- {
- private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10);
- private readonly ISchemaRepository repository;
-
- public string Name
- {
- get { return GetType().Name; }
- }
-
- public string EventsFilter
- {
- get { return string.Empty; }
- }
-
- public CachingSchemaProvider(IMemoryCache cache, ISchemaRepository repository)
- : base(cache)
- {
- Guard.NotNull(repository, nameof(repository));
-
- this.repository = repository;
- }
-
- public async Task FindSchemaByIdAsync(Guid id, bool provideDeleted = false)
- {
- var cacheKey = BuildIdCacheKey(id);
-
- if (!Cache.TryGetValue(cacheKey, out ISchemaEntity result))
- {
- result = await repository.FindSchemaAsync(id);
-
- Cache.Set(cacheKey, result, CacheDuration);
-
- if (result != null)
- {
- Cache.Set(BuildNameCacheKey(result.AppId, result.Name), result, CacheDuration);
- }
- }
-
- if (result != null && result.IsDeleted && !provideDeleted)
- {
- result = null;
- }
-
- return result;
- }
-
- public async Task FindSchemaByNameAsync(Guid appId, string name)
- {
- Guard.NotNullOrEmpty(name, nameof(name));
-
- var cacheKey = BuildNameCacheKey(appId, name);
-
- if (!Cache.TryGetValue(cacheKey, out ISchemaEntity result))
- {
- result = await repository.FindSchemaAsync(appId, name);
-
- Cache.Set(cacheKey, result, CacheDuration);
-
- if (result != null)
- {
- Cache.Set(BuildIdCacheKey(result.Id), result, CacheDuration);
- }
- }
-
- if (result != null && result.IsDeleted)
- {
- result = null;
- }
-
- return result;
- }
-
- public Task On(Envelope @event)
- {
- void Remove(NamedId appId, NamedId schemaId)
- {
- var cacheKeyById = BuildIdCacheKey(schemaId.Id);
- var cacheKeyByName = BuildNameCacheKey(appId.Id, schemaId.Name);
-
- Cache.Remove(cacheKeyById);
- Cache.Remove(cacheKeyByName);
-
- Cache.Invalidate(cacheKeyById);
- Cache.Invalidate(cacheKeyByName);
- }
-
- if (@event.Payload is SchemaEvent schemaEvent)
- {
- Remove(schemaEvent.AppId, schemaEvent.SchemaId);
- }
-
- return TaskHelper.Done;
- }
-
- private static string BuildNameCacheKey(Guid appId, string name)
- {
- return $"Schema_Ids_{appId}_{name}";
- }
-
- private static string BuildIdCacheKey(Guid schemaId)
- {
- return $"Schema_Names_{schemaId}";
- }
-
- public Task ClearAsync()
- {
- return TaskHelper.Done;
- }
- }
-}
diff --git a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj
index 17670622a..58922eba3 100644
--- a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj
+++ b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj
@@ -15,8 +15,8 @@
-
-
+
+
diff --git a/src/Squidex.Domain.Apps.Read/State/AppProvider.cs b/src/Squidex.Domain.Apps.Read/State/AppProvider.cs
new file mode 100644
index 000000000..935d0708e
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/State/AppProvider.cs
@@ -0,0 +1,89 @@
+// ==========================================================================
+// 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.Orleans.Grains;
+using Squidex.Infrastructure;
+using Squidex.Infrastructure.States;
+
+namespace Squidex.Domain.Apps.Read.State.Orleans
+{
+ public sealed class AppProvider : IAppProvider
+ {
+ private readonly IStateFactory factory;
+
+ public AppProvider(IStateFactory factory)
+ {
+ Guard.NotNull(factory, nameof(factory));
+
+ this.factory = factory;
+ }
+
+ public async Task GetAppAsync(string appName)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetAppAsync();
+ }
+
+ public async Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(string appName, Guid id)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetAppWithSchemaAsync(id);
+ }
+
+ public async Task> GetRulesAsync(string appName)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetRulesAsync();
+ }
+
+ public async Task GetSchemaAsync(string appName, Guid id, bool provideDeleted = false)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetSchemaAsync(id, provideDeleted);
+ }
+
+ public async Task GetSchemaAsync(string appName, string name, bool provideDeleted = false)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetSchemaAsync(name, provideDeleted);
+ }
+
+ public async Task> GetSchemasAsync(string appName)
+ {
+ var app = await factory.GetAsync(appName);
+
+ return await app.GetSchemasAsync();
+ }
+
+ public async Task> GetUserApps(string userId)
+ {
+ var appUser = await factory.GetAsync(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).ToList();
+ }
+ }
+}
diff --git a/src/Squidex.Domain.Apps.Read/State/AppStateEventConsumer.cs b/src/Squidex.Domain.Apps.Read/State/AppStateEventConsumer.cs
new file mode 100644
index 000000000..48e4c1678
--- /dev/null
+++ b/src/Squidex.Domain.Apps.Read/State/AppStateEventConsumer.cs
@@ -0,0 +1,70 @@
+// ==========================================================================
+// 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.Orleans.Grains;
+using Squidex.Infrastructure;
+using Squidex.Infrastructure.CQRS.Events;
+using Squidex.Infrastructure.States;
+using Squidex.Infrastructure.Tasks;
+
+namespace Squidex.Domain.Apps.Read.State.Orleans
+{
+ 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 @event)
+ {
+ if (@event.Payload is AppEvent appEvent)
+ {
+ var appGrain = await factory.GetAsync