diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index ed1d08229..9ddca62b1 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -40,11 +40,19 @@ jobs: cache-to: type=gha,mode=max tags: squidex-local + - name: Build - TestContainers + uses: kohlerdominik/docker-run-action@v2.0.0 + with: + image: mcr.microsoft.com/dotnet/sdk:8.0 + default_network: host + volumes: ${{ github.workspace }}:/src + run: dotnet test /src/backend/Squidex.sln --filter Category==TestContainers + - name: Test - Start Compose run: docker compose up -d working-directory: tools/TestSuite - - name: Test - RUN + - name: Test - RUN on Mongo uses: kohlerdominik/docker-run-action@v2.0.0 with: image: mcr.microsoft.com/dotnet/sdk:8.0 @@ -58,21 +66,63 @@ jobs: volumes: ${{ github.workspace }}:/src run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated - - name: Test - RUN on path + - name: Test - RUN on Postgres uses: kohlerdominik/docker-run-action@v2.0.0 with: image: mcr.microsoft.com/dotnet/sdk:8.0 environment: | CONFIG__BACKUPURL=http://localhost:5000 CONFIG__WAIT=60 - CONFIG__SERVER__URL=http://localhost:8081/squidex + CONFIG__SERVER__URL=http://localhost:8083 WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher default_network: host options: --name test2 volumes: ${{ github.workspace }}:/src + run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter "Category!=NotAutomated & Category!=MongoOnly" + + - name: Test - RUN on MySql + uses: kohlerdominik/docker-run-action@v2.0.0 + with: + image: mcr.microsoft.com/dotnet/sdk:8.0 + environment: | + CONFIG__BACKUPURL=http://localhost:5000 + CONFIG__WAIT=60 + CONFIG__SERVER__URL=http://localhost:8084 + WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher + default_network: host + options: --name test3 + volumes: ${{ github.workspace }}:/src + run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter "Category!=NotAutomated & Category!=MongoOnly" + + - name: Test - RUN on SqlServer + uses: kohlerdominik/docker-run-action@v2.0.0 + with: + image: mcr.microsoft.com/dotnet/sdk:8.0 + environment: | + CONFIG__BACKUPURL=http://localhost:5000 + CONFIG__WAIT=60 + CONFIG__SERVER__URL=http://localhost:8085 + WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher + default_network: host + options: --name test4 + volumes: ${{ github.workspace }}:/src + run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter "Category!=NotAutomated & Category!=MongoOnly" + + - name: Test - RUN on Mongo with path + uses: kohlerdominik/docker-run-action@v2.0.0 + with: + image: mcr.microsoft.com/dotnet/sdk:8.0 + environment: | + CONFIG__BACKUPURL=http://localhost:5000 + CONFIG__WAIT=60 + CONFIG__SERVER__URL=http://localhost:8081/squidex + WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher + default_network: host + options: --name test5 + volumes: ${{ github.workspace }}:/src run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated - - name: Test - RUN with dedicated collections + - name: Test - RUN on Mongo with dedicated collections uses: kohlerdominik/docker-run-action@v2.0.0 with: image: mcr.microsoft.com/dotnet/sdk:8.0 @@ -82,7 +132,7 @@ jobs: CONFIG__SERVER__URL=http://localhost:8082 WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher default_network: host - options: --name test3 + options: --name test6 volumes: ${{ github.workspace }}:/src run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated @@ -112,8 +162,14 @@ jobs: if: failure() uses: jwalton/gh-docker-logs@v2.2.2 with: - images: 'squidex-local,squidex/resizer' - tail: '100' + dest: './docker-logs' + + - name: Test - Upload docker logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: docker-logs + path: './docker-logs' - name: Test - Cleanup if: always() diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6958dddd3..bcdc7275f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,34 +54,6 @@ jobs: volumes: ${{ github.workspace }}:/src run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated - - name: Test - RUN on path - uses: kohlerdominik/docker-run-action@v2.0.0 - with: - image: mcr.microsoft.com/dotnet/sdk:8.0 - environment: | - CONFIG__BACKUPURL=http://localhost:5000 - CONFIG__WAIT=60 - CONFIG__SERVER__URL=http://localhost:8081/squidex - WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher - default_network: host - options: --name test2 - volumes: ${{ github.workspace }}:/src - run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated - - - name: Test - RUN with dedicated collections - uses: kohlerdominik/docker-run-action@v2.0.0 - with: - image: squidex/build:9 - environment: | - CONFIG__BACKUPURL=http://localhost:5000 - CONFIG__WAIT=60 - CONFIG__SERVER__URL=http://localhost:8082 - WEBHOOKCATCHER__HOST__ENDPOINT=webhookcatcher - default_network: host - options: --name test3 - volumes: ${{ github.workspace }}:/src - run: dotnet test /src/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj --filter Category!=NotAutomated - - name: Test - Install Playwright Dependencies run: npm ci working-directory: './tools/e2e' @@ -108,8 +80,14 @@ jobs: if: failure() uses: jwalton/gh-docker-logs@v2.2.2 with: - images: 'squidex-local,squidex/resizer' - tail: '100' + dest: './docker-logs' + + - name: Test - Upload docker logs + if: failure() + uses: actions/upload-artifact@v4 + with: + name: docker-logs + path: './docker-logs' - name: Test - Cleanup if: always() diff --git a/Dockerfile b/Dockerfile index b52b8d6ca..d61d83392 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN dotnet restore COPY backend . # Test Backend -RUN dotnet test --no-restore --filter Category!=Dependencies +RUN dotnet test --no-restore --filter "Category!=Dependencies & Category!=TestContainer" # Publish RUN dotnet publish --no-restore src/Squidex/Squidex.csproj --output /build/ --configuration Release -p:version=$SQUIDEX__BUILD__VERSION diff --git a/backend/.editorconfig b/backend/.editorconfig index bc6c6973b..aaf358673 100644 --- a/backend/.editorconfig +++ b/backend/.editorconfig @@ -55,7 +55,7 @@ dotnet_diagnostic.MA0004.severity = none dotnet_diagnostic.MA0006.severity = none # MA0007: Add a comma after the last value -dotnet_diagnostic.MA0007.severity = none +dotnet_diagnostic.MA0007.severity = warning # MA0008: Add StructLayoutAttribute dotnet_diagnostic.MA0008.severity = none diff --git a/backend/Squidex.sln b/backend/Squidex.sln index 2d9c70050..d1a1ce102 100644 --- a/backend/Squidex.sln +++ b/backend/Squidex.sln @@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Data.MongoDb", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Data.Tests", "tests\Squidex.Data.Tests\Squidex.Data.Tests.csproj", "{AA2F3C32-E3C8-4DF3-A365-F25C7EC19BCD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Data.EntityFramework", "src\Squidex.Data.EntityFramework\Squidex.Data.EntityFramework.csproj", "{0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -262,6 +264,18 @@ Global {AA2F3C32-E3C8-4DF3-A365-F25C7EC19BCD}.Release|x64.Build.0 = Release|Any CPU {AA2F3C32-E3C8-4DF3-A365-F25C7EC19BCD}.Release|x86.ActiveCfg = Release|Any CPU {AA2F3C32-E3C8-4DF3-A365-F25C7EC19BCD}.Release|x86.Build.0 = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|x64.ActiveCfg = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|x64.Build.0 = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|x86.ActiveCfg = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Debug|x86.Build.0 = Debug|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|Any CPU.Build.0 = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|x64.ActiveCfg = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|x64.Build.0 = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|x86.ActiveCfg = Release|Any CPU + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -284,6 +298,7 @@ Global {23615A39-F3FB-4575-A91C-535899DFB636} = {94207AA6-4923-4183-A558-E0F8196B8CA3} {F754F05E-02FF-47B2-AB46-BB05C7E6B29D} = {3378B841-53F8-48CC-87C1-1B30CF912BFD} {AA2F3C32-E3C8-4DF3-A365-F25C7EC19BCD} = {3378B841-53F8-48CC-87C1-1B30CF912BFD} + {0348CFDA-4DA1-4DB5-9C6F-0D234FE3E4DA} = {3378B841-53F8-48CC-87C1-1B30CF912BFD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {02F2E872-3141-44F5-BD6A-33CD84E9FE08} diff --git a/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs index 0faa5e526..f1c041f49 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs @@ -71,8 +71,8 @@ public sealed class AlgoliaActionHandler(RuleEventFormatter formatter, IScriptEn { More = new Dictionary { - ["error"] = $"Invalid JSON: {ex.Message}" - } + ["error"] = $"Invalid JSON: {ex.Message}", + }, }; } @@ -85,7 +85,7 @@ public sealed class AlgoliaActionHandler(RuleEventFormatter formatter, IScriptEn ApiKey = action.ApiKey, Content = delete ? null : serializer.Serialize(content, true), ContentId = contentId, - IndexName = indexName + IndexName = indexName, }; return (ruleDescription, ruleJob); @@ -106,7 +106,7 @@ public sealed class AlgoliaActionHandler(RuleEventFormatter formatter, IScriptEn { var raw = new[] { - new JRaw(job.Content) + new JRaw(job.Content), }; var response = await index.SaveObjectsAsync(raw, null, ct, true); diff --git a/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueActionHandler.cs index a25304ff0..4f72bfd00 100644 --- a/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueActionHandler.cs @@ -46,7 +46,7 @@ public sealed class AzureQueueActionHandler(RuleEventFormatter formatter) : Rule { QueueConnectionString = action.ConnectionString, QueueName = queueName!, - MessageBodyV2 = requestBody + MessageBodyV2 = requestBody, }; return (ruleText, ruleJob); diff --git a/backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs index 76b40369b..53682004c 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Comment/CommentActionHandler.cs @@ -26,7 +26,7 @@ public sealed class CommentActionHandler(RuleEventFormatter formatter, ICollabor var ruleJob = new CommentCreated { - AppId = contentEvent.AppId + AppId = contentEvent.AppId, }; var text = await FormatAsync(action.Text, @event); diff --git a/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs index 58e9fcc4a..de1a8e90d 100644 --- a/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentActionHandler.cs @@ -25,7 +25,7 @@ public sealed class CreateContentActionHandler(RuleEventFormatter formatter, IAp { var ruleJob = new Command { - AppId = @event.AppId + AppId = @event.AppId, }; var schema = await appProvider.GetSchemaAsync(@event.AppId.Id, action.Schema, true) diff --git a/backend/extensions/Squidex.Extensions/Actions/DeepDetect/DeepDetectActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/DeepDetect/DeepDetectActionHandler.cs index dafbb4427..3c2b1f083 100644 --- a/backend/extensions/Squidex.Extensions/Actions/DeepDetect/DeepDetectActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/DeepDetect/DeepDetectActionHandler.cs @@ -54,7 +54,7 @@ internal sealed partial class DeepDetectActionHandler( AssetId = assetEvent.Id, MaximumTags = action.MaximumTags, MinimumPropability = action.MinimumProbability, - Url = urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id.ToString(), assetEvent.FileVersion) + Url = urlGenerator.AssetContent(assetEvent.AppId, assetEvent.Id.ToString(), assetEvent.FileVersion), }; return Task.FromResult((Description, ruleJob)); @@ -81,7 +81,7 @@ internal sealed partial class DeepDetectActionHandler( data = new[] { job.Url, - } + }, }, ct); var body = await response.Content.ReadAsStringAsync(ct); @@ -120,7 +120,7 @@ internal sealed partial class DeepDetectActionHandler( AssetId = asset.Id, AppId = asset.AppId, Actor = job.Actor, - FromRule = true + FromRule = true, }; foreach (var tag in tags) diff --git a/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs index be0ba9724..db4fbdaf7 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs @@ -24,7 +24,7 @@ public sealed class DiscourseActionHandler(RuleEventFormatter formatter, IHttpCl var json = new Dictionary { - ["title"] = await FormatAsync(action.Title!, @event) + ["title"] = await FormatAsync(action.Title!, @event), }; if (action.Topic != null) @@ -46,7 +46,7 @@ public sealed class DiscourseActionHandler(RuleEventFormatter formatter, IHttpCl ApiKey = action.ApiKey, ApiUserName = action.ApiUsername, RequestUrl = url, - RequestBody = requestBody + RequestBody = requestBody, }; var description = @@ -64,7 +64,7 @@ public sealed class DiscourseActionHandler(RuleEventFormatter formatter, IHttpCl var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl) { - Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") + Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json"), }; request.Headers.TryAddWithoutValidation("Api-Key", job.ApiKey); diff --git a/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs index 5309b6bfa..8f6d352ba 100644 --- a/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs @@ -54,7 +54,7 @@ public sealed class ElasticSearchActionHandler(RuleEventFormatter formatter, ISc ServerHost = action.Host.ToString(), ServerUser = action.Username, ServerPassword = action.Password, - ContentId = contentId + ContentId = contentId, }; if (delete) @@ -88,8 +88,8 @@ public sealed class ElasticSearchActionHandler(RuleEventFormatter formatter, ISc { More = new Dictionary { - ["error"] = $"Invalid JSON: {ex.Message}" - } + ["error"] = $"Invalid JSON: {ex.Message}", + }, }; } diff --git a/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs index 05ed16296..904b555e8 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Email/EmailActionHandler.cs @@ -28,7 +28,7 @@ public sealed class EmailActionHandler(RuleEventFormatter formatter) : RuleActio MessageFrom = (await FormatAsync(action.MessageFrom, @event))!, MessageTo = (await FormatAsync(action.MessageTo, @event))!, MessageSubject = (await FormatAsync(action.MessageSubject, @event))!, - MessageBody = (await FormatAsync(action.MessageBody, @event))! + MessageBody = (await FormatAsync(action.MessageBody, @event))!, }; var description = $"Send an email to {ruleJob.MessageTo}"; @@ -58,7 +58,7 @@ public sealed class EmailActionHandler(RuleEventFormatter formatter) : RuleActio smtpMessage.Body = new TextPart(TextFormat.Html) { - Text = job.MessageBody + Text = job.MessageBody, }; smtpMessage.Subject = job.MessageSubject; diff --git a/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs index 1996542dd..bdaed974c 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyActionHandler.cs @@ -30,7 +30,7 @@ public sealed class FastlyActionHandler(RuleEventFormatter formatter, IHttpClien { Key = id, FastlyApiKey = action.ApiKey, - FastlyServiceID = action.ServiceId + FastlyServiceID = action.ServiceId, }; return (Description, ruleJob); diff --git a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaActionHandler.cs index 86c634b7f..53b6d51be 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaActionHandler.cs @@ -46,7 +46,7 @@ public sealed class KafkaActionHandler(RuleEventFormatter formatter, KafkaProduc Headers = await ParseHeadersAsync(action.Headers, @event), Schema = action.Schema, PartitionKey = await FormatAsync(action.PartitionKey, @event), - PartitionCount = action.PartitionCount + PartitionCount = action.PartitionCount, }; return (Description, ruleJob); diff --git a/backend/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs index 9efc5a845..2fee1e133 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Medium/MediumActionHandler.cs @@ -39,7 +39,7 @@ public sealed class MediumActionHandler(RuleEventFormatter formatter, IHttpClien contentFormat = action.IsHtml ? "html" : "markdown", content = await FormatAsync(action.Content, @event), canonicalUrl = await FormatAsync(action.CanonicalUrl, @event), - tags = await ParseTagsAsync(@event, action) + tags = await ParseTagsAsync(@event, action), }; ruleJob.RequestBody = ToJson(requestBody); @@ -108,7 +108,7 @@ public sealed class MediumActionHandler(RuleEventFormatter formatter, IHttpClien { var request = new HttpRequestMessage(HttpMethod.Post, path) { - Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") + Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json"), }; request.Headers.Add("Authorization", $"Bearer {job.AccessToken}"); diff --git a/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs index f3f1f6f3c..40e90b68e 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationActionHandler.cs @@ -41,7 +41,7 @@ public sealed class NotificationActionHandler(RuleEventFormatter formatter, ICol CommentId = DomainId.NewGuid(), CommentsId = DomainId.Create(user.Id), FromRule = true, - Text = (await FormatAsync(action.Text, @event))! + Text = (await FormatAsync(action.Text, @event))!, }; if (!string.IsNullOrWhiteSpace(action.Url)) diff --git a/backend/extensions/Squidex.Extensions/Actions/OpenSearch/OpenSearchActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/OpenSearch/OpenSearchActionHandler.cs index 46411e233..7b065f8eb 100644 --- a/backend/extensions/Squidex.Extensions/Actions/OpenSearch/OpenSearchActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/OpenSearch/OpenSearchActionHandler.cs @@ -54,7 +54,7 @@ public sealed class OpenSearchActionHandler(RuleEventFormatter formatter, IScrip ServerHost = action.Host.ToString(), ServerUser = action.Username, ServerPassword = action.Password, - ContentId = contentId + ContentId = contentId, }; if (delete) @@ -88,8 +88,8 @@ public sealed class OpenSearchActionHandler(RuleEventFormatter formatter, IScrip { More = new Dictionary { - ["error"] = $"Invalid JSON: {ex.Message}" - } + ["error"] = $"Invalid JSON: {ex.Message}", + }, }; } diff --git a/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs index f3dd7c652..cd756db8e 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs @@ -32,7 +32,7 @@ public sealed class PrerenderActionHandler(RuleEventFormatter formatter, IHttpCl var request = new HttpRequestMessage(HttpMethod.Post, "/recache") { - Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") + Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json"), }; return await httpClient.OneWayRequestAsync(request, job.RequestBody, ct); diff --git a/backend/extensions/Squidex.Extensions/Actions/RuleHelper.cs b/backend/extensions/Squidex.Extensions/Actions/RuleHelper.cs index d665e72f5..bbe2f31d6 100644 --- a/backend/extensions/Squidex.Extensions/Actions/RuleHelper.cs +++ b/backend/extensions/Squidex.Extensions/Actions/RuleHelper.cs @@ -21,7 +21,7 @@ public static class RuleHelper // Script vars are just wrappers over dictionaries for better performance. var vars = new EventScriptVars { - Event = @event + Event = @event, }; return scriptEngine.Evaluate(vars, expression); diff --git a/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRAction.cs b/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRAction.cs index 9ae964c60..d02568b4d 100644 --- a/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRAction.cs @@ -72,5 +72,5 @@ public enum ActionTypeEnum { Broadcast, User, - Group + Group, } diff --git a/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRActionHandler.cs index 7a4fe446b..85f3a0f69 100644 --- a/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/SignalR/SignalRActionHandler.cs @@ -54,7 +54,7 @@ public sealed class SignalRActionHandler(RuleEventFormatter formatter) : RuleAct HubName = hubName!, MethodName = action.MethodName, MethodPayload = requestBody!, - Targets = target.Split("\n") + Targets = target.Split("\n"), }; return (ruleText, ruleJob); diff --git a/backend/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs index 28cf31021..cb5059102 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs @@ -24,7 +24,7 @@ public sealed class SlackActionHandler(RuleEventFormatter formatter, IHttpClient var ruleJob = new SlackJob { RequestUrl = action.WebhookUrl.ToString(), - RequestBody = ToJson(body) + RequestBody = ToJson(body), }; return (Description, ruleJob); @@ -37,7 +37,7 @@ public sealed class SlackActionHandler(RuleEventFormatter formatter, IHttpClient var request = new HttpRequestMessage(HttpMethod.Post, job.RequestUrl) { - Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json") + Content = new StringContent(job.RequestBody, Encoding.UTF8, "application/json"), }; return await httpClient.OneWayRequestAsync(request, job.RequestBody, ct); diff --git a/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetActionHandler.cs index b09d29c23..eb272db31 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetActionHandler.cs @@ -26,7 +26,7 @@ public sealed class TweetActionHandler(RuleEventFormatter formatter, IOptions { - ["status"] = job.Text + ["status"] = job.Text, }; await tokens.Statuses.UpdateAsync(request, ct); diff --git a/backend/extensions/Squidex.Extensions/Actions/Typesense/TypesenseActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Typesense/TypesenseActionHandler.cs index fba703bd3..54d51e752 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Typesense/TypesenseActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Typesense/TypesenseActionHandler.cs @@ -41,7 +41,7 @@ public sealed class TypesenseActionHandler(RuleEventFormatter formatter, IHttpCl { ServerUrl = $"{action.Host.ToString().TrimEnd('/')}/collections/{indexName}/documents", ServerKey = action.ApiKey, - ContentId = contentId + ContentId = contentId, }; if (delete) @@ -75,8 +75,8 @@ public sealed class TypesenseActionHandler(RuleEventFormatter formatter, IHttpCl { More = new Dictionary { - ["error"] = $"Invalid JSON: {ex.Message}" - } + ["error"] = $"Invalid JSON: {ex.Message}", + }, }; } @@ -104,7 +104,7 @@ public sealed class TypesenseActionHandler(RuleEventFormatter formatter, IHttpCl { request = new HttpRequestMessage(HttpMethod.Post, $"{job.ServerUrl}?action=upsert") { - Content = new StringContent(job.Content, Encoding.UTF8, "application/json") + Content = new StringContent(job.Content, Encoding.UTF8, "application/json"), }; } else diff --git a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs index 166a59595..b28e58a9d 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs @@ -58,5 +58,5 @@ public enum WebhookMethod PUT, GET, DELETE, - PATCH + PATCH, } diff --git a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs index f1a88d735..325c1b4b9 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookActionHandler.cs @@ -44,7 +44,7 @@ public sealed class WebhookActionHandler(RuleEventFormatter formatter, IHttpClie RequestSignature = requestSignature, RequestBody = requestBody!, RequestBodyType = action.PayloadType, - Headers = await ParseHeadersAsync(action.Headers, @event) + Headers = await ParseHeadersAsync(action.Headers, @event), }; return (ruleText, ruleJob); diff --git a/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs b/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs index 475f181bb..91f089068 100644 --- a/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs +++ b/backend/extensions/Squidex.Extensions/Assets/Azure/AzureMetadataSource.cs @@ -25,13 +25,13 @@ public sealed class AzureMetadataSource : IAssetMetadataSource [ ' ', '_', - '-' + '-', ]; private readonly List features = [ VisualFeatureTypes.Categories, VisualFeatureTypes.Description, - VisualFeatureTypes.Color + VisualFeatureTypes.Color, ]; public int Order => int.MaxValue; @@ -41,7 +41,7 @@ public sealed class AzureMetadataSource : IAssetMetadataSource { client = new ComputerVisionClient(new ApiKeyServiceClientCredentials(options.Value.ApiKey)) { - Endpoint = options.Value.Endpoint + Endpoint = options.Value.Endpoint, }; this.log = log; diff --git a/backend/extensions/Squidex.Extensions/Samples/Middleware/DoubleLinkedContentMiddleware.cs b/backend/extensions/Squidex.Extensions/Samples/Middleware/DoubleLinkedContentMiddleware.cs index fe9147103..6d548585b 100644 --- a/backend/extensions/Squidex.Extensions/Samples/Middleware/DoubleLinkedContentMiddleware.cs +++ b/backend/extensions/Squidex.Extensions/Samples/Middleware/DoubleLinkedContentMiddleware.cs @@ -69,7 +69,7 @@ public sealed class DoubleLinkedContentMiddleware(IContentLoader contentLoader) // Add the reference to the new referenced content. data["referencing"] = new ContentFieldData { - ["iv"] = JsonValue.Array(content.Id) + ["iv"] = JsonValue.Array(content.Id), }; await UpdateReferencing(context, newReferenced, data, ct); @@ -90,7 +90,7 @@ public sealed class DoubleLinkedContentMiddleware(IContentLoader contentLoader) DoNotScript = true, DoNotValidate = true, Data = data, - ExpectedVersion = reference.Version + ExpectedVersion = reference.Version, }, ct); } diff --git a/backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs b/backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs index 9358c3877..9fa3561d1 100644 --- a/backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs +++ b/backend/extensions/Squidex.Extensions/Text/Azure/AzureIndexDefinition.cs @@ -15,7 +15,7 @@ public static class AzureIndexDefinition private static readonly Dictionary FieldAnalyzers = new (StringComparer.OrdinalIgnoreCase) { ["iv"] = ("iv", LexicalAnalyzerName.StandardLucene.ToString()), - ["zh"] = ("zh", LexicalAnalyzerName.ZhHansLucene.ToString()) + ["zh"] = ("zh", LexicalAnalyzerName.ZhHansLucene.ToString()), }; static AzureIndexDefinition() @@ -78,44 +78,44 @@ public static class AzureIndexDefinition { new SimpleField("docId", SearchFieldDataType.String) { - IsKey = true + IsKey = true, }, new SimpleField("appId", SearchFieldDataType.String) { - IsFilterable = true + IsFilterable = true, }, new SimpleField("appName", SearchFieldDataType.String) { - IsFilterable = false + IsFilterable = false, }, new SimpleField("contentId", SearchFieldDataType.String) { - IsFilterable = false + IsFilterable = false, }, new SimpleField("schemaId", SearchFieldDataType.String) { - IsFilterable = true + IsFilterable = true, }, new SimpleField("schemaName", SearchFieldDataType.String) { - IsFilterable = false + IsFilterable = false, }, new SimpleField("serveAll", SearchFieldDataType.Boolean) { - IsFilterable = true + IsFilterable = true, }, new SimpleField("servePublished", SearchFieldDataType.Boolean) { - IsFilterable = true + IsFilterable = true, }, new SimpleField("geoObject", SearchFieldDataType.GeographyPoint) { - IsFilterable = true + IsFilterable = true, }, new SimpleField("geoField", SearchFieldDataType.String) { - IsFilterable = true - } + IsFilterable = true, + }, }; foreach (var (field, analyzer) in FieldAnalyzers.Values) @@ -125,13 +125,13 @@ public static class AzureIndexDefinition { IsFilterable = false, IsFacetable = false, - AnalyzerName = analyzer + AnalyzerName = analyzer, }); } var index = new SearchIndex(indexName) { - Fields = fields + Fields = fields, }; return index; diff --git a/backend/extensions/Squidex.Extensions/Text/Azure/AzureTextIndex.cs b/backend/extensions/Squidex.Extensions/Text/Azure/AzureTextIndex.cs index 4efa49669..d3f7f55f7 100644 --- a/backend/extensions/Squidex.Extensions/Text/Azure/AzureTextIndex.cs +++ b/backend/extensions/Squidex.Extensions/Text/Azure/AzureTextIndex.cs @@ -144,7 +144,7 @@ public sealed class AzureTextIndex : IInitializable, ITextIndex { var searchOptions = new SearchOptions { - Filter = filter + Filter = filter, }; searchOptions.Select.Add("contentId"); diff --git a/backend/extensions/Squidex.Extensions/Text/Azure/CommandFactory.cs b/backend/extensions/Squidex.Extensions/Text/Azure/CommandFactory.cs index 6a5564556..431f89a58 100644 --- a/backend/extensions/Squidex.Extensions/Text/Azure/CommandFactory.cs +++ b/backend/extensions/Squidex.Extensions/Text/Azure/CommandFactory.cs @@ -48,8 +48,8 @@ public static class CommandFactory coordinates = new[] { point.Coordinate.X, - point.Coordinate.Y - } + point.Coordinate.Y, + }, }; break; } @@ -69,7 +69,7 @@ public static class CommandFactory ["serveAll"] = upsert.ServeAll, ["servePublished"] = upsert.ServePublished, ["geoField"] = geoField, - ["geoObject"] = geoObject + ["geoObject"] = geoObject, }; foreach (var (key, value) in upsert.Texts) @@ -96,7 +96,7 @@ public static class CommandFactory { ["docId"] = update.ToDocId(), ["serveAll"] = update.ServeAll, - ["servePublished"] = update.ServePublished + ["servePublished"] = update.ServePublished, }; batch.Add(IndexDocumentsAction.MergeOrUpload(document)); diff --git a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/CommandFactory.cs b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/CommandFactory.cs index 0a9cd9857..ff72d520e 100644 --- a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/CommandFactory.cs +++ b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/CommandFactory.cs @@ -43,7 +43,7 @@ public static class CommandFactory geoObject = new { lat = point.Coordinate.X, - lon = point.Coordinate.Y + lon = point.Coordinate.Y, }; break; } @@ -57,8 +57,8 @@ public static class CommandFactory index = new { _id = upsert.ToDocId(), - _index = indexName - } + _index = indexName, + }, }); var texts = new Dictionary(); @@ -91,7 +91,7 @@ public static class CommandFactory servePublished = upsert.ServePublished, texts, geoField, - geoObject + geoObject, }); } } @@ -103,8 +103,8 @@ public static class CommandFactory update = new { _id = update.ToDocId(), - _index = indexName - } + _index = indexName, + }, }); args.Add(new @@ -112,8 +112,8 @@ public static class CommandFactory doc = new { serveAll = update.ServeAll, - servePublished = update.ServePublished - } + servePublished = update.ServePublished, + }, }); } @@ -124,8 +124,8 @@ public static class CommandFactory delete = new { _id = delete.ToDocId(), - _index = indexName - } + _index = indexName, + }, }); } } diff --git a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchIndexDefinition.cs b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchIndexDefinition.cs index 5d50035de..d869e99f8 100644 --- a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchIndexDefinition.cs +++ b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchIndexDefinition.cs @@ -46,7 +46,7 @@ public static class ElasticSearchIndexDefinition ["es"] = "spanish", ["sv"] = "swedish", ["tr"] = "turkish", - ["th"] = "thai" + ["th"] = "thai", }; static ElasticSearchIndexDefinition() @@ -103,9 +103,9 @@ public static class ElasticSearchIndexDefinition { ["geoObject"] = new { - type = "geo_point" - } - } + type = "geo_point", + }, + }, }; foreach (var (key, analyzer) in FieldAnalyzers) @@ -113,7 +113,7 @@ public static class ElasticSearchIndexDefinition query.properties[GetFieldPath(key)] = new { type = "text", - analyzer + analyzer, }; } diff --git a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs index 8233a7da2..03a54bdb2 100644 --- a/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs +++ b/backend/extensions/Squidex.Extensions/Text/ElasticSearch/ElasticSearchTextIndex.cs @@ -71,22 +71,22 @@ public sealed partial class ElasticSearchTextIndex(IElasticSearchClient elasticC { term = new Dictionary { - ["schemaId.keyword"] = query.SchemaId.ToString() - } + ["schemaId.keyword"] = query.SchemaId.ToString(), + }, }, new { term = new Dictionary { - ["geoField.keyword"] = query.Field - } + ["geoField.keyword"] = query.Field, + }, }, new { term = new Dictionary { - [serveField] = "true" - } + [serveField] = "true", + }, }, new { @@ -95,19 +95,19 @@ public sealed partial class ElasticSearchTextIndex(IElasticSearchClient elasticC geoObject = new { lat = query.Latitude, - lon = query.Longitude + lon = query.Longitude, }, - distance = $"{query.Radius}m" - } - } - } - } + distance = $"{query.Radius}m", + }, + }, + }, + }, }, _source = new[] { - "contentId" + "contentId", }, - size = query.Take + size = query.Take, }; return await SearchAsync(elasticQuery, ct); @@ -140,32 +140,32 @@ public sealed partial class ElasticSearchTextIndex(IElasticSearchClient elasticC { term = new Dictionary { - ["appId.keyword"] = app.Id.ToString() - } + ["appId.keyword"] = app.Id.ToString(), + }, }, new { term = new Dictionary { - [serveField] = "true" - } - } + [serveField] = "true", + }, + }, }, must = new { query_string = new { - query = parsed.Text - } + query = parsed.Text, + }, }, - should = new List() - } + should = new List(), + }, }, _source = new[] { - "contentId" + "contentId", }, - size = query.Take + size = query.Take, }; if (query.RequiredSchemaIds?.Count > 0) @@ -174,8 +174,8 @@ public sealed partial class ElasticSearchTextIndex(IElasticSearchClient elasticC { terms = new Dictionary { - ["schemaId.keyword"] = query.RequiredSchemaIds.Select(x => x.ToString()).ToArray() - } + ["schemaId.keyword"] = query.RequiredSchemaIds.Select(x => x.ToString()).ToArray(), + }, }; elasticQuery.query.@bool.filter.Add(bySchema); @@ -186,8 +186,8 @@ public sealed partial class ElasticSearchTextIndex(IElasticSearchClient elasticC { terms = new Dictionary { - ["schemaId.keyword"] = query.PreferredSchemaId.ToString() - } + ["schemaId.keyword"] = query.PreferredSchemaId.ToString(), + }, }; elasticQuery.query.@bool.should.Add(bySchema); diff --git a/backend/src/Migrations/MigrationPath.cs b/backend/src/Migrations/MigrationPath.cs index 657e1c29e..3eb088fc4 100644 --- a/backend/src/Migrations/MigrationPath.cs +++ b/backend/src/Migrations/MigrationPath.cs @@ -7,10 +7,10 @@ using Microsoft.Extensions.DependencyInjection; using Migrations.Migrations; -using Migrations.Migrations.Backup; using Migrations.Migrations.MongoDb; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Migrations.Backup; namespace Migrations; @@ -35,25 +35,25 @@ public sealed class MigrationPath(IServiceProvider serviceProvider) : IMigration // Version 06: Convert Event store. Must always be executed first. if (version < 6) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 22: Integrate Domain Id. if (version < 22) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 07: Introduces AppId for backups. else if (version < 7) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 05: Fixes the broken command architecture and requires a rebuild of all snapshots. if (version < 5) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } else { @@ -68,9 +68,9 @@ public sealed class MigrationPath(IServiceProvider serviceProvider) : IMigration // Version 25: Introduce full deletion. if (version < 25) { - yield return serviceProvider.GetRequiredService(); - yield return serviceProvider.GetRequiredService(); - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); + yield return serviceProvider.GetService(); + yield return serviceProvider.GetService(); } // Version 18: Rebuild assets. @@ -91,7 +91,7 @@ public sealed class MigrationPath(IServiceProvider serviceProvider) : IMigration // Version 23: Fix parent id. if (version < 23) { - yield return serviceProvider.GetRequiredService().ForAssets(); + yield return serviceProvider.GetService()?.ForAssets(); } } @@ -99,32 +99,32 @@ public sealed class MigrationPath(IServiceProvider serviceProvider) : IMigration // Version 25: Convert content ids to names. if (version < 25) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 16: Introduce file name slugs for assets. if (version < 16) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } } // Version 13: Json refactoring. if (version < 13) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 26: New rule statistics using normal usage collection. if (version < 26) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } // Version 27: General jobs state. if (version < 27) { - yield return serviceProvider.GetRequiredService(); + yield return serviceProvider.GetService(); } } } diff --git a/backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs b/backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs index d8ed6c045..558ee6758 100644 --- a/backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs +++ b/backend/src/Migrations/Migrations/MongoDb/AddAppIdToEventStream.cs @@ -83,7 +83,7 @@ public sealed class AddAppIdToEventStream(IMongoDatabase database) : MongoBase(filter, document) { - IsUpsert = true + IsUpsert = true, }); } diff --git a/backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs b/backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs index e0f040b86..6d5826e68 100644 --- a/backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs +++ b/backend/src/Migrations/Migrations/MongoDb/ConvertDocumentIds.cs @@ -21,7 +21,7 @@ public sealed class ConvertDocumentIds(IMongoDatabase databaseDefault, IMongoDat { None, Assets, - Contents + Contents, } public override string ToString() @@ -109,7 +109,7 @@ public sealed class ConvertDocumentIds(IMongoDatabase databaseDefault, IMongoDat writes.Add(new ReplaceOneModel(filter, document) { - IsUpsert = true + IsUpsert = true, }); } diff --git a/backend/src/Migrations/Migrations/MongoDb/ConvertOldSnapshotStores.cs b/backend/src/Migrations/Migrations/MongoDb/ConvertOldSnapshotStores.cs index 3e66fea70..5f290b985 100644 --- a/backend/src/Migrations/Migrations/MongoDb/ConvertOldSnapshotStores.cs +++ b/backend/src/Migrations/Migrations/MongoDb/ConvertOldSnapshotStores.cs @@ -22,7 +22,7 @@ public sealed class ConvertOldSnapshotStores(IMongoDatabase database) : MongoBas { "States_Apps", "States_Rules", - "States_Schemas" + "States_Schemas", }.Select(x => database.GetCollection(x)); var update = Update.Rename("State", "Doc"); diff --git a/backend/src/Migrations/OldEvents/AppClientPermission.cs b/backend/src/Migrations/OldEvents/AppClientPermission.cs index 1332db787..108bee808 100644 --- a/backend/src/Migrations/OldEvents/AppClientPermission.cs +++ b/backend/src/Migrations/OldEvents/AppClientPermission.cs @@ -12,5 +12,5 @@ public enum AppClientPermission { Developer, Editor, - Reader + Reader, } diff --git a/backend/src/Migrations/OldEvents/AppContributorPermission.cs b/backend/src/Migrations/OldEvents/AppContributorPermission.cs index 748f0441d..cf85ada28 100644 --- a/backend/src/Migrations/OldEvents/AppContributorPermission.cs +++ b/backend/src/Migrations/OldEvents/AppContributorPermission.cs @@ -12,5 +12,5 @@ public enum AppContributorPermission { Owner, Developer, - Editor + Editor, } diff --git a/backend/src/Migrations/OldEvents/AppPatternAdded.cs b/backend/src/Migrations/OldEvents/AppPatternAdded.cs index c28fec8e3..7fc65c2c7 100644 --- a/backend/src/Migrations/OldEvents/AppPatternAdded.cs +++ b/backend/src/Migrations/OldEvents/AppPatternAdded.cs @@ -36,14 +36,14 @@ public sealed class AppPatternAdded : AppEvent, IMigratedStateEvent { new Pattern(Name, Pattern) { - Message = Message - } - }.ToReadonlyList() + Message = Message, + }, + }.ToReadonlyList(), }; var newEvent = new AppSettingsUpdated { - Settings = newSettings + Settings = newSettings, }; return SimpleMapper.Map(this, newEvent); diff --git a/backend/src/Migrations/OldEvents/AppPatternDeleted.cs b/backend/src/Migrations/OldEvents/AppPatternDeleted.cs index 1904a5f33..0419be232 100644 --- a/backend/src/Migrations/OldEvents/AppPatternDeleted.cs +++ b/backend/src/Migrations/OldEvents/AppPatternDeleted.cs @@ -25,7 +25,7 @@ public sealed class AppPatternDeleted : AppEvent, IMigratedStateEvent { var newEvent = new AppSettingsUpdated { - Settings = state.Settings + Settings = state.Settings, }; return SimpleMapper.Map(this, newEvent); diff --git a/backend/src/Migrations/OldEvents/AppPatternUpdated.cs b/backend/src/Migrations/OldEvents/AppPatternUpdated.cs index ca2be86c5..5933ad4d5 100644 --- a/backend/src/Migrations/OldEvents/AppPatternUpdated.cs +++ b/backend/src/Migrations/OldEvents/AppPatternUpdated.cs @@ -36,15 +36,15 @@ public sealed class AppPatternUpdated : AppEvent, IMigratedStateEvent { new Pattern(Name, Pattern) { - Message = Message - } + Message = Message, + }, }.ToReadonlyList(), - Editors = state.Settings.Editors + Editors = state.Settings.Editors, }; var newEvent = new AppSettingsUpdated { - Settings = newSettings + Settings = newSettings, }; return SimpleMapper.Map(this, newEvent); diff --git a/backend/src/Migrations/OldEvents/ContentChangesPublished.cs b/backend/src/Migrations/OldEvents/ContentChangesPublished.cs index 46283496f..264ab383c 100644 --- a/backend/src/Migrations/OldEvents/ContentChangesPublished.cs +++ b/backend/src/Migrations/OldEvents/ContentChangesPublished.cs @@ -23,7 +23,7 @@ public sealed class ContentChangesPublished : ContentEvent, IMigrated return SimpleMapper.Map(this, new ContentStatusChangedV2 { Status = Status.Published, - Change = StatusChange.Published + Change = StatusChange.Published, }); } } diff --git a/backend/src/Migrations/OldEvents/SchemaCreated.cs b/backend/src/Migrations/OldEvents/SchemaCreated.cs index 92a2279e3..fa2769da7 100644 --- a/backend/src/Migrations/OldEvents/SchemaCreated.cs +++ b/backend/src/Migrations/OldEvents/SchemaCreated.cs @@ -47,7 +47,7 @@ public sealed class SchemaCreated : SchemaEvent, IMigrated { IsLocked = eventField.IsLocked, IsHidden = eventField.IsHidden, - IsDisabled = eventField.IsDisabled + IsDisabled = eventField.IsDisabled, }; if (field is ArrayField arrayField && eventField.Nested?.Length > 0) @@ -62,7 +62,7 @@ public sealed class SchemaCreated : SchemaEvent, IMigrated { IsLocked = nestedEventField.IsLocked, IsHidden = nestedEventField.IsHidden, - IsDisabled = nestedEventField.IsDisabled + IsDisabled = nestedEventField.IsDisabled, }; arrayFields.Add(nestedField); @@ -82,7 +82,7 @@ public sealed class SchemaCreated : SchemaEvent, IMigrated SchemaType.Singleton : SchemaType.Default, IsPublished = Publish, - FieldCollection = FieldCollection.Create(fields.ToArray()) + FieldCollection = FieldCollection.Create(fields.ToArray()), }; return SimpleMapper.Map(this, new SchemaCreatedV2 { Schema = schema }); diff --git a/backend/src/Squidex.Data.EntityFramework/AppDbContext.cs b/backend/src/Squidex.Data.EntityFramework/AppDbContext.cs new file mode 100644 index 000000000..8f0eb629c --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/AppDbContext.cs @@ -0,0 +1,114 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Squidex.Assets.TusAdapter; +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Billing; +using Squidex.Domain.Apps.Entities.Contents.Counter; +using Squidex.Domain.Apps.Entities.Jobs; +using Squidex.Domain.Apps.Entities.Rules.UsageTracking; +using Squidex.Domain.Apps.Entities.Tags; +using Squidex.Domain.Users; +using Squidex.Infrastructure.EventSourcing.Consume; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; +using YDotNet.Server.EntityFramework; + +namespace Squidex; + +public class AppDbContext(DbContextOptions options, IJsonSerializer jsonSerializer) : IdentityDbContext(options) +{ + protected override void OnModelCreating(ModelBuilder builder) + { + var jsonColumnType = JsonColumnType(); + + builder.UseApps(jsonSerializer, jsonColumnType); + builder.UseAssetKeyValueStore(); + builder.UseAssets(jsonSerializer, jsonColumnType); + builder.UseCache(); + builder.UseCounters(jsonSerializer, jsonColumnType); + builder.UseChatStore(); + builder.UseContent(jsonSerializer, jsonColumnType); + builder.UseEvents(jsonSerializer, jsonColumnType); + builder.UseEventStore(); + builder.UseHistory(jsonSerializer, jsonColumnType); + builder.UseIdentity(jsonSerializer, jsonColumnType); + builder.UseJobs(jsonSerializer, jsonColumnType); + builder.UseMessagingDataStore(); + builder.UseMessagingTransport(); + builder.UseMigration(); + builder.UseNames(jsonSerializer, jsonColumnType); + builder.UseOpenIddict(); + builder.UseRequest(jsonSerializer, jsonColumnType); + builder.UseRules(jsonSerializer, jsonColumnType); + builder.UseSchema(jsonSerializer, jsonColumnType); + builder.UseSettings(jsonSerializer, jsonColumnType); + builder.UseTags(jsonSerializer, jsonColumnType); + builder.UseTeams(jsonSerializer, jsonColumnType); + builder.UseUsage(); + builder.UseUsageTracking(jsonSerializer, jsonColumnType); + builder.UseYDotNet(); + + base.OnModelCreating(builder); + } + + protected virtual string? JsonColumnType() + { + return null; + } +} + +#pragma warning disable MA0048 // File name must match type name +internal static class Extensions +#pragma warning restore MA0048 // File name must match type name +{ + public static void UseIdentity(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseUsageTracking(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + builder.UseSnapshot(jsonSerializer, jsonColumn); + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseCounters(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseEvents(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseNames(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseJobs(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseSettings(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } + + public static void UseTags(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppBuilder.cs new file mode 100644 index 000000000..9719ec03b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppBuilder.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFAppBuilder +{ + public static void UseApps(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn, b => + { + b.Property(x => x.IndexedTeamId).AsString(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppEntity.cs new file mode 100644 index 000000000..be9682ff3 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppEntity.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations.Schema; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Apps; + +public sealed class EFAppEntity : EFState +{ + [Column("Name")] + public string IndexedName { get; set; } + + [Column("UserIds")] + public string IndexedUserIds { get; set; } + + [Column("TeamId")] + public DomainId? IndexedTeamId { get; set; } + + [Column("Deleted")] + public bool IndexedDeleted { get; set; } + + [Column("Created")] + public DateTimeOffset IndexedCreated { get; set; } + + public override void Prepare() + { + var users = new HashSet + { + Document.CreatedBy.Identifier, + }; + + users.AddRange(Document.Contributors.Keys); + users.AddRange(Document.Clients.Keys); + + IndexedCreated = Document.Created.ToDateTimeOffset(); + IndexedDeleted = Document.IsDeleted; + IndexedName = Document.Name; + IndexedTeamId = Document.TeamId; + IndexedUserIds = TagsConverter.ToString(users); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppRepository.cs new file mode 100644 index 000000000..63b645c49 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Apps/EFAppRepository.cs @@ -0,0 +1,115 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities.Apps.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Apps; + +public sealed class EFAppRepository(IDbContextFactory dbContextFactory) + : EFSnapshotStore(dbContextFactory), IAppRepository where TContext : DbContext +{ + public async Task> QueryAllAsync(string contributorId, IEnumerable names, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAppRepository/QueryAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var formattedId = TagsConverter.FormatFilter(contributorId); + var entities = + await dbContext.Set() + .Where(x => x.IndexedUserIds.Contains(formattedId) || names.Contains(x.IndexedName)) + .Where(x => !x.IndexedDeleted) + .ToListAsync(ct); + + return RemoveDuplicateNames(entities); + } + } + + public async Task> QueryAllAsync(DomainId teamId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAppRepository/QueryAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set() + .Where(x => x.IndexedTeamId == teamId) + .Where(x => !x.IndexedDeleted) + .ToListAsync(ct); + + return RemoveDuplicateNames(entities); + } + } + + public async Task FindAsync(DomainId id, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAppRepository/FindAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.DocumentId == id) + .Where(x => !x.IndexedDeleted) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + public async Task FindAsync(string name, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAppRepository/FindAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.IndexedName == name) + .Where(x => !x.IndexedDeleted) + .OrderByDescending(x => x.IndexedCreated) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + private static List RemoveDuplicateNames(List entities) + { + var byName = new Dictionary(); + + // Remove duplicate names, the latest wins. + foreach (var entity in entities.OrderBy(x => x.IndexedCreated)) + { + byName[entity.IndexedName] = entity.Document; + } + + return byName.Values.ToList(); + } + + protected override Expression, SetPropertyCalls>> BuildUpdate(EFAppEntity entity) + { + return u => u + .SetProperty(x => x.Document, entity.Document) + .SetProperty(x => x.IndexedCreated, entity.IndexedCreated) + .SetProperty(x => x.IndexedDeleted, entity.IndexedDeleted) + .SetProperty(x => x.IndexedName, entity.IndexedName) + .SetProperty(x => x.IndexedTeamId, entity.IndexedTeamId) + .SetProperty(x => x.IndexedUserIds, entity.IndexedUserIds) + .SetProperty(x => x.Version, entity.Version); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/AssetSqlQueryBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/AssetSqlQueryBuilder.cs new file mode 100644 index 000000000..b2d8edf82 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/AssetSqlQueryBuilder.cs @@ -0,0 +1,64 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; +using Squidex.Infrastructure.Queries; +using Squidex.Text; + +namespace Squidex.Domain.Apps.Entities.Assets; + +internal sealed class AssetSqlQueryBuilder(SqlDialect dialect) : SqlQueryBuilder(dialect, "Assets") +{ + public override string Visit(CompareFilter nodeIn, None args) + { + if (!IsTagsField(nodeIn.Path)) + { + return base.Visit(nodeIn, args); + } + + switch (nodeIn.Operator) + { + case CompareOperator.Equals when nodeIn.Value.Value is string value: + return Visit(ClrFilter.Contains(nodeIn.Path, TagsConverter.FormatFilter(value)), args); + case CompareOperator.NotEquals when nodeIn.Value.Value is string value: + return Visit( + ClrFilter.Not( + ClrFilter.Contains(nodeIn.Path, TagsConverter.FormatFilter(value)) + ), + args); + case CompareOperator.In when nodeIn.Value.Value is List values: + return Visit( + ClrFilter.Or( + values.Select(v => + ClrFilter.Contains(nodeIn.Path, TagsConverter.FormatFilter(v)) + ).ToArray() + ), + args); + } + + return base.Visit(nodeIn, args); + } + + public override PropertyPath Visit(PropertyPath path) + { + var elements = path.ToList(); + + elements[0] = elements[0].ToPascalCase(); + + return new PropertyPath(elements); + } + + public override bool IsJsonPath(PropertyPath path) + { + return path.Count > 1 && string.Equals(path[0], "metadata", StringComparison.OrdinalIgnoreCase); + } + + private static bool IsTagsField(PropertyPath path) + { + return path.Count == 1 && string.Equals(path[0], "tags", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetBuilder.cs new file mode 100644 index 000000000..7c7ac294a --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetBuilder.cs @@ -0,0 +1,47 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFAssetBuilder +{ + public static void UseAssets(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.Entity(b => + { + b.Property(x => x.Id).AsString(); + b.Property(x => x.AppId).AsString(); + b.Property(x => x.Created).AsDateTimeOffset(); + b.Property(x => x.CreatedBy).AsString(); + b.Property(x => x.DocumentId).AsString(); + b.Property(x => x.IndexedAppId).AsString(); + b.Property(x => x.LastModified).AsDateTimeOffset(); + b.Property(x => x.LastModifiedBy).AsString(); + b.Property(x => x.Metadata).AsJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.ParentId).AsString(); + b.Property(x => x.Tags).AsString(); + b.Property(x => x.Type).AsString(); + }); + + builder.Entity(b => + { + b.Property(x => x.Id).AsString(); + b.Property(x => x.AppId).AsString(); + b.Property(x => x.Created).AsDateTimeOffset(); + b.Property(x => x.CreatedBy).AsString(); + b.Property(x => x.DocumentId).AsString(); + b.Property(x => x.IndexedAppId).AsString(); + b.Property(x => x.LastModified).AsDateTimeOffset(); + b.Property(x => x.LastModifiedBy).AsString(); + b.Property(x => x.ParentId).AsString(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetEntity.cs new file mode 100644 index 000000000..b0f333138 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetEntity.cs @@ -0,0 +1,40 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Assets; + +[Table("Assets")] +[Index(nameof(IndexedAppId), nameof(Id))] +public record EFAssetEntity : Asset, IVersionedEntity +{ + [Key] + public DomainId DocumentId { get; set; } + + public DomainId IndexedAppId { get; set; } + + public static EFAssetEntity Create(SnapshotWriteJob job) + { + var entity = new EFAssetEntity + { + DocumentId = job.Key, + // Both version and ID cannot be changed by the mapper method anymore. + Version = job.NewVersion, + // Use an app ID without the name to reduce the memory usage of the index. + IndexedAppId = job.Value.AppId.Id, + }; + + return SimpleMapper.Map(job.Value, entity); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderEntity.cs new file mode 100644 index 000000000..d014b99e3 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderEntity.cs @@ -0,0 +1,40 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Assets; + +[Table("AssetFolders")] +[Index(nameof(IndexedAppId), nameof(Id))] +public sealed record EFAssetFolderEntity : AssetFolder, IVersionedEntity +{ + [Key] + public DomainId DocumentId { get; set; } + + public DomainId IndexedAppId { get; set; } + + public static EFAssetFolderEntity Create(SnapshotWriteJob job) + { + var entity = new EFAssetFolderEntity + { + DocumentId = job.Key, + // Both version and ID cannot be changed by the mapper method anymore. + Version = job.NewVersion, + // Use an app ID without the name to reduce the memory usage of the index. + IndexedAppId = job.Value.AppId.Id, + }; + + return SimpleMapper.Map(job.Value, entity); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository.cs new file mode 100644 index 000000000..dc47962eb --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository.cs @@ -0,0 +1,70 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Assets; + +public sealed partial class EFAssetFolderRepository(IDbContextFactory dbContextFactory) + : IAssetFolderRepository where TContext : DbContext +{ + public async Task> QueryAsync(DomainId appId, DomainId? parentId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/QueryAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetFolderEntities = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .WhereIf(x => x.ParentId == parentId!.Value, parentId.HasValue) + .ToListAsync(ct); + + return ResultList.Create(assetFolderEntities.Count, assetFolderEntities); + } + } + + public async Task> QueryChildIdsAsync(DomainId appId, DomainId? parentId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/QueryChildIdsAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetFolderIds = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .WhereIf(x => x.ParentId == parentId!.Value, parentId.HasValue) + .Select(x => x.Id) + .ToListAsync(ct); + + return assetFolderIds; + } + } + + public async Task FindAssetFolderAsync(DomainId appId, DomainId id, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/FindAssetFolderAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var documentId = DomainId.Combine(appId, id); + var assetFolderEntity = + await dbContext.Set() + .Where(x => x.DocumentId == documentId) + .Where(x => !x.IsDeleted) + .FirstOrDefaultAsync(ct); + + return assetFolderEntity; + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository_SnapshotStore.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository_SnapshotStore.cs new file mode 100644 index 000000000..abe38eeb6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetFolderRepository_SnapshotStore.cs @@ -0,0 +1,130 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Assets; + +public sealed partial class EFAssetFolderRepository : ISnapshotStore, IDeleter +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + } + + async Task ISnapshotStore.ClearAsync( + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + } + + async Task ISnapshotStore.RemoveAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/RemoveAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.DocumentId == key) + .ExecuteDeleteAsync(ct); + } + } + + async IAsyncEnumerable> ISnapshotStore.ReadAllAsync( + [EnumeratorCancellation] CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = dbContext.Set().ToAsyncEnumerable(); + + await foreach (var entity in entities.WithCancellation(ct)) + { + yield return new SnapshotResult(entity.DocumentId, entity, entity.Version); + } + } + + async Task> ISnapshotStore.ReadAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/ReadAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = await dbContext.Set().Where(x => x.DocumentId == key).FirstOrDefaultAsync(ct); + if (entity == null) + { + return new SnapshotResult(default, default!, EtagVersion.Empty); + } + + return new SnapshotResult(entity.DocumentId, entity, entity.Version); + } + } + + async Task ISnapshotStore.WriteAsync(SnapshotWriteJob job, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/WriteAsync")) + { + var entity = EFAssetFolderEntity.Create(job); + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.UpsertAsync(entity, job.OldVersion, BuildUpdate, ct); + } + } + + async Task ISnapshotStore.WriteManyAsync(IEnumerable> jobs, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetFolderRepository/WriteManyAsync")) + { + var entities = jobs.Select(EFAssetFolderEntity.Create).ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkInsertAsync(entities, cancellationToken: ct); + } + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } + + private static Expression, SetPropertyCalls>> BuildUpdate(EFAssetFolderEntity entity) + { + return b => b + .SetProperty(x => x.AppId, entity.AppId) + .SetProperty(x => x.Created, entity.Created) + .SetProperty(x => x.CreatedBy, entity.CreatedBy) + .SetProperty(x => x.FolderName, entity.FolderName) + .SetProperty(x => x.IsDeleted, entity.IsDeleted) + .SetProperty(x => x.LastModified, entity.LastModified) + .SetProperty(x => x.LastModifiedBy, entity.LastModifiedBy) + .SetProperty(x => x.ParentId, entity.ParentId) + .SetProperty(x => x.Version, entity.Version); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository.cs new file mode 100644 index 000000000..c767b2fc0 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository.cs @@ -0,0 +1,189 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Domain.Apps.Entities.Assets; + +public sealed partial class EFAssetRepository(IDbContextFactory dbContextFactory, SqlDialect dialect) + : IAssetRepository where TContext : DbContext +{ + public async IAsyncEnumerable StreamAll(DomainId appId, + [EnumeratorCancellation] CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => !x.IsDeleted) + .ToAsyncEnumerable(); + + await foreach (var entity in entities.WithCancellation(ct)) + { + yield return entity; + } + } + + public async Task> QueryAsync(DomainId appId, DomainId? parentId, Q q, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/QueryAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + if (q.Ids is { Count: > 0 }) + { + var result = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => q.Ids.Contains(x.Id)) + .Where(x => !x.IsDeleted) + .QueryAsync(q, ct); + + return result; + } + + var sqlQuery = + new AssetSqlQueryBuilder(dialect) + .Where(ClrFilter.Eq(nameof(EFAssetEntity.IndexedAppId), appId)); + + if (q.Query.Filter?.HasField("IsDeleted") != true) + { + sqlQuery.Where(ClrFilter.Eq(nameof(EFAssetEntity.IsDeleted), false)); + } + + if (parentId != null) + { + sqlQuery.Where(ClrFilter.Eq(nameof(EFAssetEntity.ParentId), parentId)); + } + + sqlQuery.Where(q.Query); + + return await dbContext.QueryAsync(sqlQuery, q, ct); + } + } + + public async Task> QueryIdsAsync(DomainId appId, HashSet ids, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/QueryIdsAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetIds = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => ids.Contains(x.Id)) + .Where(x => !x.IsDeleted) + .Select(x => x.Id) + .ToListAsync(ct); + + return assetIds; + } + } + + public async Task> QueryChildIdsAsync(DomainId appId, DomainId parentId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/QueryChildIdsAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetIds = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => x.ParentId == parentId) + .Where(x => !x.IsDeleted) + .Select(x => x.Id) + .ToListAsync(ct); + + return assetIds; + } + } + + public async Task FindAssetByHashAsync(DomainId appId, string hash, string fileName, long fileSize, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/FindAssetByHashAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetEntity = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => x.FileHash == hash && x.FileName == fileName && x.FileSize == fileSize) + .Where(x => !x.IsDeleted) + .FirstOrDefaultAsync(ct); + + return assetEntity; + } + } + + public async Task FindAssetBySlugAsync(DomainId appId, string slug, bool allowDeleted, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/FindAssetBySlugAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetEntity = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId && x.Slug == slug) + .WhereIf(x => !x.IsDeleted, !allowDeleted) + .FirstOrDefaultAsync(ct); + + return assetEntity; + } + } + + public async Task FindAssetAsync(DomainId appId, DomainId id, bool allowDeleted, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/FindAssetAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var docId = DomainId.Combine(appId, id); + + var assetEntity = + await dbContext.Set() + .Where(x => x.DocumentId == docId) + .WhereIf(x => !x.IsDeleted, !allowDeleted) + .FirstOrDefaultAsync(ct); + + return assetEntity; + } + } + + public async Task FindAssetAsync(DomainId id, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/FindAssetAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var assetEntity = + await dbContext.Set() + .Where(x => x.Id == id) + .Where(x => !x.IsDeleted) + .FirstOrDefaultAsync(ct); + + return assetEntity; + } + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository_SnapshotStore.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository_SnapshotStore.cs new file mode 100644 index 000000000..394a7d0e1 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Assets/EFAssetRepository_SnapshotStore.cs @@ -0,0 +1,135 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Assets; + +public sealed partial class EFAssetRepository : ISnapshotStore, IDeleter +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + } + + async Task ISnapshotStore.ClearAsync( + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + } + + async Task ISnapshotStore.RemoveAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/RemoveAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.DocumentId == key) + .ExecuteDeleteAsync(ct); + } + } + + async IAsyncEnumerable> ISnapshotStore.ReadAllAsync( + [EnumeratorCancellation] CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = dbContext.Set().ToAsyncEnumerable(); + + await foreach (var entity in entities.WithCancellation(ct)) + { + yield return new SnapshotResult(entity.DocumentId, entity, entity.Version); + } + } + + async Task> ISnapshotStore.ReadAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/ReadAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = await dbContext.Set().Where(x => x.DocumentId == key).FirstOrDefaultAsync(ct); + if (entity == null) + { + return new SnapshotResult(default, default!, EtagVersion.Empty); + } + + return new SnapshotResult(entity.DocumentId, entity, entity.Version); + } + } + + async Task ISnapshotStore.WriteAsync(SnapshotWriteJob job, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/WriteAsync")) + { + var entity = EFAssetEntity.Create(job); + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.UpsertAsync(entity, job.OldVersion, BuildUpdate, ct); + } + } + + async Task ISnapshotStore.WriteManyAsync(IEnumerable> jobs, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFAssetRepository/WriteManyAsync")) + { + var entities = jobs.Select(EFAssetEntity.Create).ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkInsertAsync(entities, cancellationToken: ct); + } + } + + private static Expression, SetPropertyCalls>> BuildUpdate(EFAssetEntity entity) + { + return b => b + .SetProperty(x => x.AppId, entity.AppId) + .SetProperty(x => x.Created, entity.Created) + .SetProperty(x => x.CreatedBy, entity.CreatedBy) + .SetProperty(x => x.FileHash, entity.FileHash) + .SetProperty(x => x.FileName, entity.FileName) + .SetProperty(x => x.FileSize, entity.FileSize) + .SetProperty(x => x.FileVersion, entity.FileVersion) + .SetProperty(x => x.IsDeleted, entity.IsDeleted) + .SetProperty(x => x.IsProtected, entity.IsProtected) + .SetProperty(x => x.LastModified, entity.LastModified) + .SetProperty(x => x.LastModifiedBy, entity.LastModifiedBy) + .SetProperty(x => x.Metadata, entity.Metadata) + .SetProperty(x => x.MimeType, entity.MimeType) + .SetProperty(x => x.ParentId, entity.ParentId) + .SetProperty(x => x.Slug, entity.Slug) + .SetProperty(x => x.Tags, entity.Tags) + .SetProperty(x => x.TotalSize, entity.TotalSize) + .SetProperty(x => x.Type, entity.Type) + .SetProperty(x => x.Version, entity.Version); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentQueryBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentQueryBuilder.cs new file mode 100644 index 000000000..c98d04218 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/ContentQueryBuilder.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Queries; +using Squidex.Text; + +namespace Squidex.Domain.Apps.Entities.Contents; + +public class ContentQueryBuilder(SqlDialect dialect, string table, SqlParams? parameters = null) : SqlQueryBuilder(dialect, table, parameters) +{ + public override PropertyPath Visit(PropertyPath path) + { + var elements = path.ToList(); + + elements[0] = elements[0].ToPascalCase(); + + return new PropertyPath(elements); + } + + public override bool IsJsonPath(PropertyPath path) + { + return path.Count > 1 && string.Equals(path[0], "data", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentBuilder.cs new file mode 100644 index 000000000..da9bb7860 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentBuilder.cs @@ -0,0 +1,73 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Text.State; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFContentBuilder +{ + public static void UseContent(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.Entity(b => + { + b.ToTable("TextState"); + b.HasKey(x => x.UniqueContentId); + b.Property(x => x.UniqueContentId).AsString(); + b.Property(x => x.State).AsString(); + }); + + builder.UseContentEntity("ContentsAll", jsonSerializer, jsonColumn); + builder.UseContentReference("ContentReferencesAll"); + + builder.UseContentEntity("ContentsPublished", jsonSerializer, jsonColumn); + builder.UseContentReference("ContentReferencesPublished"); + } + + private static void UseContentEntity(this ModelBuilder builder, string tableName, IJsonSerializer jsonSerializer, string? jsonColumn) + where T : EFContentEntity + { + builder.Entity(b => + { + b.ToTable(tableName); + b.Property(x => x.Id).AsString(); + b.Property(x => x.AppId).AsString(); + b.Property(x => x.Created).AsDateTimeOffset(); + b.Property(x => x.CreatedBy).AsString(); + b.Property(x => x.Data).AsJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.DocumentId).AsString(); + b.Property(x => x.IndexedAppId).AsString(); + b.Property(x => x.IndexedSchemaId).AsString(); + b.Property(x => x.LastModified).AsDateTimeOffset(); + b.Property(x => x.LastModifiedBy).AsString(); + b.Property(x => x.NewData).AsNullableJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.NewStatus).AsNullableString(); + b.Property(x => x.SchemaId).AsString(); + b.Property(x => x.ScheduledAt).AsDateTimeOffset(); + b.Property(x => x.ScheduleJob).AsNullableJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.Status).AsString(); + b.Property(x => x.TranslationStatus).AsNullableJsonString(jsonSerializer, jsonColumn); + }); + } + + private static void UseContentReference(this ModelBuilder builder, string tableName) + where T : EFReferenceEntity + { + builder.Entity(b => + { + b.ToTable(tableName); + b.HasKey("AppId", "FromKey", "ToId"); + + b.Property(x => x.AppId).AsString(); + b.Property(x => x.FromKey).AsString(); + b.Property(x => x.ToId).AsString(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentEntity.cs new file mode 100644 index 000000000..f2a669cde --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentEntity.cs @@ -0,0 +1,186 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.ExtractReferenceIds; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Contents; + +public record EFContentCompleteEntity : EFContentEntity +{ + public static async Task<(EFContentCompleteEntity, EFReferenceCompleteEntity[])> CreateAsync( + SnapshotWriteJob job, + IAppProvider appProvider, + CancellationToken ct) + { + var source = job.Value; + + var appId = source.AppId.Id; + var (referencedIds, translationStatus) = await CreateExtendedValuesAsync(source, source.CurrentVersion.Data, appProvider, ct); + var references = + referencedIds + .Select(x => new EFReferenceCompleteEntity { AppId = appId, FromKey = job.Key, ToId = x }) + .ToArray(); + + var entity = new EFContentCompleteEntity + { + Id = source.Id, + AppId = source.AppId, + Created = source.Created, + CreatedBy = source.CreatedBy, + Data = source.EditingData, + DocumentId = job.Key, + IndexedAppId = source.AppId.Id, + IndexedSchemaId = source.SchemaId.Id, + IsDeleted = source.IsDeleted, + LastModified = source.LastModified, + LastModifiedBy = source.LastModifiedBy, + NewData = source.NewVersion != null ? source.CurrentVersion.Data : null, + NewStatus = source.NewVersion?.Status, + ScheduledAt = source.ScheduleJob?.DueTime, + ScheduleJob = source.ScheduleJob, + SchemaId = source.SchemaId, + Status = source.CurrentVersion.Status, + TranslationStatus = translationStatus, + Version = source.Version, + }; + + return (entity, references); + } +} + +public record EFContentPublishedEntity : EFContentEntity +{ + public static async Task<(EFContentPublishedEntity, EFReferencePublishedEntity[])> CreateAsync( + SnapshotWriteJob job, + IAppProvider appProvider, + CancellationToken ct) + { + var source = job.Value; + + var appId = source.AppId.Id; + var (referencedIds, translationStatus) = await CreateExtendedValuesAsync(source, source.CurrentVersion.Data, appProvider, ct); + var references = + referencedIds + .Select(x => new EFReferencePublishedEntity { AppId = appId, FromKey = job.Key, ToId = x }) + .ToArray(); + + var entity = new EFContentPublishedEntity + { + Id = source.Id, + AppId = source.AppId, + Created = source.Created, + CreatedBy = source.CreatedBy, + Data = source.CurrentVersion.Data, + DocumentId = job.Key, + IndexedAppId = appId, + IndexedSchemaId = source.SchemaId.Id, + IsDeleted = source.IsDeleted, + LastModified = source.LastModified, + LastModifiedBy = source.LastModifiedBy, + NewData = null, + NewStatus = null, + ScheduledAt = null, + ScheduleJob = null, + SchemaId = source.SchemaId, + Status = source.CurrentVersion.Status, + TranslationStatus = translationStatus, + Version = source.Version, + }; + + return (entity, references); + } +} + +public record EFContentEntity : Content, IVersionedEntity +{ + [Key] + public DomainId DocumentId { get; set; } + + public DomainId IndexedAppId { get; set; } + + public DomainId IndexedSchemaId { get; set; } + + public Instant? ScheduledAt { get; set; } + + public ContentData? NewData { get; set; } + + public TranslationStatus? TranslationStatus { get; set; } + + public WriteContent ToState() + { + if (NewData != null && NewStatus.HasValue) + { + return new WriteContent + { + Id = Id, + AppId = AppId, + Created = Created, + CreatedBy = CreatedBy, + CurrentVersion = new ContentVersion(Status, NewData), + IsDeleted = IsDeleted, + LastModified = LastModified, + LastModifiedBy = LastModifiedBy, + NewVersion = new ContentVersion(NewStatus.Value, Data), + ScheduleJob = ScheduleJob, + SchemaId = SchemaId, + Version = Version, + }; + } + else + { + return new WriteContent + { + Id = Id, + AppId = AppId, + Created = Created, + CreatedBy = CreatedBy, + CurrentVersion = new ContentVersion(Status, Data), + IsDeleted = IsDeleted, + LastModified = LastModified, + LastModifiedBy = LastModifiedBy, + NewVersion = null, + ScheduleJob = ScheduleJob, + SchemaId = SchemaId, + Version = Version, + }; + } + } + + protected static async Task<(HashSet, TranslationStatus?)> CreateExtendedValuesAsync( + WriteContent content, + ContentData data, + IAppProvider appProvider, + CancellationToken ct) + { + var referencedIds = new HashSet(); + + var (app, schema) = await appProvider.GetAppWithSchemaAsync(content.AppId.Id, content.SchemaId.Id, true, ct); + + if (app == null || schema == null) + { + return (referencedIds, null); + } + + if (data.CanHaveReference()) + { + var components = await appProvider.GetComponentsAsync(schema, ct: ct); + + data.AddReferencedIds(schema, referencedIds, components); + } + + var translationStatus = TranslationStatus.Create(data, schema, app.Languages); + + return (referencedIds, translationStatus); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository.cs new file mode 100644 index 000000000..a999c10e0 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository.cs @@ -0,0 +1,147 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Queries; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Contents; + +public sealed partial class EFContentRepository( + IDbContextFactory dbContextFactory, IAppProvider appProvider, + SqlDialect dialect) + : IContentRepository where TContext : DbContext +{ + public async Task FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/FindContentAsync")) + { + return scope == SearchScope.All ? + await FindContentAsync(app.Id, schema.Id, id, ct) : + await FindContentAsync(app.Id, schema.Id, id, ct); + } + } + + public async Task FindContentAsync(DomainId appId, DomainId schemaId, DomainId id, + CancellationToken ct = default) where T : EFContentEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.DocumentId == DomainId.Combine(appId, id)) + .Where(x => x.IndexedSchemaId == schemaId) + .FirstOrDefaultAsync(ct); + + return entity; + } + + public async Task> QueryIdsAsync(App app, HashSet ids, SearchScope scope, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/QueryIdsAsync")) + { + return scope == SearchScope.All ? + await QueryIdsAsync(app.Id, ids, ct) : + await QueryIdsAsync(app.Id, ids, ct); + } + } + + private async Task> QueryIdsAsync(DomainId appId, HashSet ids, + CancellationToken ct = default) where T : EFContentEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => ids.Contains(x.Id)) + .Select(x => new { SchemaId = x.IndexedSchemaId, x.Id, x.Status }) + .ToListAsync(ct); + + return entities.Select(x => new ContentIdStatus(x.SchemaId, x.Id, x.Status)).ToList(); + } + + public async Task HasReferrersAsync(App app, DomainId reference, SearchScope scope, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/HasReferrersAsync")) + { + return scope == SearchScope.All ? + await HasReferrersAsync(app.Id, reference, ct) : + await HasReferrersAsync(app.Id, reference, ct); + } + } + + public async Task HasReferrersAsync(DomainId appId, DomainId reference, + CancellationToken ct = default) where TReference : EFReferenceEntity + { + using (Telemetry.Activities.StartActivity("EFContentRepository/QueryIdsAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var result = + await dbContext.Set() + .Where(x => x.AppId == appId) + .Where(x => x.ToId == reference) + .AnyAsync(ct); + + return result; + } + } + + public Task ResetScheduledAsync(DomainId appId, DomainId id, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + ResetScheduledAsync(appId, id, ct) : + ResetScheduledAsync(appId, id, ct); + } + + public async Task ResetScheduledAsync(DomainId appId, DomainId id, + CancellationToken ct = default) where T : EFContentEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.DocumentId == DomainId.Combine(appId, id)) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.ScheduledAt, (Instant?)null) + .SetProperty(x => x.ScheduleJob, (ScheduleJob?)null), + ct); + } + + public Task CreateIndexAsync(DomainId appId, DomainId schemaId, IndexDefinition index, + CancellationToken ct = default) + { + return Task.CompletedTask; + } + + public Task> GetIndexesAsync(DomainId appId, DomainId schemaId, + CancellationToken ct = default) + { + return Task.FromResult>([]); + } + + public Task DropIndexAsync(DomainId appId, DomainId schemaId, string name, + CancellationToken ct = default) + { + return Task.CompletedTask; + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Dynamic.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Dynamic.cs new file mode 100644 index 000000000..2c947f7fe --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Dynamic.cs @@ -0,0 +1,204 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Queries; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Contents; + +public sealed partial class EFContentRepository +{ + public Task> QueryAsync(App app, Schema schema, Q q, SearchScope scope, + CancellationToken ct = default) + { + return QueryAsync(app, [schema], true, q, scope, ct); + } + + public Task> QueryAsync(App app, List schemas, Q q, SearchScope scope, + CancellationToken ct = default) + { + return QueryAsync(app, schemas, false, q, scope, ct); + } + + private async Task> QueryAsync(App app, List schemas, bool isSingle, Q q, SearchScope scope, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/QueryAsync")) + { + var schemaIds = schemas.Select(x => x.Id).ToList(); + + return scope == SearchScope.All ? + await QueryAsync( + app.Id, + schemaIds, + isSingle, + q, + "ContentsAll", + "ContentReferencesAll", + ct) : + await QueryAsync( + app.Id, + schemaIds, + isSingle, + q, + "ContentsPublished", + "ContentReferencesPublished", + ct); + } + } + + private async Task> QueryAsync( + DomainId appId, + List schemaIds, + bool isSingle, + Q q, + string tableName, + string referenceTableName, + CancellationToken ct = default) where T : EFContentEntity where TReference : EFReferenceEntity + { + if (q.Ids is { Count: > 0 } && schemaIds.Count > 0) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var result = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => schemaIds.Contains(x.IndexedSchemaId)) + .Where(x => q.Ids.Contains(x.Id)) + .Where(x => !x.IsDeleted) + .QueryAsync(q, ct); + + return result; + } + + if (q.ScheduledFrom != null && q.ScheduledTo != null && schemaIds.Count > 0) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var result = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => schemaIds.Contains(x.IndexedSchemaId)) + .Where(x => x.ScheduledAt >= q.ScheduledFrom && x.ScheduledAt <= q.ScheduledTo) + .Where(x => !x.IsDeleted) + .QueryAsync(q, ct); + + return result; + } + + if (q.Referencing != default && schemaIds.Count > 0) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var queryBuilder = + new ContentQueryBuilder(dialect, tableName) + .Where(ClrFilter.In(nameof(EFContentEntity.IndexedAppId), appId)) + .Where(ClrFilter.In(nameof(EFContentEntity.IndexedSchemaId), schemaIds)) + .WhereQuery(nameof(EFContentEntity.Id), CompareOperator.In, (p, d) => + new ContentQueryBuilder(d, referenceTableName, p) + .Where(ClrFilter.Eq(nameof(EFReferenceEntity.AppId), appId)) + .Where(ClrFilter.Eq(nameof(EFReferenceEntity.FromKey), DomainId.Combine(appId, q.Referencing))) + .Select(nameof(EFReferenceEntity.ToId)) + ) + .WhereNotDeleted(q.Query); + + return await QueryAsync(dbContext, queryBuilder, q, ct); + } + + if (q.Reference != default && schemaIds.Count > 0) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var queryBuilder = + new ContentQueryBuilder(dialect, tableName) + .WhereQuery(nameof(EFContentEntity.DocumentId), CompareOperator.In, (p, d) => + new ContentQueryBuilder(d, referenceTableName, p) + .Where(ClrFilter.Eq(nameof(EFReferenceEntity.AppId), appId)) + .Where(ClrFilter.Eq(nameof(EFReferenceEntity.ToId), q.Reference)) + .Select(nameof(EFReferenceEntity.FromKey)) + ) + .Where(ClrFilter.In(nameof(EFContentEntity.IndexedSchemaId), schemaIds)) + .WhereNotDeleted(q.Query); + + if (q.Query.Filter?.HasField("IsDeleted") != true) + { + queryBuilder.Where(ClrFilter.Eq(nameof(EFContentEntity.IsDeleted), false)); + } + + return await QueryAsync(dbContext, queryBuilder, q, ct); + } + + if (isSingle) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var queryBuilder = + new ContentQueryBuilder(dialect, tableName) + .Where(ClrFilter.Eq(nameof(EFContentEntity.IndexedAppId), appId)) + .Where(ClrFilter.Eq(nameof(EFContentEntity.IndexedSchemaId), schemaIds.Single())) + .WhereNotDeleted(q.Query); + + return await QueryAsync(dbContext, queryBuilder, q, ct); + } + + return ResultList.Empty(); + } + + private static async Task> QueryAsync(TContext dbContext, SqlQueryBuilder queryBuilder, Q q, + CancellationToken ct) where T : EFContentEntity + { + var result = await dbContext.QueryAsync(queryBuilder, q, ct); + if (result.Count > 0 && q.Fields is { Count: > 0 }) + { + foreach (var content in result) + { + content.Data.LimitFields(q.Fields); + } + } + + return result; + } + + public Task> QueryIdsAsync(App app, Schema schema, FilterNode filterNode, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + QueryIdsAsync(app.Id, schema.Id, filterNode, + "ContentsAll", ct) : + QueryIdsAsync(app.Id, schema.Id, filterNode, + "ContentsPublished", ct); + } + + private async Task> QueryIdsAsync(DomainId appId, DomainId schemaId, FilterNode filterNode, string table, + CancellationToken ct = default) where T : EFContentEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + var (sql, parameters) = + new ContentQueryBuilder(dialect, table) + .Where(ClrFilter.Eq(nameof(EFContentEntity.IndexedAppId), appId)) + .Where(ClrFilter.Eq(nameof(EFContentEntity.IndexedSchemaId), schemaId)) + .WhereNotDeleted(filterNode) + .Where(filterNode) + .Select(nameof(EFContentEntity.IndexedSchemaId)) + .Select(nameof(EFContentEntity.Id)) + .Select(nameof(EFContentEntity.Status)) + .Compile(); + + var entities = + await dbContext.Set().FromSqlRaw(sql, parameters) + .Select(x => new { SchemaId = x.IndexedSchemaId, x.Id, x.Status }).ToListAsync(ct); + + return entities.Select(x => new ContentIdStatus(x.SchemaId, x.Id, x.Status)).ToList(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_SnapshotStore.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_SnapshotStore.cs new file mode 100644 index 000000000..057f128cc --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_SnapshotStore.cs @@ -0,0 +1,295 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Contents; + +public sealed partial class EFContentRepository : ISnapshotStore, IDeleter +{ + async IAsyncEnumerable> ISnapshotStore.ReadAllAsync( + [EnumeratorCancellation] CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = dbContext.Set().ToAsyncEnumerable(); + + await foreach (var entity in entities.WithCancellation(ct)) + { + yield return new SnapshotResult(entity.DocumentId, entity.ToState(), entity.Version); + } + } + + async Task> ISnapshotStore.ReadAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/ReadAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = await dbContext.Set().Where(x => x.DocumentId == key).FirstOrDefaultAsync(ct); + if (entity == null) + { + return new SnapshotResult(default, null!, EtagVersion.Empty); + } + + return new SnapshotResult(entity.DocumentId, entity.ToState(), entity.Version); + } + } + + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/DeleteAppAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + await using var dbTransaction = await dbContext.Database.BeginTransactionAsync(ct); + try + { + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.AppId == app.Id) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.AppId == app.Id) + .ExecuteDeleteAsync(ct); + + await dbTransaction.CommitAsync(ct); + } + catch + { + await dbTransaction.RollbackAsync(ct); + throw; + } + } + } + + async Task ISnapshotStore.ClearAsync( + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/ClearAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + await using var dbTransaction = await dbContext.Database.BeginTransactionAsync(ct); + try + { + await dbContext.Set() + .ExecuteDeleteAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + + await dbTransaction.CommitAsync(ct); + } + catch + { + await dbTransaction.RollbackAsync(ct); + throw; + } + } + } + + async Task ISnapshotStore.RemoveAsync(DomainId key, + CancellationToken ct) + { + using (Telemetry.Activities.StartActivity("EFContentRepository/RemoveAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + await using var dbTransaction = await dbContext.Database.BeginTransactionAsync(ct); + try + { + await dbContext.Set().Where(x => x.DocumentId == key) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.DocumentId == key) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.FromKey == key) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.FromKey == key) + .ExecuteDeleteAsync(ct); + + await dbTransaction.CommitAsync(ct); + } + catch + { + await dbTransaction.RollbackAsync(ct); + throw; + } + } + } + + async Task ISnapshotStore.WriteAsync(SnapshotWriteJob job, + CancellationToken ct) + { + // Some data is corrupt and might throw an exception if we do not ignore it. + if (!IsValid(job.Value)) + { + return; + } + + using (Telemetry.Activities.StartActivity("EFContentRepository/WriteAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + await using var dbTransaction = await dbContext.Database.BeginTransactionAsync(ct); + + try + { + var appId = job.Value.AppId.Id; + + await dbContext.Set().Where(x => x.FromKey == job.Key) + .ExecuteDeleteAsync(ct); + + await dbContext.Set().Where(x => x.FromKey == job.Key) + .ExecuteDeleteAsync(ct); + + if (job.Value.ShouldWritePublished()) + { + await UpsertVersionedPublishedAsync(dbContext, job, ct); + } + else + { + await dbContext.Set().Where(x => x.DocumentId == job.Key) + .ExecuteDeleteAsync(ct); + } + + await UpsertVersionedCompleteAsync(dbContext, job, ct); + await dbTransaction.CommitAsync(ct); + } + catch + { + await dbTransaction.RollbackAsync(ct); + throw; + } + } + } + + async Task ISnapshotStore.WriteManyAsync(IEnumerable> jobs, + CancellationToken ct) + { + var validJobs = jobs.Where(x => IsValid(x.Value)).ToList(); + + using (Telemetry.Activities.StartActivity("EFContentRepository/WriteManyAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + await using var dbTransaction = await dbContext.Database.BeginTransactionAsync(ct); + + try + { + var keys = validJobs.Select(x => x.Key); + + var writesToCompleteContents = new List(); + var writesToCompleteReferences = new List(); + var writesToPublishedContents = new List(); + var writesToPublishedReferences = new List(); + + foreach (var job in validJobs) + { + { + var (entity, references) = await EFContentCompleteEntity.CreateAsync(job, appProvider, ct); + + writesToCompleteContents.Add(entity); + writesToCompleteReferences.AddRange(references); + } + + if (job.Value.ShouldWritePublished()) + { + var (entity, references) = await EFContentPublishedEntity.CreateAsync(job, appProvider, ct); + + writesToPublishedContents.Add(entity); + writesToPublishedReferences.AddRange(references); + } + } + + await dbContext.BulkInsertAsync(writesToCompleteContents, cancellationToken: ct); + await dbContext.BulkInsertAsync(writesToCompleteReferences, cancellationToken: ct); + await dbContext.BulkInsertAsync(writesToPublishedContents, cancellationToken: ct); + await dbContext.BulkInsertAsync(writesToPublishedReferences, cancellationToken: ct); + + await dbContext.SaveChangesAsync(ct); + await dbTransaction.CommitAsync(ct); + } + catch + { + await dbTransaction.RollbackAsync(ct); + throw; + } + } + } + + private async Task UpsertVersionedPublishedAsync(TContext dbContext, SnapshotWriteJob job, + CancellationToken ct) + { + var (entity, references) = await EFContentPublishedEntity.CreateAsync(job, appProvider, ct); + + await dbContext.AddRangeAsync(references); + await dbContext.UpsertAsync(entity, job.OldVersion, BuildUpdate, ct); + } + + private async Task UpsertVersionedCompleteAsync(TContext dbContext, SnapshotWriteJob job, + CancellationToken ct) + { + var (entity, references) = await EFContentCompleteEntity.CreateAsync(job, appProvider, ct); + + await dbContext.AddRangeAsync(references); + await dbContext.UpsertAsync(entity, job.OldVersion, BuildUpdate, ct); + } + + private static Expression, SetPropertyCalls>> BuildUpdate(EFContentEntity entity) where T : EFContentEntity + { + return b => b + .SetProperty(x => x.AppId, entity.AppId) + .SetProperty(x => x.Created, entity.Created) + .SetProperty(x => x.CreatedBy, entity.CreatedBy) + .SetProperty(x => x.Data, entity.Data) + .SetProperty(x => x.IndexedAppId, entity.IndexedAppId) + .SetProperty(x => x.IndexedSchemaId, entity.IndexedSchemaId) + .SetProperty(x => x.IsDeleted, entity.IsDeleted) + .SetProperty(x => x.LastModified, entity.LastModified) + .SetProperty(x => x.LastModifiedBy, entity.LastModifiedBy) + .SetProperty(x => x.NewData, entity.NewData) + .SetProperty(x => x.NewStatus, entity.NewStatus) + .SetProperty(x => x.ScheduledAt, entity.ScheduledAt) + .SetProperty(x => x.ScheduleJob, entity.ScheduleJob) + .SetProperty(x => x.SchemaId, entity.SchemaId) + .SetProperty(x => x.Status, entity.Status) + .SetProperty(x => x.TranslationStatus, entity.TranslationStatus) + .SetProperty(x => x.Version, entity.Version); + } + + private static bool IsValid(WriteContent state) + { + // Some data is corrupt and might throw an exception during migration if we do not skip them. + return + state.AppId != null && + state.AppId.Id != DomainId.Empty && + state.CurrentVersion != null && + state.SchemaId != null && + state.SchemaId.Id != DomainId.Empty; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Streaming.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Streaming.cs new file mode 100644 index 000000000..1bf4095a3 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFContentRepository_Streaming.cs @@ -0,0 +1,133 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Runtime.CompilerServices; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Contents; + +public sealed partial class EFContentRepository +{ + public IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + StreamIds(appId, schemaIds, ct) : + StreamIds(appId, schemaIds, ct); + } + + private async IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, + [EnumeratorCancellation] CancellationToken ct = default) where T : EFContentEntity + { + if (schemaIds is { Count: 0 }) + { + yield break; + } + + await using var dbContext = await CreateDbContextAsync(ct); + + var query = + dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .WhereIf(x => schemaIds!.Contains(x.IndexedSchemaId), schemaIds is { Count: > 0 }) + .Select(x => x.Id) + .ToAsyncEnumerable(); + + await foreach (var id in query.WithCancellation(ct)) + { + yield return id; + } + } + + public IAsyncEnumerable StreamAll(DomainId appId, HashSet? schemaIds, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + StreamAll(appId, schemaIds, ct) : + StreamAll(appId, schemaIds, ct); + } + + private async IAsyncEnumerable StreamAll(DomainId appId, HashSet? schemaIds, + [EnumeratorCancellation] CancellationToken ct = default) where T : EFContentEntity + { + if (schemaIds is { Count: 0 }) + { + yield break; + } + + await using var dbContext = await CreateDbContextAsync(ct); + + var query = + dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .WhereIf(x => schemaIds!.Contains(x.IndexedSchemaId), schemaIds is { Count: > 0 }) + .Select(x => x) + .ToAsyncEnumerable(); + + await foreach (var entity in query.WithCancellation(ct)) + { + yield return entity; + } + } + + public IAsyncEnumerable StreamReferencing(DomainId appId, DomainId reference, int take, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + StreamReferencing(appId, reference, take, ct) : + StreamReferencing(appId, reference, take, ct); + } + + private async IAsyncEnumerable StreamReferencing(DomainId appId, DomainId reference, int take, + [EnumeratorCancellation] CancellationToken ct = default) where T : EFContentEntity where TReference : EFReferenceEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + var query = + dbContext.Set() + .Join(dbContext.Set(), t => t.DocumentId, r => r.FromKey, (t, r) => new { T = t, R = r }) + .Where(x => x.R.ToId == reference) + .Where(x => x.R.AppId == appId) + .Where(x => x.T.IndexedAppId == appId) + .Select(x => x.T).Distinct() + .Take(take) + .ToAsyncEnumerable(); + + await foreach (var entity in query.WithCancellation(ct)) + { + yield return entity; + } + } + + public IAsyncEnumerable StreamScheduledWithoutDataAsync(Instant now, SearchScope scope, + CancellationToken ct = default) + { + return scope == SearchScope.All ? + StreamScheduledWithoutDataAsync(now, ct) : + StreamScheduledWithoutDataAsync(now, ct); + } + + private async IAsyncEnumerable StreamScheduledWithoutDataAsync(Instant now, + [EnumeratorCancellation] CancellationToken ct = default) where T : EFContentEntity + { + await using var dbContext = await CreateDbContextAsync(ct); + + var query = + dbContext.Set() + .Where(x => x.ScheduledAt != null && x.ScheduledAt < now) + .ToAsyncEnumerable(); + + await foreach (var entity in query.WithCancellation(ct)) + { + yield return entity; + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFReferenceEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFReferenceEntity.cs new file mode 100644 index 000000000..dec91d90c --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/EFReferenceEntity.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Domain.Apps.Entities.Contents; + +public sealed record EFReferenceCompleteEntity : EFReferenceEntity +{ +} + +public sealed record EFReferencePublishedEntity : EFReferenceEntity +{ +} + +public abstract record EFReferenceEntity +{ + public DomainId AppId { get; set; } + + public DomainId FromKey { get; set; } + + public DomainId ToId { get; set; } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Extensions.cs new file mode 100644 index 000000000..c8d5aa05a --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Extensions.cs @@ -0,0 +1,75 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Domain.Apps.Entities.Contents; + +internal static class Extensions +{ + public static bool ShouldWritePublished(this WriteContent content) + { + return content.CurrentVersion.Status == Status.Published && !content.IsDeleted; + } + + public static SqlQueryBuilder WhereNotDeleted(this SqlQueryBuilder builder, Query? query) + { + return builder.WhereNotDeleted(query?.Filter); + } + + public static SqlQueryBuilder WhereNotDeleted(this SqlQueryBuilder builder, FilterNode? filter) + { + if (filter?.HasField("IsDeleted") != true) + { + builder.Where(ClrFilter.Eq(nameof(EFContentEntity.IsDeleted), false)); + } + + return builder; + } + + public static void LimitFields(this ContentData data, IReadOnlySet fields) + { + List? toDelete = null; + foreach (var (key, value) in data) + { + if (!fields.Any(x => IsMatch(key, x))) + { + toDelete ??= []; + toDelete.Add(key); + } + } + + if (toDelete != null) + { + foreach (var key in toDelete) + { + data.Remove(key); + } + } + + static bool IsMatch(string actual, string filter) + { + const string Prefix = "data."; + + var span = filter.AsSpan(); + if (span.Equals(actual, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (span.Length <= Prefix.Length || !span.StartsWith(Prefix, StringComparison.Ordinal)) + { + return false; + } + + span = span[Prefix.Length..]; + + return span.Equals(actual, StringComparison.Ordinal); + } + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryIntegrationTests.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/QueriedStatus.cs similarity index 60% rename from backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryIntegrationTests.cs rename to backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/QueriedStatus.cs index db78bb2f4..e8612673a 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryIntegrationTests.cs +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/QueriedStatus.cs @@ -5,10 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.MongoDb.Domain.Contents; +namespace Squidex.Domain.Apps.Entities.Contents; -[Trait("Category", "Dependencies")] -public class ContentsQueryIntegrationTests(ContentsQueryFixture_Default fixture) - : ContentsQueryTestsBase(fixture), IClassFixture +public class QueriedStatus { + public string IndexedSchemaId { get; set; } + + public string Id { get; set; } + + public string Status { get; set; } } diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/EFTextIndexerState.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/EFTextIndexerState.cs new file mode 100644 index 000000000..b018c1093 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/EFTextIndexerState.cs @@ -0,0 +1,121 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.Contents.Text.State; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Domain.Apps.Entities.Contents.Text; + +public sealed class EFTextIndexerState(IDbContextFactory dbContextFactory, SqlDialect dialect, IContentRepository contentRepository) + : ITextIndexerState, IDeleter where TContext : DbContext +{ + int IDeleter.Order => -2000; + + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var (query, parameters) = + new SqlQueryBuilder(dialect, "TextState") + .Where(ClrFilter.Gt(nameof(TextContentState.UniqueContentId), new UniqueContentId(app.Id, DomainId.Empty).ToParseableString())) + .OrderAsc(nameof(TextContentState.UniqueContentId)) + .OrderAsc(nameof(TextContentState.State)) + .Compile(); + + var ids = + dbContext.Set() + .FromSqlRaw(query, parameters) + .ToAsyncEnumerable() + .TakeWhile(x => x.UniqueContentId.AppId == app.Id) + .Take(int.MaxValue) + .Select(x => x.UniqueContentId); + + await DeleteInBatchesAsync(ids, ct); + } + + async Task IDeleter.DeleteSchemaAsync(App app, Schema schema, + CancellationToken ct) + { + var ids = + contentRepository.StreamIds(app.Id, [schema.Id], SearchScope.All, ct) + .Select(x => new UniqueContentId(app.Id, x)); + + await DeleteInBatchesAsync(ids, ct); + } + + private async Task DeleteInBatchesAsync(IAsyncEnumerable ids, + CancellationToken ct) + { + var dbContext = await CreateDbContextAsync(ct); + await foreach (var batch in ids.Batch(1000, ct).WithCancellation(ct)) + { + await dbContext.Set().Where(x => batch.Contains(x.UniqueContentId)) + .ExecuteDeleteAsync(ct); + } + } + + public async Task ClearAsync( + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + } + + public async Task> GetAsync(HashSet ids, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set().Where(x => ids.Contains(x.UniqueContentId)) + .ToListAsync(ct); + + return entities.ToDictionary(x => x.UniqueContentId); + } + + public async Task SetAsync(List updates, + CancellationToken ct = default) + { + var toDelete = new List(); + var toUpsert = new List(); + + foreach (var update in updates) + { + if (update.State == TextState.Deleted) + { + toDelete.Add(update); + } + else + { + toUpsert.Add(update); + } + } + + if (toDelete.Count == 0 && toUpsert.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkDeleteAsync(toDelete, cancellationToken: ct); + await dbContext.BulkInsertOrUpdateAsync(toUpsert, cancellationToken: ct); + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/NullTextIndex.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/NullTextIndex.cs new file mode 100644 index 000000000..cbc952acf --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Contents/Text/NullTextIndex.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Contents.Text; + +public sealed class NullTextIndex : ITextIndex +{ + public Task ClearAsync( + CancellationToken ct = default) + { + return Task.CompletedTask; + } + + public Task ExecuteAsync(IndexCommand[] commands, + CancellationToken ct = default) + { + return Task.CompletedTask; + } + + public Task?> SearchAsync(App app, TextQuery query, SearchScope scope, + CancellationToken ct = default) + { + return Task.FromResult?>(null); + } + + public Task?> SearchAsync(App app, GeoQuery query, SearchScope scope, + CancellationToken ct = default) + { + return Task.FromResult?>(null); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryBuilder.cs new file mode 100644 index 000000000..8d5e0a72a --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryBuilder.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.History; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFHistoryBuilder +{ + public static void UseHistory(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.Entity(b => + { + b.Property(x => x.Actor).AsString(); + b.Property(x => x.Id).AsString(); + b.Property(x => x.OwnerId).AsString(); + b.Property(x => x.Parameters).AsJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.Created).AsDateTimeOffset(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryEventRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryEventRepository.cs new file mode 100644 index 000000000..773eaa1d3 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/History/EFHistoryEventRepository.cs @@ -0,0 +1,75 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities.History.Repositories; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.History; + +public sealed class EFHistoryEventRepository(IDbContextFactory dbContextFactory) + : IHistoryEventRepository, IDeleter where TContext : DbContext +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.OwnerId == app.Id) + .ExecuteDeleteAsync(ct); + } + + public async Task ClearAsync( + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + } + + public async Task> QueryByChannelAsync(DomainId ownerId, string? channel, int count, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var query = dbContext.Set().Where(x => x.OwnerId == ownerId); + if (!string.IsNullOrWhiteSpace(channel)) + { + query = query.Where(x => x.Channel == channel); + } + + query = query + .OrderByDescending(x => x.Created) + .ThenByDescending(x => x.Version) + .Take(count); + + var result = await query.ToListAsync(ct); + + return result; + } + + public async Task InsertManyAsync(IEnumerable historyEvents, + CancellationToken ct = default) + { + var entities = historyEvents.ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkInsertOrUpdateAsync(entities, cancellationToken: ct); + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleBuilder.cs new file mode 100644 index 000000000..2e4bef599 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleBuilder.cs @@ -0,0 +1,40 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFRuleBuilder +{ + public static void UseRules(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn, b => + { + b.Property(x => x.IndexedAppId).AsString(); + b.Property(x => x.IndexedId).AsString(); + }); + + builder.Entity(b => + { + b.Property(x => x.Id).AsString(); + b.Property(x => x.AppId).AsString(); + b.Property(x => x.Created).AsDateTimeOffset(); + b.Property(x => x.Expires).AsDateTimeOffset(); + b.Property(x => x.Job).AsJsonString(jsonSerializer, jsonColumn); + b.Property(x => x.JobResult).AsString(); + b.Property(x => x.LastModified).AsDateTimeOffset(); + b.Property(x => x.NextAttempt).AsDateTimeOffset(); + b.Property(x => x.Result).AsString(); + b.Property(x => x.RuleId).AsString(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEntity.cs new file mode 100644 index 000000000..7f22e8c1f --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEntity.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations.Schema; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Rules; + +public sealed class EFRuleEntity : EFState +{ + [Column("AppId")] + public DomainId IndexedAppId { get; set; } + + [Column("Id")] + public DomainId IndexedId { get; set; } + + [Column("Deleted")] + public bool IndexedDeleted { get; set; } + + public override void Prepare() + { + IndexedAppId = Document.AppId.Id; + IndexedDeleted = Document.IsDeleted; + IndexedId = Document.Id; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventEntity.cs new file mode 100644 index 000000000..221f2bb7c --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventEntity.cs @@ -0,0 +1,65 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using NodaTime; +using Squidex.Domain.Apps.Core.HandleRules; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Domain.Apps.Entities.Rules; + +[Table("RuleEvents")] +public sealed class EFRuleEventEntity : IRuleEventEntity +{ + [Key] + public DomainId Id { get; set; } + + public DomainId AppId { get; set; } + + public DomainId RuleId { get; set; } + + public Instant Created { get; set; } + + public Instant LastModified { get; set; } + + public RuleResult Result { get; set; } + + public RuleJobResult JobResult { get; set; } + + public RuleJob Job { get; set; } + + public string? LastDump { get; set; } + + public int NumCalls { get; set; } + + public Instant Expires { get; set; } + + public Instant? NextAttempt { get; set; } + + public static EFRuleEventEntity FromJob(RuleEventWrite item) + { + var (job, nextAttempt, error) = item; + + var entity = new EFRuleEventEntity { Job = job, Id = job.Id, NextAttempt = nextAttempt }; + + SimpleMapper.Map(job, entity); + + if (nextAttempt == null) + { + entity.JobResult = RuleJobResult.Failed; + entity.LastDump = error?.ToString(); + entity.LastModified = job.Created; + entity.Result = RuleResult.Failed; + } + + return entity; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventRepository.cs new file mode 100644 index 000000000..a3a887abd --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleEventRepository.cs @@ -0,0 +1,167 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Data; +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Rules; + +public sealed class EFRuleEventRepository(IDbContextFactory dbContextFactory) + : IRuleEventRepository, IDeleter where TContext : DbContext +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.AppId == app.Id) + .ExecuteDeleteAsync(ct); + } + + public async IAsyncEnumerable QueryPendingAsync(Instant now, + [EnumeratorCancellation] CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var ruleEvents = + dbContext.Set().Where(x => x.NextAttempt < now) + .ToAsyncEnumerable(); + + await foreach (var ruleEvent in ruleEvents.WithCancellation(ct)) + { + yield return ruleEvent; + } + } + + public async Task> QueryByAppAsync(DomainId appId, DomainId? ruleId = null, int skip = 0, int take = 20, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var query = + dbContext.Set() + .Where(x => x.AppId == appId) + .WhereIf(x => x.RuleId == ruleId!.Value, ruleId.HasValue); + + var ruleEventEntities = await query.Skip(skip).Take(take).OrderByDescending(x => x.Created).ToListAsync(ct); + var ruleEventTotal = (long)ruleEventEntities.Count; + + if (ruleEventTotal >= take || skip > 0) + { + ruleEventTotal = await query.CountAsync(ct); + } + + return ResultList.Create(ruleEventTotal, ruleEventEntities); + } + + public async Task FindAsync(DomainId id, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var ruleEvent = + await dbContext.Set().Where(x => x.Id == id) + .FirstOrDefaultAsync(ct); + + return ruleEvent; + } + + public async Task EnqueueAsync(DomainId id, Instant nextAttempt, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.Id == id) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.NextAttempt, nextAttempt), + ct); + } + + public async Task CancelByEventAsync(DomainId id, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.Id == id) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.NextAttempt, (Instant?)null) + .SetProperty(x => x.JobResult, RuleJobResult.Cancelled), + ct); + } + + public async Task CancelByRuleAsync(DomainId ruleId, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.RuleId == ruleId) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.NextAttempt, (Instant?)null) + .SetProperty(x => x.JobResult, RuleJobResult.Cancelled), + ct); + } + + public async Task CancelByAppAsync(DomainId appId, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.AppId == appId) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.NextAttempt, (Instant?)null) + .SetProperty(x => x.JobResult, RuleJobResult.Cancelled), + ct); + } + + public async Task UpdateAsync(RuleJob job, RuleJobUpdate update, + CancellationToken ct = default) + { + Guard.NotNull(job); + Guard.NotNull(update); + + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.Id == job.Id) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.Result, update.ExecutionResult) + .SetProperty(x => x.LastDump, update.ExecutionDump) + .SetProperty(x => x.JobResult, update.JobResult) + .SetProperty(x => x.NextAttempt, update.JobNext) + .SetProperty(x => x.NumCalls, x => x.NumCalls + 1), + ct); + } + + public async Task EnqueueAsync(List jobs, + CancellationToken ct = default) + { + var entities = jobs.Select(EFRuleEventEntity.FromJob).ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkInsertAsync(entities, cancellationToken: ct); + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleRepository.cs new file mode 100644 index 000000000..a121d04aa --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Rules/EFRuleRepository.cs @@ -0,0 +1,45 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Rules; + +public sealed class EFRuleRepository(IDbContextFactory dbContextFactory) + : EFSnapshotStore(dbContextFactory), IRuleRepository, IDeleter where TContext : DbContext +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + } + + public async Task> QueryAllAsync(DomainId appId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFRuleRepository/QueryAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => !x.IndexedDeleted) + .ToListAsync(ct); + + return entities.Select(x => x.Document).ToList(); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaBuilder.cs new file mode 100644 index 000000000..7fed7648a --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaBuilder.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFSchemaBuilder +{ + public static void UseSchema(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn, b => + { + b.Property(x => x.IndexedAppId).AsString(); + b.Property(x => x.IndexedId).AsString(); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaEntity.cs new file mode 100644 index 000000000..961e9da53 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaEntity.cs @@ -0,0 +1,36 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations.Schema; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Schemas; + +public sealed class EFSchemaEntity : EFState +{ + [Column("AppId")] + public DomainId IndexedAppId { get; set; } + + [Column("Id")] + public DomainId IndexedId { get; set; } + + [Column("Name")] + public string IndexedName { get; set; } + + [Column("Deleted")] + public bool IndexedDeleted { get; set; } + + public override void Prepare() + { + IndexedAppId = Document.AppId.Id; + IndexedDeleted = Document.IsDeleted; + IndexedId = Document.Id; + IndexedName = Document.Name; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaRepository.cs new file mode 100644 index 000000000..5816f2ebf --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Schemas/EFSchemaRepository.cs @@ -0,0 +1,119 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Schemas; + +public sealed class EFSchemaRepository(IDbContextFactory dbContextFactory) + : EFSnapshotStore(dbContextFactory), ISchemaRepository, ISchemasHash, IDeleter where TContext : DbContext +{ + async Task IDeleter.DeleteAppAsync(App app, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.IndexedAppId == app.Id) + .ExecuteDeleteAsync(ct); + } + + async Task IDeleter.DeleteSchemaAsync(App app, Schema schema, + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.IndexedId == schema.Id) + .ExecuteDeleteAsync(ct); + } + + public async Task> QueryAllAsync(DomainId appId, CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFSchemaRepository/QueryAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId) + .Where(x => !x.IndexedDeleted) + .OrderBy(x => x.IndexedName) + .ToListAsync(ct); + + return entities.Select(x => x.Document).ToList(); + } + } + + public async Task FindAsync(DomainId appId, DomainId id, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFSchemaRepository/FindAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId && x.IndexedId == id) + .Where(x => !x.IndexedDeleted) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + public async Task FindAsync(DomainId appId, string name, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFSchemaRepository/FindAsyncByName")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.IndexedAppId == appId && x.IndexedName == name) + .Where(x => !x.IndexedDeleted) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + public async Task GetCurrentHashAsync(App app, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFSchemaRepository/GetCurrentHashAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + await dbContext.Set() + .Where(x => x.IndexedAppId == app.Id) + .Where(x => !x.IndexedDeleted) + .Select(x => new { Id = x.IndexedId, x.Version }) + .ToListAsync(ct); + + return SchemasHashKey.Create(app, entities.ToDictionary(x => x.Id, x => x.Version)); + } + } + + protected override Expression, SetPropertyCalls>> BuildUpdate(EFSchemaEntity entity) + { + return u => u + .SetProperty(x => x.Document, entity.Document) + .SetProperty(x => x.IndexedAppId, entity.IndexedAppId) + .SetProperty(x => x.IndexedDeleted, entity.IndexedDeleted) + .SetProperty(x => x.IndexedId, entity.IndexedId) + .SetProperty(x => x.IndexedName, entity.IndexedName) + .SetProperty(x => x.Version, entity.Version); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamBuilder.cs new file mode 100644 index 000000000..9fc53ea9a --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamBuilder.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Teams; +using Squidex.Domain.Apps.Entities.Teams; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFTeamBuilder +{ + public static void UseTeams(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.UseSnapshot(jsonSerializer, jsonColumn); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamEntity.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamEntity.cs new file mode 100644 index 000000000..acc99a88b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamEntity.cs @@ -0,0 +1,39 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations.Schema; +using Squidex.Domain.Apps.Core.Teams; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Teams; + +public sealed class EFTeamEntity : EFState +{ + [Column("UserIds")] + public string IndexedUserIds { get; set; } + + [Column("Deleted")] + public bool IndexedDeleted { get; set; } + + [Column("AuthDomain")] + public string? IndexedAuthDomain { get; set; } + + public override void Prepare() + { + var users = new HashSet + { + Document.CreatedBy.Identifier, + }; + + users.AddRange(Document.Contributors.Keys); + + IndexedAuthDomain = Document.AuthScheme?.Domain; + IndexedDeleted = Document.IsDeleted; + IndexedUserIds = TagsConverter.ToString(users); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamRepository.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamRepository.cs new file mode 100644 index 000000000..b81ec97ff --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Apps/Entities/Teams/EFTeamRepository.cs @@ -0,0 +1,82 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Core.Teams; +using Squidex.Domain.Apps.Entities.Teams.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Domain.Apps.Entities.Teams; + +public sealed class EFTeamRepository(IDbContextFactory dbContextFactory) + : EFSnapshotStore(dbContextFactory), ITeamRepository where TContext : DbContext +{ + public async Task> QueryAllAsync(string contributorId, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFTeamRepository/QueryAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var formattedId = TagsConverter.FormatFilter(contributorId); + var entities = + await dbContext.Set() + .Where(x => x.IndexedUserIds.Contains(formattedId)) + .Where(x => !x.IndexedDeleted) + .ToListAsync(ct); + + return entities.Select(x => x.Document).ToList(); + } + } + + public async Task FindAsync(DomainId id, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFTeamRepository/FindAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.DocumentId == id) + .Where(x => !x.IndexedDeleted) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + public async Task FindByAuthDomainAsync(string authDomain, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFTeamRepository/FindByAuthDomainAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.IndexedAuthDomain == authDomain) + .Where(x => !x.IndexedDeleted) + .FirstOrDefaultAsync(ct); + + return entity?.Document; + } + } + + protected override Expression, SetPropertyCalls>> BuildUpdate(EFTeamEntity entity) + { + return u => u + .SetProperty(x => x.Document, entity.Document) + .SetProperty(x => x.IndexedAuthDomain, entity.IndexedAuthDomain) + .SetProperty(x => x.IndexedDeleted, entity.IndexedDeleted) + .SetProperty(x => x.IndexedUserIds, entity.IndexedUserIds) + .SetProperty(x => x.Version, entity.Version); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Domain/Users/EFUserFactory.cs b/backend/src/Squidex.Data.EntityFramework/Domain/Users/EFUserFactory.cs new file mode 100644 index 000000000..ba2875f5e --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Domain/Users/EFUserFactory.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using Microsoft.AspNetCore.Identity; + +namespace Squidex.Domain.Users; + +public sealed class EFUserFactory : IUserFactory +{ + public IdentityUser Create(string email) + { + return new IdentityUser { Email = email, UserName = email }; + } + + public bool IsId(string id) + { + return Guid.TryParse(id, CultureInfo.InvariantCulture, out _); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheBuilder.cs new file mode 100644 index 000000000..7bd90b2d1 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheBuilder.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Caching; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFCacheBuilder +{ + public static void UseCache(this ModelBuilder builder) + { + builder.Entity(b => + { + b.ToTable("Cache"); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheEntity.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheEntity.cs new file mode 100644 index 000000000..2ab379363 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFCacheEntity.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Squidex.Infrastructure.Caching; + +[Table("Cache")] +public class EFCacheEntity +{ + [Key] + public string Key { get; set; } + + public DateTime Expires { get; set; } + + public byte[] Value { get; set; } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFDistributedCache.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFDistributedCache.cs new file mode 100644 index 000000000..3c620b3f6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Caching/EFDistributedCache.cs @@ -0,0 +1,147 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Distributed; +using Squidex.Hosting; +using Squidex.Infrastructure.Timers; + +namespace Squidex.Infrastructure.Caching; + +public sealed class EFDistributedCache(IDbContextFactory dbContextFactory, TimeProvider timeProvider) + : IDistributedCache, IInitializable where TContext : DbContext +{ +#pragma warning disable RECS0108 // Warns about static fields in generic types + private static readonly TimeSpan CleanupTime = TimeSpan.FromMinutes(10); +#pragma warning restore RECS0108 // Warns about static fields in generic types + private CompletionTimer? timer; + + public Task InitializeAsync( + CancellationToken ct) + { + timer = new CompletionTimer(CleanupTime, CleanupAsync); + return Task.CompletedTask; + } + + public Task ReleaseAsync( + CancellationToken ct) + { + return timer?.StopAsync() ?? Task.CompletedTask; + } + + public byte[] Get(string key) + { + throw new NotSupportedException(); + } + + public void Refresh(string key) + { + throw new NotSupportedException(); + } + + public void Remove(string key) + { + throw new NotSupportedException(); + } + + public void Set(string key, byte[] value, DistributedCacheEntryOptions options) + { + throw new NotSupportedException(); + } + + public Task RefreshAsync(string key, + CancellationToken token = default) + { + return Task.CompletedTask; + } + + public async Task CleanupAsync( + CancellationToken token) + { + var now = timeProvider.GetUtcNow().UtcDateTime; + + var dbContext = await CreateDbContextAsync(token); + + await dbContext.Set().Where(x => x.Expires < now) + .ExecuteDeleteAsync(token); + } + + public async Task RemoveAsync(string key, + CancellationToken token = default) + { + await using var dbContext = await CreateDbContextAsync(token); + + await dbContext.Set().Where(x => x.Key == key) + .ExecuteDeleteAsync(token); + } + + public async Task GetAsync(string key, + CancellationToken token = default) + { + await using var dbContext = await CreateDbContextAsync(token); + + var now = timeProvider.GetUtcNow().UtcDateTime; + + var entry = + await dbContext.Set() + .Where(x => x.Key == key).FirstOrDefaultAsync(token); + + if (entry != null && entry.Expires > now) + { + return entry.Value; + } + + return null; + } + + public async Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, + CancellationToken token = default) + { + await using var dbContext = await CreateDbContextAsync(token); + + var expires = timeProvider.GetUtcNow().UtcDateTime; + + if (options.AbsoluteExpiration.HasValue) + { + expires = options.AbsoluteExpiration.Value.UtcDateTime; + } + else if (options.AbsoluteExpirationRelativeToNow.HasValue) + { + expires += options.AbsoluteExpirationRelativeToNow.Value; + } + else if (options.SlidingExpiration.HasValue) + { + expires += options.SlidingExpiration.Value; + } + else + { + expires = DateTime.MaxValue; + } + + var entity = new EFCacheEntity { Key = key, Value = value, Expires = expires }; + try + { + await dbContext.Set().AddAsync(entity, token); + await dbContext.SaveChangesAsync(token); + } + finally + { + dbContext.Entry(entity).State = EntityState.Detached; + } + + await dbContext.Set().Where(x => x.Key == key) + .ExecuteUpdateAsync(u => u + .SetProperty(x => x.Value, value) + .SetProperty(x => x.Expires, expires), + token); + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Extensions.cs new file mode 100644 index 000000000..bf2741136 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Extensions.cs @@ -0,0 +1,143 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using Squidex.Domain.Apps.Entities; +using Squidex.Infrastructure.Queries; +using Squidex.Infrastructure.States; + +namespace Squidex.Infrastructure; + +public static class Extensions +{ + public static IQueryable Pagination(this IQueryable source, ClrQuery query) + { + if (query.Skip > 0) + { + source = source.Skip((int)query.Skip); + } + + if (query.Take < long.MaxValue) + { + source = source.Take((int)query.Take); + } + + return source; + } + + public static IQueryable WhereIf(this IQueryable source, Expression> predicate, bool valid) + { + if (!valid) + { + return source; + } + + return source.Where(predicate); + } + + public static async Task> QueryAsync(this IQueryable queryable, Q q, + CancellationToken ct) where T : class + { + var query = q.Query; + + var queryEntities = await queryable.Pagination(q.Query).ToListAsync(ct); + var queryTotal = (long)queryEntities.Count; + + if (queryEntities.Count >= query.Take || query.Skip > 0) + { + if (q.NoTotal) + { + queryTotal = -1; + } + else + { + queryTotal = await queryable.CountAsync(ct); + } + } + + if (q.Query.Random > 0) + { + queryEntities = queryEntities.TakeRandom(q.Query.Random).ToList(); + } + + return ResultList.Create(queryTotal, queryEntities.OfType()); + } + + public static async Task> QueryAsync(this DbContext dbContext, SqlQueryBuilder sqlQuery, Q q, + CancellationToken ct) where T : class + { + sqlQuery.Limit(q.Query); + sqlQuery.Offset(q.Query); + sqlQuery.Order(q.Query); + sqlQuery.Where(q.Query); + + var (sql, parameters) = sqlQuery.Compile(); + + var queryEntities = await dbContext.Set().FromSqlRaw(sql, parameters).ToListAsync(ct); + var queryTotal = (long)queryEntities.Count; + + if (queryEntities.Count >= q.Query.Take || q.Query.Skip > 0) + { + if (q.NoTotal || q.NoSlowTotal) + { + queryTotal = -1; + } + else + { + var (countSql, countParams) = sqlQuery.Count().Compile(); + + queryTotal = + await dbContext.Database.SqlQueryRaw(countSql, countParams) + .FirstOrDefaultAsync(ct); + } + } + + if (q.Query.Random > 0) + { + queryEntities = queryEntities.TakeRandom(q.Query.Random).ToList(); + } + + return ResultList.Create(queryTotal, queryEntities.OfType()); + } + + public static async Task UpsertAsync(this DbContext dbContext, T entity, long oldVersion, + Func, SetPropertyCalls>>> update, + CancellationToken ct) where T : class, IVersionedEntity + { + try + { + await dbContext.Set().AddAsync(entity, ct); + await dbContext.SaveChangesAsync(ct); + } + catch (DbUpdateException) + { + var updateQuery = dbContext.Set().Where(x => x.DocumentId == entity.DocumentId); + if (oldVersion > EtagVersion.Any) + { + updateQuery = updateQuery.Where(x => x.Version == oldVersion); + } + + var updateCount = + await updateQuery + .ExecuteUpdateAsync(update(entity), ct); + + if (updateCount != 1) + { + var currentVersions = + await dbContext.Set() + .Where(x => x.DocumentId == entity.DocumentId).Select(x => x.Version) + .ToListAsync(ct); + + var current = currentVersions.Count == 1 ? currentVersions[0] : EtagVersion.Empty; + + throw new InconsistentStateException(current, oldVersion); + } + } + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryDedicatedIntegrationTests.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/IVersionedEntity.cs similarity index 59% rename from backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryDedicatedIntegrationTests.cs rename to backend/src/Squidex.Data.EntityFramework/Infrastructure/IVersionedEntity.cs index b6666129a..a5f98e990 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryDedicatedIntegrationTests.cs +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/IVersionedEntity.cs @@ -5,10 +5,11 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.MongoDb.Domain.Contents; +namespace Squidex.Infrastructure; -[Trait("Category", "Dependencies")] -public class ContentsQueryDedicatedIntegrationTests(ContentsQueryFixture_Dedicated fixture) - : ContentsQueryTestsBase(fixture), IClassFixture +public interface IVersionedEntity { + T DocumentId { get; } + + long Version { get; } } diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonAttribute.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonAttribute.cs new file mode 100644 index 000000000..47c57f9bf --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonAttribute.cs @@ -0,0 +1,13 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure; + +[AttributeUsage(AttributeTargets.Property)] +public sealed class JsonAttribute : Attribute +{ +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonConversion.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonConversion.cs new file mode 100644 index 000000000..835880db4 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/JsonConversion.cs @@ -0,0 +1,184 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.Infrastructure.Json; + +#pragma warning disable RECS0015 // If an extension method is called as static method convert it to method syntax + +namespace Squidex.Infrastructure; + +public static class JsonConversion +{ + public static PropertyBuilder AsJsonString(this PropertyBuilder propertyBuilder, IJsonSerializer jsonSerializer, string? columnType) + where T : class + { + var converter = new ValueConverter( + v => jsonSerializer.Serialize(v, false), + v => jsonSerializer.Deserialize(v, null)! + ); + + propertyBuilder.HasConversion(converter).HasColumnType(columnType); + return propertyBuilder; + } + + public static PropertyBuilder AsNullableJsonString(this PropertyBuilder propertyBuilder, IJsonSerializer jsonSerializer, string? columnType) + where T : class + { + var converter = new ValueConverter( + v => v != null ? jsonSerializer.Serialize(v, false) : null, + v => v != null ? jsonSerializer.Deserialize(v, null) : null! + ); + + propertyBuilder.HasConversion(converter).HasColumnType(columnType); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v.ToString()!, + v => DomainId.Create(v) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(255); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v != null ? v.ToString()! : null, + v => v != null ? DomainId.Create(v) : null + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(255); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v.ToString(), + v => RefToken.Parse(v) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(100); + return propertyBuilder; + } + + public static PropertyBuilder> AsString(this PropertyBuilder> propertyBuilder) + { + var converter = new ValueConverter, string>( + v => v.ToString(), + v => NamedId.Parse(v, ParseDomainId) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(255); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) where T : struct + { + var converter = new ValueConverter( + v => v.ToString()!, + v => Enum.Parse(v, true) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(100); + return propertyBuilder; + } + + public static PropertyBuilder AsNullableString(this PropertyBuilder propertyBuilder) where T : struct + { + var converter = new ValueConverter( + v => v != null ? v.ToString() : null, + v => v != null ? Enum.Parse(v, true) : null + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(100); + return propertyBuilder; + } + + public static PropertyBuilder> AsString(this PropertyBuilder> propertyBuilder) + { + var converter = new ValueConverter, string>( + v => TagsConverter.ToString(v), + v => TagsConverter.ToSet(v) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(1000); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v.ToString()!, + v => new Status(v) + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(100); + return propertyBuilder; + } + + public static PropertyBuilder AsNullableString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v != null ? v.ToString()! : null, + v => v != null ? new Status(v) : null + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(100); + return propertyBuilder; + } + + public static PropertyBuilder AsString(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v.ToParseableString(), + v => v.ToUniqueContentId() + ); + + propertyBuilder.HasConversion(converter).HasMaxLength(255); + return propertyBuilder; + } + + public static PropertyBuilder AsDateTimeOffset(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v.ToDateTimeOffset(), + v => Instant.FromDateTimeOffset(v) + ); + + propertyBuilder.HasConversion(converter); + return propertyBuilder; + } + + public static PropertyBuilder AsDateTimeOffset(this PropertyBuilder propertyBuilder) + { + var converter = new ValueConverter( + v => v != null ? v.Value.ToDateTimeOffset() : null, + v => v != null ? Instant.FromDateTimeOffset(v.Value) : null + ); + + propertyBuilder.HasConversion(converter); + return propertyBuilder; + } + + private static bool ParseDomainId(ReadOnlySpan value, out DomainId result) + { + result = DomainId.Create(new string(value)); + + return true; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestBuilder.cs new file mode 100644 index 000000000..3c5989950 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestBuilder.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.Log; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFRequestBuilder +{ + public static void UseRequest(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn) + { + builder.Entity(b => + { + b.ToTable("Requests"); + b.Property(x => x.Timestamp).AsDateTimeOffset(); + b.Property(x => x.Properties).AsJsonString(jsonSerializer, jsonColumn); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestEntity.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestEntity.cs new file mode 100644 index 000000000..94a6e9cde --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestEntity.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using NodaTime; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Infrastructure.Log; + +[Table("Requests")] +[Index(nameof(Key))] +public sealed class EFRequestEntity +{ + [Key] + public int Id { get; set; } + + public string Key { get; set; } + + public Instant Timestamp { get; set; } + + public Dictionary Properties { get; set; } + + public static EFRequestEntity FromRequest(Request request) + { + return SimpleMapper.Map(request, new EFRequestEntity()); + } + + public Request ToRequest() + { + return SimpleMapper.Map(this, new Request()); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestLogRepository.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestLogRepository.cs new file mode 100644 index 000000000..2271bb03e --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Log/EFRequestLogRepository.cs @@ -0,0 +1,101 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using NodaTime; +using Squidex.Hosting; +using Squidex.Infrastructure.Timers; + +namespace Squidex.Infrastructure.Log; + +public sealed class EFRequestLogRepository(IDbContextFactory dbContextFactory, IOptions options) + : IRequestLogRepository, IInitializable where TContext : DbContext +{ +#pragma warning disable RECS0108 // Warns about static fields in generic types + private static readonly TimeSpan CleanupTime = TimeSpan.FromMinutes(10); +#pragma warning restore RECS0108 // Warns about static fields in generic types + private readonly RequestLogStoreOptions options = options.Value; + private CompletionTimer? timer; + + public Task InitializeAsync( + CancellationToken ct) + { + timer = new CompletionTimer(CleanupTime, CleanupAsync); + return Task.CompletedTask; + } + + public Task ReleaseAsync( + CancellationToken ct) + { + return timer?.StopAsync() ?? Task.CompletedTask; + } + + private async Task CleanupAsync( + CancellationToken ct) + { + var maxAge = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromDays(options.StoreRetentionInDays)); + + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.Timestamp < maxAge) + .ExecuteDeleteAsync(ct); + } + + public async Task DeleteAsync(string key, + CancellationToken ct = default) + { + Guard.NotNullOrEmpty(key); + + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.Key == key) + .ExecuteDeleteAsync(ct); + } + + public async Task InsertManyAsync(IEnumerable items, + CancellationToken ct = default) + { + Guard.NotNull(items); + + var entities = items.Select(EFRequestEntity.FromRequest).ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.BulkInsertAsync(entities, cancellationToken: ct); + } + + public async IAsyncEnumerable QueryAllAsync(string key, Instant fromTime, Instant toTime, + [EnumeratorCancellation] CancellationToken ct = default) + { + Guard.NotNullOrEmpty(key); + + await using var dbContext = await CreateDbContextAsync(ct); + + var entities = + dbContext.Set() + .Where(x => x.Key == key) + .Where(x => x.Timestamp >= fromTime && x.Timestamp <= toTime) + .ToAsyncEnumerable(); + + await foreach (var entity in entities.WithCancellation(ct)) + { + yield return entity.ToRequest(); + } + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs new file mode 100644 index 000000000..733789a9b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseCreator.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Squidex.Hosting; + +namespace Squidex.Infrastructure.Migrations; + +public sealed class DatabaseCreator(IDbContextFactory dbContextFactory) : IInitializable + where TContext : DbContext +{ + public int Order => -1000; + + public async Task InitializeAsync( + CancellationToken ct) + { + await using var context = await dbContextFactory.CreateDbContextAsync(ct); + + if (context.Database.GetService() is not RelationalDatabaseCreator relationalDatabaseCreator) + { + return; + } + + await relationalDatabaseCreator.EnsureCreatedAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs new file mode 100644 index 000000000..8b01dac63 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Hosting; + +namespace Squidex.Infrastructure.Migrations; + +public sealed class DatabaseMigrator(IDbContextFactory dbContextFactory) : IInitializable + where TContext : DbContext +{ + public int Order => -1000; + + public async Task InitializeAsync( + CancellationToken ct) + { + await using var context = await dbContextFactory.CreateDbContextAsync(ct); + + await context.Database.MigrateAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationBuilder.cs new file mode 100644 index 000000000..038dfb0de --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationBuilder.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Migrations; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFMigrationBuilder +{ + public static void UseMigration(this ModelBuilder builder) + { + builder.Entity(b => + { + b.ToTable("Migrations"); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationEntity.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationEntity.cs new file mode 100644 index 000000000..1082f4339 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationEntity.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Squidex.Infrastructure.Migrations; + +public sealed class EFMigrationEntity +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.None)] + public int Id { get; set; } + + public bool IsLocked { get; set; } + + public int Version { get; set; } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationStatus.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationStatus.cs new file mode 100644 index 000000000..628b29067 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/EFMigrationStatus.cs @@ -0,0 +1,84 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Hosting; + +namespace Squidex.Infrastructure.Migrations; + +public sealed class EFMigrationStatus(IDbContextFactory dbContextFactory) + : IMigrationStatus, IInitializable where TContext : DbContext +{ + private const int DefaultId = 1; + + public async Task InitializeAsync( + CancellationToken ct) + { + await using var dbContext = await CreateDbContextAsync(ct); + + try + { + var newEntry = new EFMigrationEntity { Id = DefaultId }; + + await dbContext.Set().AddAsync(newEntry, ct); + await dbContext.SaveChangesAsync(ct); + } + catch (DbUpdateException) + { + } + } + + public async Task GetVersionAsync( + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = + await dbContext.Set() + .Where(x => x.Id == DefaultId).FirstOrDefaultAsync(ct); + + return entity?.Version ?? 0; + } + + public async Task TryLockAsync( + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var updateCount = + await dbContext.Set() + .Where(x => x.Id == DefaultId && !x.IsLocked) + .ExecuteUpdateAsync(x => x.SetProperty(p => p.IsLocked, true), ct); + + return updateCount == 1; + } + + public async Task CompleteAsync(int newVersion, + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.Id == DefaultId) + .ExecuteUpdateAsync(x => x.SetProperty(p => p.Version, newVersion), ct); + } + + public async Task UnlockAsync( + CancellationToken ct = default) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .Where(x => x.Id == DefaultId && x.IsLocked) + .ExecuteUpdateAsync(x => x.SetProperty(p => p.IsLocked, false), ct); + } + + private Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/Extensions.cs new file mode 100644 index 000000000..4701ceae4 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/Extensions.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Text; + +namespace Squidex.Infrastructure.Queries; + +public static class Extensions +{ + public static void AppendLines(this StringBuilder sb, List lines, string tab) + { + sb.AppendLine(); + sb.Append(tab); + sb.Append(lines[0]); + + foreach (var line in lines.Skip(1)) + { + sb.Append(','); + sb.AppendLine(); + sb.Append(tab); + sb.Append(line); + } + + sb.AppendLine(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs new file mode 100644 index 000000000..abd792fbe --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlDialect.cs @@ -0,0 +1,233 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections; +using System.Text; + +namespace Squidex.Infrastructure.Queries; + +public class SqlDialect +{ + private const string Tab = " "; + + public virtual string BuildSelectStatement(SqlQuery request) + { + var sb = new StringBuilder("SELECT"); + + sb.AppendLines(request.Fields, Tab); + sb.AppendLine($"FROM {FormatTable(request.Table)}"); + + if (request.Where.Count > 0) + { + var query = And(request.Where); + + sb.AppendLine("WHERE"); + sb.Append(Tab); + sb.Append(query); + sb.AppendLine(); + } + + if (request.Order.Count > 0) + { + sb.Append("ORDER BY"); + sb.AppendLines(request.Order, Tab); + } + + var pagination = FormatLimitOffset(request.Limit, request.Offset, request.Order.Count > 0); + + if (!string.IsNullOrEmpty(pagination)) + { + sb.AppendLine(pagination); + } + + return sb.ToString(); + } + + public virtual string FormatLimitOffset(long limit, long offset, bool hasOrder) + { + var hasLimit = limit > 0 && limit < long.MaxValue; + + if (hasLimit && offset > 0) + { + return $"LIMIT {limit} OFFSET {offset}"; + } + + if (offset > 0) + { + return $"OFFSET {offset}"; + } + + if (hasLimit) + { + return $"LIMIT {limit}"; + } + + return string.Empty; + } + + public virtual string? JsonColumnType() + { + return null; + } + + public virtual string And(IEnumerable parts) + { + return $"({string.Join(" AND ", parts)})"; + } + + public virtual string Or(IEnumerable parts) + { + return $"({string.Join(" OR ", parts)})"; + } + + public virtual string Not(string part) + { + return $"NOT ({part})"; + } + + public virtual string SelectAll() + { + return "*"; + } + + public virtual string CountAll() + { + return $"COUNT(*) as {Field("Value", false)}"; + } + + public virtual string Field(PropertyPath path, bool isJson) + { + return $"{FormatField(path, isJson)}"; + } + + public virtual string OrderBy(PropertyPath path, SortOrder order, bool isJson) + { + return $"{FormatField(path, isJson)} {FormatOrder(order)}"; + } + + public virtual string Where(PropertyPath path, CompareOperator op, ClrValue value, SqlParams queryParameters, bool isJson) + { + return $"{FormatField(path, isJson)} {FormatOperator(op, value)} {FormatValues(op, value, queryParameters)}"; + } + + public virtual string WhereQuery(PropertyPath path, CompareOperator op, string query, bool isJson) + { + return $"{FormatField(path, isJson)} {FormatOperator(op, ClrValue.Null)} ({query})"; + } + + protected virtual string FormatValues(CompareOperator op, ClrValue value, SqlParams queryParameters) + { + if (!value.IsList && value.ValueType == ClrValueType.Null) + { + return "NULL"; + } + + string[] parameters; + if (value.IsList && value.Value is IEnumerable list) + { + parameters = list.Cast().Select(AddParameter).ToArray(); + } + else + { + parameters = [AddParameter(value.Value!)]; + } + + string AddParameter(object value) + { + value = FormatRawValue(value, op); + + return queryParameters.AddPositional(value); + } + + if (op == CompareOperator.In) + { + return $"({string.Join(", ", parameters)})"; + } + + return parameters[0]; + } + + protected virtual object FormatRawValue(object value, CompareOperator op) + { + switch (op) + { + case CompareOperator.StartsWith: + value = $"{value}%"; + break; + case CompareOperator.EndsWith: + value = $"%{value}"; + break; + case CompareOperator.Contains: + value = $"%{value}%"; + break; + } + + return value; + } + + protected virtual string FormatOperator(CompareOperator op, ClrValue value) + { + switch (op) + { + case CompareOperator.Equals when value.ValueType == ClrValueType.Null: + return "IS"; + case CompareOperator.Equals: + return "="; + case CompareOperator.NotEquals when value.ValueType == ClrValueType.Null: + return "IS NOT"; + case CompareOperator.NotEquals: + return "!="; + case CompareOperator.LessThan: + return "<"; + case CompareOperator.LessThanOrEqual: + return "<="; + case CompareOperator.Contains: + return "LIKE"; + case CompareOperator.EndsWith: + return "LIKE"; + case CompareOperator.StartsWith: + return "LIKE"; + case CompareOperator.GreaterThan: + return ">"; + case CompareOperator.GreaterThanOrEqual: + return ">="; + case CompareOperator.In: + return "IN"; + default: + ThrowHelper.NotSupportedException(); + return null!; + } + } + + protected virtual string FormatOrder(SortOrder order) + { + switch (order) + { + case SortOrder.Ascending: + return "ASC"; + case SortOrder.Descending: + return "DESC"; + default: + throw new ArgumentException("Invalid enum value.", nameof(order)); + } + } + + protected virtual string FormatTable(string tableName) + { + return tableName; + } + + protected virtual string FormatField(PropertyPath path, bool isJson) + { + if (isJson) + { + throw new InvalidOperationException("JSON is not supported by basic dialect."); + } + + return path[0]; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlParams.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlParams.cs new file mode 100644 index 000000000..44e0cfb44 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlParams.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Queries; + +public sealed class SqlParams : List +{ + public string AddPositional(object value) + { + var parameterName = $"{{{Count}}}"; + + Add(value); + + return parameterName; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQuery.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQuery.cs new file mode 100644 index 000000000..da26f8803 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQuery.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Queries; + +public class SqlQuery(string table) +{ + public string Table => table; + + public bool AsCount { get; set; } + + public long Limit { get; set; } = long.MaxValue; + + public long Offset { get; set; } + + public List Fields { get; set; } = []; + + public List Where { get; set; } = []; + + public List Order { get; set; } = []; +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQueryBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQueryBuilder.cs new file mode 100644 index 000000000..5b573c337 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Queries/SqlQueryBuilder.cs @@ -0,0 +1,160 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.Queries; + +public class SqlQueryBuilder(SqlDialect dialect, string table, SqlParams? parameters = null) : FilterNodeVisitor +{ + private readonly SqlQuery sqlQuery = new SqlQuery(table); + private readonly SqlParams sqlParameters = parameters ?? []; + + public SqlQueryBuilder Count() + { + sqlQuery.Fields = [dialect.CountAll()]; + sqlQuery.Order.Clear(); + sqlQuery.Offset = 0; + sqlQuery.Limit = long.MaxValue; + return this; + } + + public SqlQueryBuilder WhereQuery(PropertyPath path, CompareOperator op, Func factory) + { + var builder = factory(sqlParameters, dialect); + + sqlQuery.Where.Add(dialect.WhereQuery(Visit(path), op, builder.CompileQuery(), IsJsonPath(path))); + return this; + } + + public SqlQueryBuilder Where(FilterNode filter) + { + sqlQuery.Where.Add(filter.Accept(this, None.Value)); + return this; + } + + public SqlQueryBuilder Order(PropertyPath path, SortOrder order) + { + sqlQuery.Order.Add(dialect.OrderBy(Visit(path), order, IsJsonPath(path))); + return this; + } + + public SqlQueryBuilder Select(PropertyPath path) + { + sqlQuery.Fields.Add(dialect.Field(Visit(path), IsJsonPath(path))); + return this; + } + + public SqlQueryBuilder Limit(long limit) + { + sqlQuery.Limit = limit; + return this; + } + + public SqlQueryBuilder Offset(long offset) + { + sqlQuery.Offset = offset; + return this; + } + + public SqlQueryBuilder OrderAsc(PropertyPath path) + { + return Order(path, SortOrder.Ascending); + } + + public SqlQueryBuilder OrderDesc(PropertyPath path) + { + return Order(path, SortOrder.Descending); + } + + public SqlQueryBuilder Limit(ClrQuery query) + { + return Limit(query.Take); + } + + public SqlQueryBuilder Offset(ClrQuery query) + { + return Offset(query.Skip); + } + + public SqlQueryBuilder Where(ClrQuery query) + { + Guard.NotNull(query); + + if (query.Filter != null) + { + return Where(query.Filter); + } + + return this; + } + + public SqlQueryBuilder Order(ClrQuery query) + { + Guard.NotNull(query); + + if (query.Sort != null) + { + foreach (var sort in query.Sort) + { + Order(sort.Path, sort.Order); + } + } + + return this; + } + + public (string Sql, object[] Parameters) Compile() + { + return (CompileQuery(), sqlParameters.ToArray()); + } + + public virtual string CompileQuery() + { + if (sqlQuery.Fields.Count == 0) + { + sqlQuery.Fields.Add(dialect.SelectAll()); + } + + return dialect.BuildSelectStatement(sqlQuery); + } + + public virtual bool IsJsonPath(PropertyPath path) + { + return false; + } + + public virtual PropertyPath Visit(PropertyPath path) + { + return path; + } + + public override string Visit(CompareFilter nodeIn, None args) + { + return dialect.Where( + Visit(nodeIn.Path), + nodeIn.Operator, + nodeIn.Value, + sqlParameters, + IsJsonPath(nodeIn.Path)); + } + + public override string Visit(LogicalFilter nodeIn, None args) + { + var parts = nodeIn.Filters.Select(x => x.Accept(this, args)); + + if (nodeIn.Type == LogicalFilterType.And) + { + return dialect.And(parts); + } + + return dialect.Or(parts); + } + + public override string Visit(NegateFilter nodeIn, None args) + { + return dialect.Not(nodeIn.Filter.Accept(this, args)); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotBuilder.cs new file mode 100644 index 000000000..9d8e4b87c --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotBuilder.cs @@ -0,0 +1,44 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Squidex.Infrastructure.Json; + +namespace Squidex.Infrastructure.States; + +public static class EFSnapshotBuilder +{ + public static void UseSnapshot(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn, Action>>? configure = null) + where T : class + { + builder.UseSnapshot>(jsonSerializer, jsonColumn, configure); + } + + public static void UseSnapshot(this ModelBuilder builder, IJsonSerializer jsonSerializer, string? jsonColumn, Action>? configure = null) + where TEntity : EFState where T : class + { + builder.Entity(b => + { + b.ToTable(GetTableName()); + b.Property(x => x.DocumentId).AsString(); + b.Property(x => x.Document).AsJsonString(jsonSerializer, jsonColumn); + + configure?.Invoke(b); + }); + } + + private static string GetTableName() + { + var attribute = typeof(T).GetCustomAttributes(true).OfType().FirstOrDefault(); + + var tableSuffix = attribute?.Name ?? typeof(T).Name; + var tableName = $"States_{tableSuffix}"; + + return tableName; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotStore.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotStore.cs new file mode 100644 index 000000000..02fb37ad6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFSnapshotStore.cs @@ -0,0 +1,138 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using EFCore.BulkExtensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; + +namespace Squidex.Infrastructure.States; + +public class EFSnapshotStore(IDbContextFactory dbContextFactory) : ISnapshotStore + where TContext : DbContext + where TState : EFState, new() +{ + public async Task ClearAsync( + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/ClearAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set() + .ExecuteDeleteAsync(ct); + } + } + + public async IAsyncEnumerable> ReadAllAsync( + [EnumeratorCancellation] CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/ReadAllAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await foreach (var entity in dbContext.Set().ToAsyncEnumerable().WithCancellation(ct)) + { + if (entity.Document is IOnRead onRead) + { + await onRead.OnReadAsync(); + } + + yield return new SnapshotResult(entity.DocumentId, entity.Document, entity.Version); + } + } + } + + public async Task> ReadAsync(DomainId key, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/ReadAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + var entity = await dbContext.Set().Where(x => x.DocumentId == key).FirstOrDefaultAsync(ct); + if (entity == null) + { + return new SnapshotResult(default, default!, EtagVersion.Empty); + } + + if (entity.Document is IOnRead onRead) + { + await onRead.OnReadAsync(); + } + + return new SnapshotResult(entity.DocumentId, entity.Document, entity.Version); + } + } + + public async Task RemoveAsync(DomainId key, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/RemoveAsync")) + { + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.DocumentId == key) + .ExecuteDeleteAsync(ct); + } + } + + public async Task WriteAsync(SnapshotWriteJob job, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/WriteAsync")) + { + var entity = CreateEntity(job.Key, job.Value, job.NewVersion); + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.UpsertAsync(entity, job.OldVersion, BuildUpdate, ct); + } + } + + public async Task WriteManyAsync(IEnumerable> jobs, + CancellationToken ct = default) + { + using (Telemetry.Activities.StartActivity("EFStateStore/WriteAsync")) + { + var entities = jobs.Select(x => CreateEntity(x.Key, x.Value, x.NewVersion)).ToList(); + if (entities.Count == 0) + { + return; + } + + await using var dbContext = await CreateDbContextAsync(ct); + await dbContext.BulkInsertOrUpdateAsync(entities, cancellationToken: ct); + } + } + + protected Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } + + protected virtual Expression, SetPropertyCalls>> BuildUpdate(TState entity) + { + return u => u + .SetProperty(x => x.Document, entity.Document) + .SetProperty(x => x.Version, entity.Version); + } + + protected static TState CreateEntity(DomainId id, T doc, long version) + { + var result = new TState + { + Document = doc, + DocumentId = id, + Version = version, + }; + + result.Prepare(); + + return result; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFState.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFState.cs new file mode 100644 index 000000000..c03d02612 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/States/EFState.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; + +namespace Squidex.Infrastructure.States; + +public class EFState : IVersionedEntity +{ + [Key] + public DomainId DocumentId { get; set; } + + [Json] + public T Document { get; set; } + + public long Version { get; set; } + + public virtual void Prepare() + { + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/TagsConverter.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/TagsConverter.cs new file mode 100644 index 000000000..af5a63281 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/TagsConverter.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure; + +public static class TagsConverter +{ + private const string Separator = "&&"; + + public static string FormatFilter(string value) + { + return $"{Separator}{value}{Separator}"; + } + + public static string ToString(this ICollection values) + { + if (values.Count == 0) + { + return string.Empty; + } + + return $"{Separator}{string.Join(Separator, values)}{Separator}"; + } + + public static HashSet ToSet(this string values) + { + if (string.IsNullOrEmpty(values)) + { + return []; + } + + return values.Split(Separator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToHashSet(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/UniqueContentIdConverter.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UniqueContentIdConverter.cs new file mode 100644 index 000000000..9bf99130d --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UniqueContentIdConverter.cs @@ -0,0 +1,31 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Contents.Text; + +namespace Squidex.Infrastructure; + +public static class UniqueContentIdConverter +{ + public static string ToParseableString(this UniqueContentId source) + { + return $"{source.AppId}__{source.ContentId}"; + } + + public static UniqueContentId ToUniqueContentId(this string source) + { + var separator = source.IndexOf("__", StringComparison.Ordinal); + if (separator < 0) + { + throw new ArgumentException("Invalid ID", nameof(source)); + } + + return new UniqueContentId( + DomainId.Create(source[..separator]), + DomainId.Create(source[(separator + 2)..])); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageBuilder.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageBuilder.cs new file mode 100644 index 000000000..ceed76763 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageBuilder.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.UsageTracking; + +namespace Microsoft.EntityFrameworkCore; + +public static class EFUsageBuilder +{ + public static void UseUsage(this ModelBuilder builder) + { + builder.Entity(b => + { + b.ToTable("Counter"); + }); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageCounterEntity.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageCounterEntity.cs new file mode 100644 index 000000000..04450ccfe --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageCounterEntity.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Squidex.Infrastructure.UsageTracking; + +[Table("Counter")] +[PrimaryKey(nameof(Key), nameof(Date), nameof(Category), nameof(CounterKey))] +public sealed class EFUsageCounterEntity +{ + public DateTime Date { get; set; } + + public string Key { get; set; } + + public string Category { get; set; } + + public string CounterKey { get; set; } + + public double CounterValue { get; set; } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageRepository.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageRepository.cs new file mode 100644 index 000000000..46780efd6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/UsageTracking/EFUsageRepository.cs @@ -0,0 +1,113 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; + +namespace Squidex.Infrastructure.UsageTracking; + +public class EFUsageRepository(IDbContextFactory dbContextFactory) : IUsageRepository where TContext : DbContext +{ + public async Task DeleteAsync(string key, + CancellationToken ct = default) + { + Guard.NotNullOrEmpty(key); + + await using var dbContext = await CreateDbContextAsync(ct); + + await dbContext.Set().Where(x => x.Key == key) + .ExecuteDeleteAsync(ct); + } + + public Task DeleteByKeyPatternAsync(string pattern, + CancellationToken ct = default) + { + return Task.CompletedTask; + } + + public async Task TrackUsagesAsync(UsageUpdate[] updates, + CancellationToken ct = default) + { + Guard.NotNull(updates); + + await using var dbContext = await CreateDbContextAsync(ct); + + var set = dbContext.Set(); + + foreach (var update in updates) + { + foreach (var (counterKey, counterValue) in update.Counters) + { + var date = update.Date.ToDateTime(default, DateTimeKind.Utc); + + var entity = new EFUsageCounterEntity + { + Key = update.Key, + Category = update.Category, + CounterKey = counterKey, + CounterValue = counterValue, + Date = date, + }; + + try + { + await set.AddAsync(entity, ct); + await dbContext.SaveChangesAsync(ct); + } + catch + { + var updateQuery = set.Where(existing => + existing.Key == update.Key && + existing.Category == update.Category && + existing.CounterKey == counterKey && + existing.Date == date); + + await updateQuery.ExecuteUpdateAsync(u => u + .SetProperty(x => x.CounterValue, x => x.CounterValue + counterValue), + ct); + } + } + } + } + + public async Task> QueryAsync(string key, DateOnly fromDate, DateOnly toDate, + CancellationToken ct = default) + { + Guard.NotNullOrEmpty(key); + + await using var dbContext = await CreateDbContextAsync(ct); + + var dateTimeFrom = fromDate.ToDateTime(default, DateTimeKind.Utc); + var dateTimeTo = toDate.ToDateTime(default, DateTimeKind.Utc); + + var entities = + await dbContext.Set() + .Where(x => x.Key == key) + .Where(x => x.Date >= dateTimeFrom && x.Date <= dateTimeTo) + .ToListAsync(ct); + + var result = entities + .GroupBy(x => new { x.Date, x.Category }) + .Select(group => + { + var counters = new Counters(); + foreach (var item in group) + { + counters[item.CounterKey] = item.CounterValue; + } + + return new StoredUsage(group.Key.Category, group.Key.Date.ToDateOnly(), counters); + }) + .ToList(); + + return result; + } + + protected Task CreateDbContextAsync(CancellationToken ct) + { + return dbContextFactory.CreateDbContextAsync(ct); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Extensions.cs new file mode 100644 index 000000000..e028ca64f --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Extensions.cs @@ -0,0 +1,45 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using System.Text; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.MySql; + +internal static class Extensions +{ + public static StringBuilder AppendJsonPath(this StringBuilder sb, PropertyPath path) + { + sb.Append('`'); + sb.Append(path[0]); + sb.Append("`, \'$"); + + foreach (var property in path.Skip(1)) + { + if (int.TryParse(property, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index)) + { + sb.Append(CultureInfo.InvariantCulture, $"[{index}]"); + } + else + { + sb.Append('.'); + sb.Append('"'); + sb.Append(property); + sb.Append('"'); + } + } + + sb.Append('\''); + return sb; + } + + public static string JsonPath(this PropertyPath path) + { + return new StringBuilder().AppendJsonPath(path).ToString(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.Designer.cs new file mode 100644 index 000000000..0e902d7d7 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.Designer.cs @@ -0,0 +1,1449 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Squidex.Providers.MySql; + +#nullable disable + +namespace Squidex.Providers.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20250204160706_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("Scopes") + .HasColumnType("longtext"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .HasColumnType("varchar(255)"); + + b.Property("AuthorizationId") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("RedemptionDate") + .HasColumnType("datetime(6)"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedCreated") + .HasColumnType("datetime(6)") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsProtected") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("json"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Job") + .IsRequired() + .HasColumnType("json"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("LastDump") + .HasColumnType("longtext"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("NextAttempt") + .HasColumnType("datetime(6)"); + + b.Property("NumCalls") + .HasColumnType("int"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAuthDomain") + .HasColumnType("longtext") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("json"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Category") + .HasColumnType("varchar(255)"); + + b.Property("CounterKey") + .HasColumnType("varchar(255)"); + + b.Property("CounterValue") + .HasColumnType("double"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TimeHandled") + .HasColumnType("datetime(6)"); + + b.Property("TimeToLive") + .HasColumnType("datetime(6)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("varchar(255)"); + + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("ValueFormat") + .HasColumnType("longtext"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.cs new file mode 100644 index 000000000..3e23f0721 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/20250204160706_Initial.cs @@ -0,0 +1,1238 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Squidex.Providers.MySql.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AssetFolders", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedAppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastModifiedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + LastModified = table.Column(type: "datetime(6)", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false), + ParentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FolderName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AssetFolders", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AssetKeyValueStore_TusMetadata", + columns: table => new + { + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Expires = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetKeyValueStore_TusMetadata", x => x.Key); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Assets", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedAppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastModifiedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + LastModified = table.Column(type: "datetime(6)", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false), + ParentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FileName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FileHash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + MimeType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Slug = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FileSize = table.Column(type: "bigint", nullable: false), + FileVersion = table.Column(type: "bigint", nullable: false), + TotalSize = table.Column(type: "bigint", nullable: false), + IsProtected = table.Column(type: "tinyint(1)", nullable: false), + Tags = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Metadata = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Assets", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Cache", + columns: table => new + { + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Expires = table.Column(type: "datetime(6)", nullable: false), + Value = table.Column(type: "longblob", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Cache", x => x.Key); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Chats", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastUpdated = table.Column(type: "datetime(6)", nullable: false), + Version = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_Chats", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ContentReferencesAll", + columns: table => new + { + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FromKey = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ToId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesAll", x => new { x.AppId, x.FromKey, x.ToId }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ContentReferencesPublished", + columns: table => new + { + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FromKey = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ToId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesPublished", x => new { x.AppId, x.FromKey, x.ToId }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ContentsAll", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastModifiedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + LastModified = table.Column(type: "datetime(6)", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false), + SchemaId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NewStatus = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Data = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ScheduleJob = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedAppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedSchemaId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ScheduledAt = table.Column(type: "datetime(6)", nullable: true), + NewData = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + TranslationStatus = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsAll", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ContentsPublished", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastModifiedBy = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + LastModified = table.Column(type: "datetime(6)", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "tinyint(1)", nullable: false), + SchemaId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NewStatus = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Data = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ScheduleJob = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedAppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IndexedSchemaId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ScheduledAt = table.Column(type: "datetime(6)", nullable: true), + NewData = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + TranslationStatus = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsPublished", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Counter", + columns: table => new + { + Date = table.Column(type: "datetime(6)", nullable: false), + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Category = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CounterKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CounterValue = table.Column(type: "double", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Counter", x => new { x.Key, x.Date, x.Category, x.CounterKey }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "EventPosition", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false), + Position = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EventPosition", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Events", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EventStream = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EventStreamOffset = table.Column(type: "bigint", nullable: false), + EventsCount = table.Column(type: "bigint", nullable: false), + Events = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Timestamp = table.Column(type: "datetime(6)", nullable: false), + Position = table.Column(type: "bigint", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Events", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "HistoryEvent", + columns: table => new + { + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + OwnerId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + Actor = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false), + Channel = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EventType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Parameters = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_HistoryEvent", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Messages", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ChannelName = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + QueueName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + MessageData = table.Column(type: "longblob", nullable: false), + MessageHeaders = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TimeToLive = table.Column(type: "datetime(6)", nullable: false), + TimeHandled = table.Column(type: "datetime(6)", nullable: true), + Version = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK_Messages", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MessagingData", + columns: table => new + { + Group = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ValueType = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ValueFormat = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ValueData = table.Column(type: "longblob", nullable: false), + Expiration = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MessagingData", x => new { x.Group, x.Key }); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Migrations", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + IsLocked = table.Column(type: "tinyint(1)", nullable: false), + Version = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Migrations", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApplicationId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyToken = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationDate = table.Column(type: "datetime(6)", nullable: true), + Properties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Scopes = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Subject = table.Column(type: "varchar(400)", maxLength: 400, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Requests", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Key = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Timestamp = table.Column(type: "datetime(6)", nullable: false), + Properties = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Requests", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "RuleEvents", + columns: table => new + { + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RuleId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Created = table.Column(type: "datetime(6)", nullable: false), + LastModified = table.Column(type: "datetime(6)", nullable: false), + Result = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + JobResult = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Job = table.Column(type: "json", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastDump = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NumCalls = table.Column(type: "int", nullable: false), + Expires = table.Column(type: "datetime(6)", nullable: false), + NextAttempt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RuleEvents", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_App", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserIds = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TeamId = table.Column(type: "varchar(255)", maxLength: 255, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Deleted = table.Column(type: "tinyint(1)", nullable: false), + Created = table.Column(type: "datetime(6)", nullable: false), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_App", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Counters", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Counters", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_EventConsumerState", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_EventConsumerState", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Identity_Keys", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Keys", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Identity_Xml", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Xml", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Index_TagHistory", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_TagHistory", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Index_Tags", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_Tags", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_JobsState", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_JobsState", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Names", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Names", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Rule", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Deleted = table.Column(type: "tinyint(1)", nullable: false), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Rule", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Schema", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + AppId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Id = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Deleted = table.Column(type: "tinyint(1)", nullable: false), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Schema", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_Team", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UserIds = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Deleted = table.Column(type: "tinyint(1)", nullable: false), + AuthDomain = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Team", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_UISettings", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UISettings", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_UsageNotifications", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageNotifications", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "States_UsageTracker", + columns: table => new + { + DocumentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Document = table.Column(type: "json", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageTracker", x => x.DocumentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "TextState", + columns: table => new + { + UniqueContentId = table.Column(type: "varchar(255)", maxLength: 255, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + State = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_TextState", x => x.UniqueContentId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "YDotNetDocument", + columns: table => new + { + Id = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Data = table.Column(type: "longblob", nullable: false), + Expiration = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_YDotNetDocument", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RoleId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ApplicationId = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + AuthorizationId = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyToken = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + CreationDate = table.Column(type: "datetime(6)", nullable: true), + ExpirationDate = table.Column(type: "datetime(6)", nullable: true), + Payload = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Properties = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + RedemptionDate = table.Column(type: "datetime(6)", nullable: true), + ReferenceId = table.Column(type: "varchar(100)", maxLength: 100, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Status = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Subject = table.Column(type: "varchar(400)", maxLength: 400, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Type = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AssetFolders_IndexedAppId_Id", + table: "AssetFolders", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_AssetKeyValueStore_TusMetadata_Expires", + table: "AssetKeyValueStore_TusMetadata", + column: "Expires"); + + migrationBuilder.CreateIndex( + name: "IX_Assets_IndexedAppId_Id", + table: "Assets", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_Chats_LastUpdated", + table: "Chats", + column: "LastUpdated"); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_EventStreamOffset", + table: "Events", + columns: new[] { "EventStream", "EventStreamOffset" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Position", + table: "Events", + columns: new[] { "EventStream", "Position" }); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Timestamp", + table: "Events", + columns: new[] { "EventStream", "Timestamp" }); + + migrationBuilder.CreateIndex( + name: "IX_Messages_ChannelName_TimeHandled", + table: "Messages", + columns: new[] { "ChannelName", "TimeHandled" }); + + migrationBuilder.CreateIndex( + name: "IX_MessagingData_Expiration", + table: "MessagingData", + column: "Expiration"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Requests_Key", + table: "Requests", + column: "Key"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AssetFolders"); + + migrationBuilder.DropTable( + name: "AssetKeyValueStore_TusMetadata"); + + migrationBuilder.DropTable( + name: "Assets"); + + migrationBuilder.DropTable( + name: "Cache"); + + migrationBuilder.DropTable( + name: "Chats"); + + migrationBuilder.DropTable( + name: "ContentReferencesAll"); + + migrationBuilder.DropTable( + name: "ContentReferencesPublished"); + + migrationBuilder.DropTable( + name: "ContentsAll"); + + migrationBuilder.DropTable( + name: "ContentsPublished"); + + migrationBuilder.DropTable( + name: "Counter"); + + migrationBuilder.DropTable( + name: "EventPosition"); + + migrationBuilder.DropTable( + name: "Events"); + + migrationBuilder.DropTable( + name: "HistoryEvent"); + + migrationBuilder.DropTable( + name: "Messages"); + + migrationBuilder.DropTable( + name: "MessagingData"); + + migrationBuilder.DropTable( + name: "Migrations"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens"); + + migrationBuilder.DropTable( + name: "Requests"); + + migrationBuilder.DropTable( + name: "RuleEvents"); + + migrationBuilder.DropTable( + name: "States_App"); + + migrationBuilder.DropTable( + name: "States_Counters"); + + migrationBuilder.DropTable( + name: "States_EventConsumerState"); + + migrationBuilder.DropTable( + name: "States_Identity_Keys"); + + migrationBuilder.DropTable( + name: "States_Identity_Xml"); + + migrationBuilder.DropTable( + name: "States_Index_TagHistory"); + + migrationBuilder.DropTable( + name: "States_Index_Tags"); + + migrationBuilder.DropTable( + name: "States_JobsState"); + + migrationBuilder.DropTable( + name: "States_Names"); + + migrationBuilder.DropTable( + name: "States_Rule"); + + migrationBuilder.DropTable( + name: "States_Schema"); + + migrationBuilder.DropTable( + name: "States_Team"); + + migrationBuilder.DropTable( + name: "States_UISettings"); + + migrationBuilder.DropTable( + name: "States_UsageNotifications"); + + migrationBuilder.DropTable( + name: "States_UsageTracker"); + + migrationBuilder.DropTable( + name: "TextState"); + + migrationBuilder.DropTable( + name: "YDotNetDocument"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations"); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/MySqlDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 000000000..22132178e --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,1446 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Squidex.Providers.MySql; + +#nullable disable + +namespace Squidex.Providers.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("Scopes") + .HasColumnType("longtext"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .HasColumnType("varchar(255)"); + + b.Property("AuthorizationId") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("RedemptionDate") + .HasColumnType("datetime(6)"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedCreated") + .HasColumnType("datetime(6)") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsProtected") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("json"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Job") + .IsRequired() + .HasColumnType("json"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("LastDump") + .HasColumnType("longtext"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("NextAttempt") + .HasColumnType("datetime(6)"); + + b.Property("NumCalls") + .HasColumnType("int"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAuthDomain") + .HasColumnType("longtext") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("json"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Category") + .HasColumnType("varchar(255)"); + + b.Property("CounterKey") + .HasColumnType("varchar(255)"); + + b.Property("CounterValue") + .HasColumnType("double"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TimeHandled") + .HasColumnType("datetime(6)"); + + b.Property("TimeToLive") + .HasColumnType("datetime(6)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("varchar(255)"); + + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("ValueFormat") + .HasColumnType("longtext"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContext.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContext.cs new file mode 100644 index 000000000..3d13b8aa6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContext.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Infrastructure.Json; + +namespace Squidex.Providers.MySql; + +public sealed class MySqlDbContext(DbContextOptions options, IJsonSerializer jsonSerializer) + : AppDbContext(options, jsonSerializer) +{ + protected override string? JsonColumnType() + { + return "json"; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContextDesignTimeFactory.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContextDesignTimeFactory.cs new file mode 100644 index 000000000..be488f6fe --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDbContextDesignTimeFactory.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Squidex.Infrastructure.Json.System; + +namespace Squidex.Providers.MySql; + +public sealed class MySqlDbContextDesignTimeFactory : IDesignTimeDbContextFactory +{ + public MySqlDbContext CreateDbContext(string[] args) + { + const string ConnectionString = "Server=localhost;Port=33060;Database=test;User=mysql;Password=mysql"; + + var builder = new DbContextOptionsBuilder() + .UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString)); + + return new MySqlDbContext(builder.Options, new SystemJsonSerializer(JsonSerializerOptions.Default)); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs new file mode 100644 index 000000000..0f8a6e552 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDialect.cs @@ -0,0 +1,100 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.MySql; + +public sealed class MySqlDialect : SqlDialect +{ + public static readonly SqlDialect Instance = new MySqlDialect(); + + private MySqlDialect() + { + } + + public override string FormatLimitOffset(long limit, long offset, bool hasOrder) + { + var hasLimit = limit > 0 && limit < long.MaxValue; + + if (offset > 0) + { + return $"LIMIT {limit} OFFSET {offset}"; + } + + if (offset > 0) + { + return $"LIMIT 18446744073709551615 OFFSET {offset}"; + } + + if (hasLimit) + { + return $"LIMIT {limit}"; + } + + return string.Empty; + } + + protected override string FormatTable(string tableName) + { + return $"`{tableName}`"; + } + + protected override object FormatRawValue(object value, CompareOperator op) + { + switch (value) + { + case true: + return 1; + case false: + return 0; + default: + return base.FormatRawValue(value, op); + } + } + + public override string OrderBy(PropertyPath path, SortOrder order, bool isJson) + { + if (isJson) + { + var sqlOrder = FormatOrder(order); + var sqlPath = path.JsonPath(); + + return $"IF(JSON_TYPE(JSON_EXTRACT({sqlPath})) IN ('INTEGER', 'DOUBLE', 'DECIMAL'), CAST(JSON_VALUE({sqlPath}) AS DOUBLE), NULL) {sqlOrder}, JSON_VALUE({sqlPath}) {sqlOrder}"; + } + + return base.OrderBy(path, order, isJson); + } + + public override string Where(PropertyPath path, CompareOperator op, ClrValue value, SqlParams queryParameters, bool isJson) + { + if (isJson) + { + var isBoolean = value.ValueType is ClrValueType.Boolean; + if (isBoolean) + { + var sqlPath = path.JsonPath(); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + return $"IF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + } + } + + return base.Where(path, op, value, queryParameters, isJson); + } + + protected override string FormatField(PropertyPath path, bool isJson) + { + if (isJson && path.Count > 1) + { + return $"JSON_VALUE({path.JsonPath()})"; + } + + return $"`{path[0]}`"; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Extensions.cs new file mode 100644 index 000000000..c19667a38 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Extensions.cs @@ -0,0 +1,53 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using System.Text; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.Postgres; + +public static class Extensions +{ + public static StringBuilder AppendJsonPath(this StringBuilder sb, PropertyPath path, bool asString) + { + sb.Append('"'); + sb.Append(path[0]); + sb.Append('"'); + + var i = 1; + foreach (var property in path.Skip(1)) + { + if (i == path.Count - 1 && asString) + { + sb.Append("->>"); + } + else + { + sb.Append("->"); + } + + if (int.TryParse(property, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index)) + { + sb.Append(index); + } + else + { + sb.Append($"'{property}'"); + } + + i++; + } + + return sb; + } + + public static string JsonPath(this PropertyPath path, bool asString) + { + return new StringBuilder().AppendJsonPath(path, asString).ToString(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.Designer.cs new file mode 100644 index 000000000..d404a1496 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.Designer.cs @@ -0,0 +1,1449 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Squidex.Providers.Postgres; + +#nullable disable + +namespace Squidex.Providers.Postgres.Migrations +{ + [DbContext(typeof(PostgresDbContext))] + [Migration("20250204160712_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsProtected") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Job") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("LastDump") + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("NextAttempt") + .HasColumnType("timestamp with time zone"); + + b.Property("NumCalls") + .HasColumnType("integer"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAuthDomain") + .HasColumnType("text") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("text"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("IsLocked") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("CounterKey") + .HasColumnType("text"); + + b.Property("CounterValue") + .HasColumnType("double precision"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("text"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("text"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TimeHandled") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeToLive") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ValueFormat") + .HasColumnType("text"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.cs new file mode 100644 index 000000000..3c906c4b7 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/20250204160712_Initial.cs @@ -0,0 +1,1012 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Squidex.Providers.Postgres.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AssetFolders", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IndexedAppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastModified = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + ParentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + FolderName = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetFolders", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "AssetKeyValueStore_TusMetadata", + columns: table => new + { + Key = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: false), + Expires = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetKeyValueStore_TusMetadata", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "Assets", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IndexedAppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastModified = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + ParentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + FileName = table.Column(type: "text", nullable: false), + FileHash = table.Column(type: "text", nullable: false), + MimeType = table.Column(type: "text", nullable: false), + Slug = table.Column(type: "text", nullable: false), + FileSize = table.Column(type: "bigint", nullable: false), + FileVersion = table.Column(type: "bigint", nullable: false), + TotalSize = table.Column(type: "bigint", nullable: false), + IsProtected = table.Column(type: "boolean", nullable: false), + Tags = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: false), + Metadata = table.Column(type: "jsonb", nullable: false), + Type = table.Column(type: "character varying(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Assets", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "Cache", + columns: table => new + { + Key = table.Column(type: "text", nullable: false), + Expires = table.Column(type: "timestamp with time zone", nullable: false), + Value = table.Column(type: "bytea", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Cache", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "Chats", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: false), + LastUpdated = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Chats", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ContentReferencesAll", + columns: table => new + { + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + FromKey = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + ToId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesAll", x => new { x.AppId, x.FromKey, x.ToId }); + }); + + migrationBuilder.CreateTable( + name: "ContentReferencesPublished", + columns: table => new + { + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + FromKey = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + ToId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesPublished", x => new { x.AppId, x.FromKey, x.ToId }); + }); + + migrationBuilder.CreateTable( + name: "ContentsAll", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastModified = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + SchemaId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + NewStatus = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Data = table.Column(type: "jsonb", nullable: false), + ScheduleJob = table.Column(type: "jsonb", nullable: true), + IndexedAppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IndexedSchemaId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + ScheduledAt = table.Column(type: "timestamp with time zone", nullable: true), + NewData = table.Column(type: "jsonb", nullable: true), + TranslationStatus = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsAll", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "ContentsPublished", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastModified = table.Column(type: "timestamp with time zone", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "boolean", nullable: false), + SchemaId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + NewStatus = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Data = table.Column(type: "jsonb", nullable: false), + ScheduleJob = table.Column(type: "jsonb", nullable: true), + IndexedAppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + IndexedSchemaId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + ScheduledAt = table.Column(type: "timestamp with time zone", nullable: true), + NewData = table.Column(type: "jsonb", nullable: true), + TranslationStatus = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsPublished", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "Counter", + columns: table => new + { + Date = table.Column(type: "timestamp with time zone", nullable: false), + Key = table.Column(type: "text", nullable: false), + Category = table.Column(type: "text", nullable: false), + CounterKey = table.Column(type: "text", nullable: false), + CounterValue = table.Column(type: "double precision", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Counter", x => new { x.Key, x.Date, x.Category, x.CounterKey }); + }); + + migrationBuilder.CreateTable( + name: "EventPosition", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false), + Position = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EventPosition", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Events", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + EventStream = table.Column(type: "text", nullable: false), + EventStreamOffset = table.Column(type: "bigint", nullable: false), + EventsCount = table.Column(type: "bigint", nullable: false), + Events = table.Column(type: "text[]", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Position = table.Column(type: "bigint", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Events", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HistoryEvent", + columns: table => new + { + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + OwnerId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + Actor = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Version = table.Column(type: "bigint", nullable: false), + Channel = table.Column(type: "text", nullable: false), + EventType = table.Column(type: "text", nullable: false), + Parameters = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HistoryEvent", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Messages", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ChannelName = table.Column(type: "text", nullable: false), + QueueName = table.Column(type: "text", nullable: false), + MessageData = table.Column(type: "bytea", nullable: false), + MessageHeaders = table.Column(type: "text", nullable: false), + TimeToLive = table.Column(type: "timestamp with time zone", nullable: false), + TimeHandled = table.Column(type: "timestamp with time zone", nullable: true), + Version = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Messages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "MessagingData", + columns: table => new + { + Group = table.Column(type: "text", nullable: false), + Key = table.Column(type: "text", nullable: false), + ValueType = table.Column(type: "text", nullable: false), + ValueFormat = table.Column(type: "text", nullable: true), + ValueData = table.Column(type: "bytea", nullable: false), + Expiration = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MessagingData", x => new { x.Group, x.Key }); + }); + + migrationBuilder.CreateTable( + name: "Migrations", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false), + IsLocked = table.Column(type: "boolean", nullable: false), + Version = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Migrations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: false), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + Properties = table.Column(type: "text", nullable: true), + Scopes = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Requests", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Key = table.Column(type: "text", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Properties = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Requests", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "RuleEvents", + columns: table => new + { + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + RuleId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + LastModified = table.Column(type: "timestamp with time zone", nullable: false), + Result = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + JobResult = table.Column(type: "character varying(100)", maxLength: 100, nullable: false), + Job = table.Column(type: "jsonb", nullable: false), + LastDump = table.Column(type: "text", nullable: true), + NumCalls = table.Column(type: "integer", nullable: false), + Expires = table.Column(type: "timestamp with time zone", nullable: false), + NextAttempt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RuleEvents", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "States_App", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Name = table.Column(type: "text", nullable: false), + UserIds = table.Column(type: "text", nullable: false), + TeamId = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), + Deleted = table.Column(type: "boolean", nullable: false), + Created = table.Column(type: "timestamp with time zone", nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_App", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Counters", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Counters", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_EventConsumerState", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_EventConsumerState", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Identity_Keys", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Keys", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Identity_Xml", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Xml", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Index_TagHistory", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_TagHistory", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Index_Tags", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_Tags", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_JobsState", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_JobsState", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Names", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Names", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Rule", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Deleted = table.Column(type: "boolean", nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Rule", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Schema", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Name = table.Column(type: "text", nullable: false), + Deleted = table.Column(type: "boolean", nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Schema", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Team", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + UserIds = table.Column(type: "text", nullable: false), + Deleted = table.Column(type: "boolean", nullable: false), + AuthDomain = table.Column(type: "text", nullable: true), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Team", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UISettings", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UISettings", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UsageNotifications", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageNotifications", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UsageTracker", + columns: table => new + { + DocumentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "jsonb", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageTracker", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "TextState", + columns: table => new + { + UniqueContentId = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), + State = table.Column(type: "character varying(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TextState", x => x.UniqueContentId); + }); + + migrationBuilder.CreateTable( + name: "YDotNetDocument", + columns: table => new + { + Id = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Data = table.Column(type: "bytea", nullable: false), + Expiration = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_YDotNetDocument", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "text", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "text", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "text", nullable: false), + RoleId = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "text", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: true), + AuthorizationId = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + ExpirationDate = table.Column(type: "timestamp with time zone", nullable: true), + Payload = table.Column(type: "text", nullable: true), + Properties = table.Column(type: "text", nullable: true), + RedemptionDate = table.Column(type: "timestamp with time zone", nullable: true), + ReferenceId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AssetFolders_IndexedAppId_Id", + table: "AssetFolders", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_AssetKeyValueStore_TusMetadata_Expires", + table: "AssetKeyValueStore_TusMetadata", + column: "Expires"); + + migrationBuilder.CreateIndex( + name: "IX_Assets_IndexedAppId_Id", + table: "Assets", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_Chats_LastUpdated", + table: "Chats", + column: "LastUpdated"); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_EventStreamOffset", + table: "Events", + columns: new[] { "EventStream", "EventStreamOffset" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Position", + table: "Events", + columns: new[] { "EventStream", "Position" }); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Timestamp", + table: "Events", + columns: new[] { "EventStream", "Timestamp" }); + + migrationBuilder.CreateIndex( + name: "IX_Messages_ChannelName_TimeHandled", + table: "Messages", + columns: new[] { "ChannelName", "TimeHandled" }); + + migrationBuilder.CreateIndex( + name: "IX_MessagingData_Expiration", + table: "MessagingData", + column: "Expiration"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Requests_Key", + table: "Requests", + column: "Key"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AssetFolders"); + + migrationBuilder.DropTable( + name: "AssetKeyValueStore_TusMetadata"); + + migrationBuilder.DropTable( + name: "Assets"); + + migrationBuilder.DropTable( + name: "Cache"); + + migrationBuilder.DropTable( + name: "Chats"); + + migrationBuilder.DropTable( + name: "ContentReferencesAll"); + + migrationBuilder.DropTable( + name: "ContentReferencesPublished"); + + migrationBuilder.DropTable( + name: "ContentsAll"); + + migrationBuilder.DropTable( + name: "ContentsPublished"); + + migrationBuilder.DropTable( + name: "Counter"); + + migrationBuilder.DropTable( + name: "EventPosition"); + + migrationBuilder.DropTable( + name: "Events"); + + migrationBuilder.DropTable( + name: "HistoryEvent"); + + migrationBuilder.DropTable( + name: "Messages"); + + migrationBuilder.DropTable( + name: "MessagingData"); + + migrationBuilder.DropTable( + name: "Migrations"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens"); + + migrationBuilder.DropTable( + name: "Requests"); + + migrationBuilder.DropTable( + name: "RuleEvents"); + + migrationBuilder.DropTable( + name: "States_App"); + + migrationBuilder.DropTable( + name: "States_Counters"); + + migrationBuilder.DropTable( + name: "States_EventConsumerState"); + + migrationBuilder.DropTable( + name: "States_Identity_Keys"); + + migrationBuilder.DropTable( + name: "States_Identity_Xml"); + + migrationBuilder.DropTable( + name: "States_Index_TagHistory"); + + migrationBuilder.DropTable( + name: "States_Index_Tags"); + + migrationBuilder.DropTable( + name: "States_JobsState"); + + migrationBuilder.DropTable( + name: "States_Names"); + + migrationBuilder.DropTable( + name: "States_Rule"); + + migrationBuilder.DropTable( + name: "States_Schema"); + + migrationBuilder.DropTable( + name: "States_Team"); + + migrationBuilder.DropTable( + name: "States_UISettings"); + + migrationBuilder.DropTable( + name: "States_UsageNotifications"); + + migrationBuilder.DropTable( + name: "States_UsageTracker"); + + migrationBuilder.DropTable( + name: "TextState"); + + migrationBuilder.DropTable( + name: "YDotNetDocument"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations"); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/PostgresDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/PostgresDbContextModelSnapshot.cs new file mode 100644 index 000000000..481a27c52 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/Migrations/PostgresDbContextModelSnapshot.cs @@ -0,0 +1,1446 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Squidex.Providers.Postgres; + +#nullable disable + +namespace Squidex.Providers.Postgres.Migrations +{ + [DbContext(typeof(PostgresDbContext))] + partial class PostgresDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsProtected") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Job") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("LastDump") + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("NextAttempt") + .HasColumnType("timestamp with time zone"); + + b.Property("NumCalls") + .HasColumnType("integer"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAuthDomain") + .HasColumnType("text") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("text"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("IsLocked") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("CounterKey") + .HasColumnType("text"); + + b.Property("CounterValue") + .HasColumnType("double precision"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("text"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("text"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TimeHandled") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeToLive") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("text"); + + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ValueFormat") + .HasColumnType("text"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContext.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContext.cs new file mode 100644 index 000000000..6455b6bec --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContext.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Infrastructure.Json; + +namespace Squidex.Providers.Postgres; + +public class PostgresDbContext(DbContextOptions options, IJsonSerializer jsonSerializer) + : AppDbContext(options, jsonSerializer) +{ + protected override string? JsonColumnType() + { + return "jsonb"; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContextDesignTimeFactory.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContextDesignTimeFactory.cs new file mode 100644 index 000000000..1619914d2 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDbContextDesignTimeFactory.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Squidex.Infrastructure.Json.System; + +namespace Squidex.Providers.Postgres; + +public sealed class PostgresDbContextDesignTimeFactory : IDesignTimeDbContextFactory +{ + public PostgresDbContext CreateDbContext(string[] args) + { + const string ConnectionString = "Server=localhost;Port=54320;Database=test;User=postgres;Password=postgres"; + + var builder = new DbContextOptionsBuilder() + .UseNpgsql(ConnectionString); + + return new PostgresDbContext(builder.Options, new SystemJsonSerializer(JsonSerializerOptions.Default)); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs new file mode 100644 index 000000000..085cb04ac --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDialect.cs @@ -0,0 +1,79 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.Postgres; + +public class PostgresDialect : SqlDialect +{ + public static readonly SqlDialect Instance = new PostgresDialect(); + + private PostgresDialect() + { + } + + protected override string FormatTable(string tableName) + { + return $"\"{tableName}\""; + } + + public override string OrderBy(PropertyPath path, SortOrder order, bool isJson) + { + if (isJson) + { + var sqlOrder = FormatOrder(order); + var sqlPath = path.JsonPath(true); + + return $"CASE WHEN jsonb_typeof({path.JsonPath(false)}) = 'number' THEN ({sqlPath})::numeric END {sqlOrder} NULLS LAST, {sqlPath} {sqlOrder}"; + } + + return base.OrderBy(path, order, isJson); + } + + public override string Where(PropertyPath path, CompareOperator op, ClrValue value, SqlParams queryParameters, bool isJson) + { + if (isJson) + { + var issNumeric = value.ValueType is + ClrValueType.Single or + ClrValueType.Double or + ClrValueType.Int32 or + ClrValueType.Int64; + if (issNumeric) + { + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + return $"(CASE WHEN jsonb_typeof({path.JsonPath(false)}) = 'number' THEN ({path.JsonPath(true)})::numeric {sqlOp} {sqlRhs} ELSE FALSE END)"; + } + + var isBoolean = value.ValueType is ClrValueType.Boolean; + if (isBoolean) + { + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + return $"(CASE WHEN jsonb_typeof({path.JsonPath(false)}) = 'boolean' THEN ({path.JsonPath(true)})::boolean {sqlOp} {sqlRhs} ELSE FALSE END)"; + } + } + + return base.Where(path, op, value, queryParameters, isJson); + } + + protected override string FormatField(PropertyPath path, bool isJson) + { + var baseField = path[0]; + + if (isJson && path.Count > 1) + { + return path.JsonPath(true); + } + + return $"\"{baseField}\""; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Extensions.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Extensions.cs new file mode 100644 index 000000000..87cfa1375 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Extensions.cs @@ -0,0 +1,45 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using System.Text; +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.SqlServer; + +internal static class Extensions +{ + public static StringBuilder AppendJsonPath(this StringBuilder sb, PropertyPath path) + { + sb.Append('['); + sb.Append(path[0]); + sb.Append("], \'$"); + + foreach (var property in path.Skip(1)) + { + if (int.TryParse(property, NumberStyles.Integer, CultureInfo.InvariantCulture, out var index)) + { + sb.Append(CultureInfo.InvariantCulture, $"[{index}]"); + } + else + { + sb.Append('.'); + sb.Append('"'); + sb.Append(property); + sb.Append('"'); + } + } + + sb.Append('\''); + return sb; + } + + public static string JsonPath(this PropertyPath path) + { + return new StringBuilder().AppendJsonPath(path).ToString(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.Designer.cs new file mode 100644 index 000000000..8e37837d6 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.Designer.cs @@ -0,0 +1,1452 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Squidex.Providers.SqlServer; + +#nullable disable + +namespace Squidex.Providers.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + [Migration("20250204160718_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("LastUpdated") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedCreated") + .HasColumnType("datetimeoffset") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProtected") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Job") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LastDump") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("NextAttempt") + .HasColumnType("datetimeoffset"); + + b.Property("NumCalls") + .HasColumnType("int"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAuthDomain") + .HasColumnType("nvarchar(max)") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Category") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterKey") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterValue") + .HasColumnType("float"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TimeHandled") + .HasColumnType("datetime2"); + + b.Property("TimeToLive") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("nvarchar(450)"); + + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ValueFormat") + .HasColumnType("nvarchar(max)"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.cs new file mode 100644 index 000000000..5d18ed577 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/20250204160718_Initial.cs @@ -0,0 +1,1014 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Squidex.Providers.SqlServer.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AssetFolders", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IndexedAppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + LastModified = table.Column(type: "datetimeoffset", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "bit", nullable: false), + ParentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + FolderName = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetFolders", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "AssetKeyValueStore_TusMetadata", + columns: table => new + { + Key = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: false), + Expires = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetKeyValueStore_TusMetadata", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "Assets", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IndexedAppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + LastModified = table.Column(type: "datetimeoffset", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "bit", nullable: false), + ParentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + FileName = table.Column(type: "nvarchar(max)", nullable: false), + FileHash = table.Column(type: "nvarchar(max)", nullable: false), + MimeType = table.Column(type: "nvarchar(max)", nullable: false), + Slug = table.Column(type: "nvarchar(max)", nullable: false), + FileSize = table.Column(type: "bigint", nullable: false), + FileVersion = table.Column(type: "bigint", nullable: false), + TotalSize = table.Column(type: "bigint", nullable: false), + IsProtected = table.Column(type: "bit", nullable: false), + Tags = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: false), + Metadata = table.Column(type: "nvarchar(max)", nullable: false), + Type = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Assets", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "Cache", + columns: table => new + { + Key = table.Column(type: "nvarchar(450)", nullable: false), + Expires = table.Column(type: "datetime2", nullable: false), + Value = table.Column(type: "varbinary(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Cache", x => x.Key); + }); + + migrationBuilder.CreateTable( + name: "Chats", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: false), + LastUpdated = table.Column(type: "datetime2", nullable: false), + Version = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Chats", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ContentReferencesAll", + columns: table => new + { + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + FromKey = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + ToId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesAll", x => new { x.AppId, x.FromKey, x.ToId }); + }); + + migrationBuilder.CreateTable( + name: "ContentReferencesPublished", + columns: table => new + { + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + FromKey = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + ToId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentReferencesPublished", x => new { x.AppId, x.FromKey, x.ToId }); + }); + + migrationBuilder.CreateTable( + name: "ContentsAll", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + LastModified = table.Column(type: "datetimeoffset", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "bit", nullable: false), + SchemaId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + NewStatus = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: false), + ScheduleJob = table.Column(type: "nvarchar(max)", nullable: true), + IndexedAppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IndexedSchemaId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + ScheduledAt = table.Column(type: "datetimeoffset", nullable: true), + NewData = table.Column(type: "nvarchar(max)", nullable: true), + TranslationStatus = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsAll", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "ContentsPublished", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + LastModifiedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + LastModified = table.Column(type: "datetimeoffset", nullable: false), + Version = table.Column(type: "bigint", nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IsDeleted = table.Column(type: "bit", nullable: false), + SchemaId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + NewStatus = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Data = table.Column(type: "nvarchar(max)", nullable: false), + ScheduleJob = table.Column(type: "nvarchar(max)", nullable: true), + IndexedAppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + IndexedSchemaId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + ScheduledAt = table.Column(type: "datetimeoffset", nullable: true), + NewData = table.Column(type: "nvarchar(max)", nullable: true), + TranslationStatus = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ContentsPublished", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "Counter", + columns: table => new + { + Date = table.Column(type: "datetime2", nullable: false), + Key = table.Column(type: "nvarchar(450)", nullable: false), + Category = table.Column(type: "nvarchar(450)", nullable: false), + CounterKey = table.Column(type: "nvarchar(450)", nullable: false), + CounterValue = table.Column(type: "float", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Counter", x => new { x.Key, x.Date, x.Category, x.CounterKey }); + }); + + migrationBuilder.CreateTable( + name: "EventPosition", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false), + Position = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EventPosition", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Events", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + EventStream = table.Column(type: "nvarchar(450)", nullable: false), + EventStreamOffset = table.Column(type: "bigint", nullable: false), + EventsCount = table.Column(type: "bigint", nullable: false), + Events = table.Column(type: "nvarchar(max)", nullable: false), + Timestamp = table.Column(type: "datetime2", nullable: false), + Position = table.Column(type: "bigint", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Events", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "HistoryEvent", + columns: table => new + { + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + OwnerId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + Actor = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Version = table.Column(type: "bigint", nullable: false), + Channel = table.Column(type: "nvarchar(max)", nullable: false), + EventType = table.Column(type: "nvarchar(max)", nullable: false), + Parameters = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_HistoryEvent", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Messages", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + ChannelName = table.Column(type: "nvarchar(450)", nullable: false), + QueueName = table.Column(type: "nvarchar(max)", nullable: false), + MessageData = table.Column(type: "varbinary(max)", nullable: false), + MessageHeaders = table.Column(type: "nvarchar(max)", nullable: false), + TimeToLive = table.Column(type: "datetime2", nullable: false), + TimeHandled = table.Column(type: "datetime2", nullable: true), + Version = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Messages", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "MessagingData", + columns: table => new + { + Group = table.Column(type: "nvarchar(450)", nullable: false), + Key = table.Column(type: "nvarchar(450)", nullable: false), + ValueType = table.Column(type: "nvarchar(max)", nullable: false), + ValueFormat = table.Column(type: "nvarchar(max)", nullable: true), + ValueData = table.Column(type: "varbinary(max)", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MessagingData", x => new { x.Group, x.Key }); + }); + + migrationBuilder.CreateTable( + name: "Migrations", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + IsLocked = table.Column(type: "bit", nullable: false), + Version = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Migrations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + ApplicationId = table.Column(type: "nvarchar(450)", nullable: false), + ConcurrencyToken = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "datetime2", nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + Scopes = table.Column(type: "nvarchar(max)", nullable: true), + Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Requests", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Key = table.Column(type: "nvarchar(450)", nullable: false), + Timestamp = table.Column(type: "datetimeoffset", nullable: false), + Properties = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Requests", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "RuleEvents", + columns: table => new + { + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + RuleId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + LastModified = table.Column(type: "datetimeoffset", nullable: false), + Result = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + JobResult = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + Job = table.Column(type: "nvarchar(max)", nullable: false), + LastDump = table.Column(type: "nvarchar(max)", nullable: true), + NumCalls = table.Column(type: "int", nullable: false), + Expires = table.Column(type: "datetimeoffset", nullable: false), + NextAttempt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RuleEvents", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "States_App", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + UserIds = table.Column(type: "nvarchar(max)", nullable: false), + TeamId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: true), + Deleted = table.Column(type: "bit", nullable: false), + Created = table.Column(type: "datetimeoffset", nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_App", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Counters", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Counters", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_EventConsumerState", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_EventConsumerState", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Identity_Keys", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Keys", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Identity_Xml", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Identity_Xml", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Index_TagHistory", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_TagHistory", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Index_Tags", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Index_Tags", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_JobsState", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_JobsState", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Names", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Names", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Rule", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Deleted = table.Column(type: "bit", nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Rule", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Schema", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + AppId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Id = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Deleted = table.Column(type: "bit", nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Schema", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_Team", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + UserIds = table.Column(type: "nvarchar(max)", nullable: false), + Deleted = table.Column(type: "bit", nullable: false), + AuthDomain = table.Column(type: "nvarchar(max)", nullable: true), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_Team", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UISettings", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UISettings", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UsageNotifications", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageNotifications", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "States_UsageTracker", + columns: table => new + { + DocumentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + Document = table.Column(type: "nvarchar(max)", nullable: true), + Version = table.Column(type: "bigint", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_States_UsageTracker", x => x.DocumentId); + }); + + migrationBuilder.CreateTable( + name: "TextState", + columns: table => new + { + UniqueContentId = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + State = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_TextState", x => x.UniqueContentId); + }); + + migrationBuilder.CreateTable( + name: "YDotNetDocument", + columns: table => new + { + Id = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Data = table.Column(type: "varbinary(max)", nullable: false), + Expiration = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_YDotNetDocument", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + ApplicationId = table.Column(type: "nvarchar(450)", nullable: true), + AuthorizationId = table.Column(type: "nvarchar(450)", nullable: true), + ConcurrencyToken = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "datetime2", nullable: true), + ExpirationDate = table.Column(type: "datetime2", nullable: true), + Payload = table.Column(type: "nvarchar(max)", nullable: true), + Properties = table.Column(type: "nvarchar(max)", nullable: true), + RedemptionDate = table.Column(type: "datetime2", nullable: true), + ReferenceId = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "nvarchar(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AssetFolders_IndexedAppId_Id", + table: "AssetFolders", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_AssetKeyValueStore_TusMetadata_Expires", + table: "AssetKeyValueStore_TusMetadata", + column: "Expires"); + + migrationBuilder.CreateIndex( + name: "IX_Assets_IndexedAppId_Id", + table: "Assets", + columns: new[] { "IndexedAppId", "Id" }); + + migrationBuilder.CreateIndex( + name: "IX_Chats_LastUpdated", + table: "Chats", + column: "LastUpdated"); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_EventStreamOffset", + table: "Events", + columns: new[] { "EventStream", "EventStreamOffset" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Position", + table: "Events", + columns: new[] { "EventStream", "Position" }); + + migrationBuilder.CreateIndex( + name: "IX_Events_EventStream_Timestamp", + table: "Events", + columns: new[] { "EventStream", "Timestamp" }); + + migrationBuilder.CreateIndex( + name: "IX_Messages_ChannelName_TimeHandled", + table: "Messages", + columns: new[] { "ChannelName", "TimeHandled" }); + + migrationBuilder.CreateIndex( + name: "IX_MessagingData_Expiration", + table: "MessagingData", + column: "Expiration"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true, + filter: "[ReferenceId] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Requests_Key", + table: "Requests", + column: "Key"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AssetFolders"); + + migrationBuilder.DropTable( + name: "AssetKeyValueStore_TusMetadata"); + + migrationBuilder.DropTable( + name: "Assets"); + + migrationBuilder.DropTable( + name: "Cache"); + + migrationBuilder.DropTable( + name: "Chats"); + + migrationBuilder.DropTable( + name: "ContentReferencesAll"); + + migrationBuilder.DropTable( + name: "ContentReferencesPublished"); + + migrationBuilder.DropTable( + name: "ContentsAll"); + + migrationBuilder.DropTable( + name: "ContentsPublished"); + + migrationBuilder.DropTable( + name: "Counter"); + + migrationBuilder.DropTable( + name: "EventPosition"); + + migrationBuilder.DropTable( + name: "Events"); + + migrationBuilder.DropTable( + name: "HistoryEvent"); + + migrationBuilder.DropTable( + name: "Messages"); + + migrationBuilder.DropTable( + name: "MessagingData"); + + migrationBuilder.DropTable( + name: "Migrations"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens"); + + migrationBuilder.DropTable( + name: "Requests"); + + migrationBuilder.DropTable( + name: "RuleEvents"); + + migrationBuilder.DropTable( + name: "States_App"); + + migrationBuilder.DropTable( + name: "States_Counters"); + + migrationBuilder.DropTable( + name: "States_EventConsumerState"); + + migrationBuilder.DropTable( + name: "States_Identity_Keys"); + + migrationBuilder.DropTable( + name: "States_Identity_Xml"); + + migrationBuilder.DropTable( + name: "States_Index_TagHistory"); + + migrationBuilder.DropTable( + name: "States_Index_Tags"); + + migrationBuilder.DropTable( + name: "States_JobsState"); + + migrationBuilder.DropTable( + name: "States_Names"); + + migrationBuilder.DropTable( + name: "States_Rule"); + + migrationBuilder.DropTable( + name: "States_Schema"); + + migrationBuilder.DropTable( + name: "States_Team"); + + migrationBuilder.DropTable( + name: "States_UISettings"); + + migrationBuilder.DropTable( + name: "States_UsageNotifications"); + + migrationBuilder.DropTable( + name: "States_UsageTracker"); + + migrationBuilder.DropTable( + name: "TextState"); + + migrationBuilder.DropTable( + name: "YDotNetDocument"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations"); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs new file mode 100644 index 000000000..cfacf8314 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs @@ -0,0 +1,1449 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Squidex.Providers.SqlServer; + +#nullable disable + +namespace Squidex.Providers.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + partial class SqlServerDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.12") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("LastUpdated") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedCreated") + .HasColumnType("datetimeoffset") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FileHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProtected") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEventEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Job") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("JobResult") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LastDump") + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("NextAttempt") + .HasColumnType("datetimeoffset"); + + b.Property("NumCalls") + .HasColumnType("int"); + + b.Property("Result") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("RuleId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.ToTable("RuleEvents"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAuthDomain") + .HasColumnType("nvarchar(max)") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EventStream") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Category") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterKey") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterValue") + .HasColumnType("float"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ChannelName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("MessageHeaders") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("QueueName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TimeHandled") + .HasColumnType("datetime2"); + + b.Property("TimeToLive") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasColumnType("nvarchar(450)"); + + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ValueFormat") + .HasColumnType("nvarchar(max)"); + + b.Property("ValueType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContext.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContext.cs new file mode 100644 index 000000000..fa1c3c7e5 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContext.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Infrastructure.Json; + +namespace Squidex.Providers.SqlServer; + +public sealed class SqlServerDbContext(DbContextOptions options, IJsonSerializer jsonSerializer) + : AppDbContext(options, jsonSerializer) +{ +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContextDesignTimeFactory.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContextDesignTimeFactory.cs new file mode 100644 index 000000000..a3dbbb37b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDbContextDesignTimeFactory.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Squidex.Infrastructure.Json.System; + +namespace Squidex.Providers.SqlServer; + +public sealed class SqlServerDbContextDesignTimeFactory : IDesignTimeDbContextFactory +{ + public SqlServerDbContext CreateDbContext(string[] args) + { + const string ConnectionString = "Server=localhost;Port=14330;Database=test;User=sa;Password=sqlserver"; + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(ConnectionString); + + return new SqlServerDbContext(builder.Options, new SystemJsonSerializer(JsonSerializerOptions.Default)); + } +} \ No newline at end of file diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs new file mode 100644 index 000000000..e8ad9cff5 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDialect.cs @@ -0,0 +1,112 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Queries; + +namespace Squidex.Providers.SqlServer; + +public sealed class SqlServerDialect : SqlDialect +{ + public static readonly SqlDialect Instance = new SqlServerDialect(); + + private SqlServerDialect() + { + } + + public override string FormatLimitOffset(long limit, long offset, bool hasOrder) + { + var hasLimit = limit > 0 && limit < long.MaxValue; + + if (hasLimit) + { + return $"OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY"; + } + + if (offset > 0 || hasOrder) + { + return $"OFFSET {offset} ROWS"; + } + + return string.Empty; + } + + protected override string FormatTable(string tableName) + { + return $"[{tableName}]"; + } + + public override string OrderBy(PropertyPath path, SortOrder order, bool isJson) + { + if (isJson) + { + var sqlOrder = FormatOrder(order); + var sqlPath = path.JsonPath(); + + return $"IIF(ISNUMERIC(JSON_VALUE({sqlPath})) = 1, CAST(JSON_VALUE({sqlPath}) AS NUMERIC), NULL) {sqlOrder}, JSON_VALUE({sqlPath}) {sqlOrder}"; + } + + return base.OrderBy(path, order, isJson); + } + + public override string Where(PropertyPath path, CompareOperator op, ClrValue value, SqlParams queryParameters, bool isJson) + { + if (isJson) + { + var issNumeric = value.ValueType is + ClrValueType.Single or + ClrValueType.Double or + ClrValueType.Int32 or + ClrValueType.Int64; + if (issNumeric) + { + var sqlPath = path.JsonPath(); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + return $"TRY_CAST(JSON_VALUE({sqlPath}) AS NUMERIC) {sqlOp} {sqlRhs}"; + } + + var isBoolean = value.ValueType is ClrValueType.Boolean; + if (isBoolean) + { + var sqlPath = path.JsonPath(); + var sqlOp = FormatOperator(op, value); + var sqlRhs = FormatValues(op, value, queryParameters); + + return $"IIF(JSON_VALUE({sqlPath}) = 'true', 1, 0) {sqlOp} {sqlRhs}"; + } + + var isNull = value.ValueType is ClrValueType.Null; + if (isNull) + { + var sqlPath = path.JsonPath(); + + if (op == CompareOperator.Equals) + { + return $"JSON_QUERY({sqlPath}) IS NULL AND JSON_VALUE({sqlPath}) IS NULL"; + } + + if (op == CompareOperator.NotEquals) + { + return $"JSON_QUERY({sqlPath}) IS NOT NULL OR JSON_VALUE({sqlPath}) IS NOT NULL"; + } + } + } + + return base.Where(path, op, value, queryParameters, isJson); + } + + protected override string FormatField(PropertyPath path, bool isJson) + { + if (isJson && path.Count > 1) + { + return $"JSON_VALUE({path.JsonPath()})"; + } + + return $"[{path[0]}]"; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs new file mode 100644 index 000000000..7d135858e --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs @@ -0,0 +1,252 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Squidex.AI; +using Squidex.Assets.TusAdapter; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.Teams; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Apps.Repositories; +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.Domain.Apps.Entities.Contents.Text.State; +using Squidex.Domain.Apps.Entities.History; +using Squidex.Domain.Apps.Entities.History.Repositories; +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Repositories; +using Squidex.Domain.Apps.Entities.Teams; +using Squidex.Domain.Apps.Entities.Teams.Repositories; +using Squidex.Domain.Users; +using Squidex.Infrastructure.Caching; +using Squidex.Infrastructure.Log; +using Squidex.Infrastructure.Migrations; +using Squidex.Infrastructure.States; +using Squidex.Infrastructure.UsageTracking; +using Squidex.Messaging; +using Squidex.Providers.MySql; +using Squidex.Providers.Postgres; +using Squidex.Providers.SqlServer; +using YDotNet.Server.EntityFramework; + +namespace Squidex; + +public static class ServiceExtensions +{ + public static void AddSquidexEntityFramework(this IServiceCollection services, IConfiguration config) + { + var connectionString = config.GetRequiredValue("store:sql:connectionString"); + + config.ConfigureByOption("store:sql:provider", new Alternatives + { + ["MySql"] = () => + { + services.AddDbContextFactory(builder => + { + var versionString = config.GetOptionalValue("store:sql:version"); + + var version = + !string.IsNullOrWhiteSpace(versionString) ? + ServerVersion.Parse(versionString) : + ServerVersion.AutoDetect(connectionString); + + builder.ConfigureWarnings(w => w.Ignore(CoreEventId.CollectionWithoutComparer)); + builder.UseMySql(connectionString, version, mysql => + { + mysql.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }); + + services.AddSingleton(typeof(ISnapshotStore<>), typeof(MySqlSnapshotStore<>)); + services.AddSingleton(MySqlDialect.Instance); + services.AddSquidexEntityFramework(config); + }, + ["Postgres"] = () => + { + services.AddDbContextFactory(builder => + { + builder.ConfigureWarnings(w => w.Ignore(CoreEventId.CollectionWithoutComparer)); + builder.UseNpgsql(connectionString); + }); + + services.AddSingleton(typeof(ISnapshotStore<>), typeof(PostgresSnapshotStore<>)); + services.AddSingleton(PostgresDialect.Instance); + services.AddSquidexEntityFramework(config); + }, + ["SqlServer"] = () => + { + services.AddDbContextFactory(builder => + { + builder.ConfigureWarnings(w => w.Ignore(CoreEventId.CollectionWithoutComparer)); + builder.UseSqlServer(connectionString); + }); + + services.AddSingleton(typeof(ISnapshotStore<>), typeof(SqlServerSnapshotStore<>)); + services.AddSingleton(SqlServerDialect.Instance); + services.AddSquidexEntityFramework(config); + } + }); + } + + private static void AddSquidexEntityFramework(this IServiceCollection services, IConfiguration config) where TContext : AppDbContext + { + if (config.GetValue("store:sql:runMigration")) + { + services.AddSingletonAs>(); + } + + services.AddOpenIddict() + .AddCore(builder => + { + builder.UseEntityFrameworkCore() + .UseDbContext(); + }); + + services.AddIdentityCore() + .AddEntityFrameworkStores(); + + services.AddYDotNet() + .AddEntityFrameworkStorage(); + + services.AddAI() + .AddEntityFrameworkChatStore(); + + services.AddMessaging() + .AddEntityFrameworkDataStore(config); + + services.AddSingletonAs>() + .As(); + + services.AddSingletonAs>() + .As(); + + services.AddHealthChecks() + .AddDbContextCheck(); + + services.AddSingletonAs>() + .As().As>(); + + services.AddSingletonAs>() + .As().As>().As(); + + services.AddSingletonAs>() + .As().As>().As(); + + services.AddSingletonAs>() + .As().As>().As(); + + services.AddSingletonAs>() + .As().As(); + + services.AddSingletonAs>() + .As(); + + services.AddSingletonAs>() + .As().As>().As(); + + services.AddSingletonAs>() + .As().As(); + + services.AddSingletonAs>() + .As().As>().As().As(); + + services.AddSingletonAs>() + .As().As>(); + + services.AddSingletonAs>() + .As().As(); + + services.AddSingletonAs>() + .As(); + + services.AddSingletonAs() + .As(); + + services.TryAddSingleton(); + + services.AddEntityFrameworkAssetKeyValueStore(); + } + + public static void AddSquidexEntityFrameworkEventStore(this IServiceCollection services, IConfiguration config) + { + config.ConfigureByOption("store:sql:provider", new Alternatives + { + ["MySql"] = () => + { + services.AddEntityFrameworkEventStore(config) + .AddMysqlAdapter(); + }, + ["Postgres"] = () => + { + services.AddEntityFrameworkEventStore(config) + .AddPostgresAdapter(); + }, + ["SqlServer"] = () => + { + services.AddEntityFrameworkEventStore(config) + .AddSqlServerAdapter(); + }, + }); + } + + public static MessagingBuilder AddSquidexEntityFrameworkTransport(this MessagingBuilder messaging, IConfiguration config) + { + config.ConfigureByOption("store:sql:provider", new Alternatives + { + ["MySql"] = () => + { + messaging.AddEntityFrameworkTransport(config); + }, + ["Postgres"] = () => + { + messaging.AddEntityFrameworkTransport(config); + }, + ["SqlServer"] = () => + { + messaging.AddEntityFrameworkTransport(config); + }, + }); + + return messaging; + } + + public class MySqlSnapshotStore(IDbContextFactory dbContextFactory) + : EFSnapshotStore>(dbContextFactory) + { + } + + public class PostgresSnapshotStore(IDbContextFactory dbContextFactory) + : EFSnapshotStore>(dbContextFactory) + { + } + + public class SqlServerSnapshotStore(IDbContextFactory dbContextFactory) + : EFSnapshotStore>(dbContextFactory) + { + } + + public static IdentityBuilder AddIdentityCore(this IServiceCollection services) + { + return new IdentityBuilder(typeof(IdentityUser), typeof(IdentityRole), services); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj new file mode 100644 index 000000000..90027c373 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj @@ -0,0 +1,50 @@ + + + net8.0 + Squidex + latest + enable + enable + + + full + True + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.sln b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.sln new file mode 100644 index 000000000..f8988fd48 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Data.EntityFramework", "Squidex.Data.EntityFramework.csproj", "{D57960A9-D84C-06B8-3267-BE8E0B999F36}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D57960A9-D84C-06B8-3267-BE8E0B999F36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D57960A9-D84C-06B8-3267-BE8E0B999F36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D57960A9-D84C-06B8-3267-BE8E0B999F36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D57960A9-D84C-06B8-3267-BE8E0B999F36}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6AB9E82C-07C3-4D4E-9CB2-AB42853A1CCB} + EndGlobalSection +EndGlobal diff --git a/backend/src/Squidex.Data.EntityFramework/docker-compose.yml b/backend/src/Squidex.Data.EntityFramework/docker-compose.yml new file mode 100644 index 000000000..87a2caaed --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/docker-compose.yml @@ -0,0 +1,31 @@ +services: + migration_postgres: + image: postgres + environment: + - POSTGRES_DB=test + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + ports: + - "54320:5432" + + migration_mysql: + image: mysql + environment: + - MYSQL_DATABASE=test + - MYSQL_USER=mysql + - MYSQL_PASSWORD=mysql + - MYSQL_ROOT_PASSWORD=mysql + ports: + - "33060:3306" + volumes: + - mysql:/var/lib/mysql + + migration_sqlserver: + image: mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04 + environment: + - SA_PASSWORD=sqlserver + - ACCEPT_EULA=Y + ports: + - "14330:1433" +volumes: + mysql: \ No newline at end of file diff --git a/backend/src/Squidex.Data.EntityFramework/run-migration.ps1 b/backend/src/Squidex.Data.EntityFramework/run-migration.ps1 new file mode 100644 index 000000000..e2235d6c9 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/run-migration.ps1 @@ -0,0 +1,22 @@ +# Stop the script when a cmdlet or a native command fails +$ErrorActionPreference = 'Stop' + +$migrationName = $args[0] + +dotnet ef migrations add $migrationName --context MysqlDbContext -o Providers/MySql/Migrations +if (!$?) { + Write-Error "Creating migration failed for MySql" + exit 1 +} + +dotnet ef migrations add $migrationName --context PostgresDbContext -o Providers/Postgres/Migrations +if (!$?) { + Write-Error "Creating migration failed for Postgres" + exit 1 +} + +dotnet ef migrations add $migrationName --context SqlServerDbContext -o Providers/SqlServer/Migrations +if (!$?) { + Write-Error "Creating migration failed for SqlServer" + exit 1 +} \ No newline at end of file diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/AppEntityClassMap.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/AppEntityClassMap.cs new file mode 100644 index 000000000..63a6ee793 --- /dev/null +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/AppEntityClassMap.cs @@ -0,0 +1,30 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using MongoDB.Bson.Serialization; +using Squidex.Domain.Apps.Core; + +namespace Squidex.Domain.Apps.Entities; + +public static class AppEntityClassMap +{ + public static void Register() + { + EntityClassMap.Register(); + + BsonClassMap.TryRegisterClassMap(cm => + { + cm.MapProperty(x => x.AppId) + .SetElementName("ai") + .SetIsRequired(true); + + cm.MapProperty(x => x.IsDeleted) + .SetElementName("dl") + .SetIgnoreIfDefault(false); + }); + } +} diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppEntity.cs index d46ce6105..d935ac216 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppEntity.cs @@ -39,7 +39,7 @@ public sealed class MongoAppEntity : MongoState { var users = new HashSet { - Document.CreatedBy.Identifier + Document.CreatedBy.Identifier, }; users.AddRange(Document.Contributors.Keys); diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppRepository.cs index cf0fd46c7..fa9adf737 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Apps/MongoAppRepository.cs @@ -33,7 +33,7 @@ public sealed class MongoAppRepository(IMongoDatabase database) : MongoSnapshotS .Ascending(x => x.IndexedUserIds)), new CreateIndexModel( Index - .Ascending(x => x.IndexedTeamId)) + .Ascending(x => x.IndexedTeamId)), ], ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/AssetItemClassMap.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/AssetItemClassMap.cs index 54f1fec2c..a819347f9 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/AssetItemClassMap.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/AssetItemClassMap.cs @@ -14,13 +14,13 @@ internal static class AssetItemClassMap { public static void Register() { + AppEntityClassMap.Register(); + BsonClassMap.TryRegisterClassMap(cm => { cm.MapProperty(x => x.ParentId) .SetElementName("pi") .SetIgnoreIfDefault(true); }); - - EntityClassMap.Register(); } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetEntity.cs index a9cbdccb5..fa46014b0 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetEntity.cs @@ -21,6 +21,8 @@ public record MongoAssetEntity : Asset, IVersionedEntity public static void RegisterClassMap() { + AssetItemClassMap.Register(); + BsonClassMap.TryRegisterClassMap(cm => { cm.MapProperty(x => x.DocumentId) @@ -78,13 +80,6 @@ public record MongoAssetEntity : Asset, IVersionedEntity .SetElementName("at") .SetIsRequired(true); }); - - AssetItemClassMap.Register(); - } - - public Asset ToState() - { - return this; } public static MongoAssetEntity Create(SnapshotWriteJob job) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderEntity.cs index 6112dfe8c..d826ddf12 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderEntity.cs @@ -42,11 +42,6 @@ public record MongoAssetFolderEntity : AssetFolder, IVersionedEntity AssetItemClassMap.Register(); } - public AssetFolder ToState() - { - return this; - } - public static MongoAssetFolderEntity Create(SnapshotWriteJob job) { var entity = new MongoAssetFolderEntity diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository.cs index 608cad584..6d32b4a8e 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository.cs @@ -34,7 +34,7 @@ public sealed partial class MongoAssetFolderRepository(IMongoDatabase database) Index .Ascending(x => x.IndexedAppId) .Ascending(x => x.ParentId) - .Ascending(x => x.IsDeleted)) + .Ascending(x => x.IsDeleted)), ], ct); } @@ -90,7 +90,7 @@ public sealed partial class MongoAssetFolderRepository(IMongoDatabase database) var filters = new List> { Filter.Eq(x => x.IndexedAppId, appId), - Filter.Ne(x => x.IsDeleted, true) + Filter.Ne(x => x.IsDeleted, true), }; if (parentId != null) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository_SnapshotStore.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository_SnapshotStore.cs index e20d62bf5..cc63a8dbc 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetFolderRepository_SnapshotStore.cs @@ -29,7 +29,7 @@ public sealed partial class MongoAssetFolderRepository : ISnapshotStore new SnapshotResult(x.DocumentId, x.ToState(), x.Version, true)); + return documents.Select(x => new SnapshotResult(x.DocumentId, x, x.Version, true)); } async Task> ISnapshotStore.ReadAsync(DomainId key, @@ -43,7 +43,7 @@ public sealed partial class MongoAssetFolderRepository : ISnapshotStore(existing.DocumentId, existing.ToState(), existing.Version); + return new SnapshotResult(existing.DocumentId, existing, existing.Version); } return new SnapshotResult(default, null!, EtagVersion.Empty); @@ -71,7 +71,7 @@ public sealed partial class MongoAssetFolderRepository : ISnapshotStore y.DocumentId, x.DocumentId), x) { - IsUpsert = true + IsUpsert = true, }).ToList(); if (updates.Count == 0) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository.cs index b38c64d7e..6f6a7ed24 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository.cs @@ -71,7 +71,7 @@ public sealed partial class MongoAssetRepository : MongoRepositoryBase( Index .Ascending(x => x.Id) - .Ascending(x => x.IsDeleted)) + .Ascending(x => x.IsDeleted)), ], ct); } @@ -80,15 +80,9 @@ public sealed partial class MongoAssetRepository : MongoRepositoryBase x.IndexedAppId == appId && !x.IsDeleted); - using (var cursor = await find.ToCursorAsync(ct)) + await foreach (var entity in find.ToAsyncEnumerable(ct).WithCancellation(ct)) { - while (await cursor.MoveNextAsync(ct)) - { - foreach (var entity in cursor.Current) - { - yield return entity; - } - } + yield return entity; } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository_SnapshotStore.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository_SnapshotStore.cs index 6e86ca64c..c07cfea5c 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/MongoAssetRepository_SnapshotStore.cs @@ -29,7 +29,7 @@ public sealed partial class MongoAssetRepository : ISnapshotStore, IDelet { var documents = Collection.Find(FindAll, Batching.Options).ToAsyncEnumerable(ct); - return documents.Select(x => new SnapshotResult(x.DocumentId, x.ToState(), x.Version)); + return documents.Select(x => new SnapshotResult(x.DocumentId, x, x.Version)); } async Task> ISnapshotStore.ReadAsync(DomainId key, @@ -43,7 +43,7 @@ public sealed partial class MongoAssetRepository : ISnapshotStore, IDelet if (existing != null) { - return new SnapshotResult(existing.DocumentId, existing.ToState(), existing.Version); + return new SnapshotResult(existing.DocumentId, existing, existing.Version); } return new SnapshotResult(default, null!, EtagVersion.Empty); @@ -71,7 +71,7 @@ public sealed partial class MongoAssetRepository : ISnapshotStore, IDelet Filter.Eq(y => y.DocumentId, x.DocumentId), x) { - IsUpsert = true + IsUpsert = true, }).ToList(); if (updates.Count == 0) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/Visitors/FindExtensions.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/Visitors/FindExtensions.cs index 043ad189b..3905a3eb5 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/Visitors/FindExtensions.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Assets/Visitors/FindExtensions.cs @@ -41,7 +41,7 @@ public static class FindExtensions { Filter.Ne(x => x.LastModified, default), Filter.Ne(x => x.Id, default), - Filter.Eq(x => x.IndexedAppId, appId) + Filter.Eq(x => x.IndexedAppId, appId), }; var isDefault = false; diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/BsonUniqueContentIdSerializer.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/BsonUniqueContentIdSerializer.cs index 60dddc590..254df99c0 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/BsonUniqueContentIdSerializer.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/BsonUniqueContentIdSerializer.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Text; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; using Squidex.Domain.Apps.Entities.Contents.Text; @@ -22,40 +21,6 @@ public partial class BsonUniqueContentIdSerializer : SerializerBase= IdInfo.LongIdIndicator) { ThrowHelper.InvalidOperationException("App ID cannot be longer than 253 bytes."); diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Extensions.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Extensions.cs new file mode 100644 index 000000000..a03cf8dcc --- /dev/null +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Extensions.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; + +namespace Squidex.Domain.Apps.Entities.Contents; + +internal static class Extensions +{ + public static bool ShouldWritePublished(this WriteContent content) + { + return content.CurrentVersion.Status == Status.Published && !content.IsDeleted; + } +} diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentCollection.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentCollection.cs index 371055be8..b15ef76a7 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentCollection.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentCollection.cs @@ -70,7 +70,7 @@ public sealed class MongoContentCollection : MongoRepositoryBase x.CreateIndexes()), ct); } - public Task ResetScheduledAsync(DomainId appId, DomainId contentId, + public Task ResetScheduledAsync(DomainId appId, DomainId id, CancellationToken ct) { - var documentId = DomainId.Combine(appId, contentId); + var documentId = DomainId.Combine(appId, id); return Collection.UpdateOneAsync( x => x.DocumentId == documentId, @@ -127,10 +127,10 @@ public sealed class MongoContentCollection : MongoRepositoryBase StreamIds(DomainId appId, DomainId schemaId, + public IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, CancellationToken ct) { - return queryAsStream.StreamAllIds(appId, schemaId, ct); + return queryAsStream.StreamAllIds(appId, schemaIds, ct); } public async Task DeleteAppAsync(DomainId appId, @@ -151,7 +151,19 @@ public sealed class MongoContentCollection : MongoRepositoryBase> QueryAsync(App app, List schemas, Q q, + public Task> QueryAsync(App app, Schema schema, Q q, + CancellationToken ct) + { + return QueryAsync(app, [schema], true, q, ct); + } + + public Task> QueryAsync(App app, List schemas, Q q, + CancellationToken ct) + { + return QueryAsync(app, schemas, false, q, ct); + } + + private async Task> QueryAsync(App app, List schemas, bool isSingle, Q q, CancellationToken ct) { using (Telemetry.Activities.StartActivity("MongoContentCollection/QueryAsync")) @@ -178,47 +190,17 @@ public sealed class MongoContentCollection : MongoRepositoryBase(); - } - catch (MongoCommandException ex) when (ex.Code == 96) - { - throw new DomainException(T.Get("common.resultTooLarge")); - } - catch (MongoQueryException ex) when (ex.Message.Contains("17406", StringComparison.Ordinal)) - { - throw new DomainException(T.Get("common.resultTooLarge")); - } - } - } - - public async Task> QueryAsync(App app, Schema schema, Q q, - CancellationToken ct) - { - using (Telemetry.Activities.StartActivity("MongoContentCollection/QueryAsync")) - { - try - { - if (q.Ids is { Count: > 0 }) - { - return await queryByIds.QueryAsync(app, [schema], q, ct); - } - - if (q.ScheduledFrom != null && q.ScheduledTo != null) + if (isSingle && queryInDedicatedCollection != null) { - return await queryScheduled.QueryAsync(app, [schema], q, ct); + return await queryInDedicatedCollection.QueryAsync(schemas.Single(), q, ct); } - if (q.Referencing != default) + if (isSingle) { - return await queryReferences.QueryAsync(app, [schema], q, ct); + return await queryByQuery.QueryAsync(schemas.Single(), q, ct); } - if (queryInDedicatedCollection != null) - { - return await queryInDedicatedCollection.QueryAsync(schema, q, ct); - } - - return await queryByQuery.QueryAsync(schema, q, ct); + return ResultList.Empty(); } catch (MongoCommandException ex) when (ex.Code == 96) { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentEntity.cs index 881031c95..1ec136b0e 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentEntity.cs @@ -34,6 +34,8 @@ public record MongoContentEntity : Content, IVersionedEntity public static void RegisterClassMap() { + AppEntityClassMap.Register(); + BsonClassMap.TryRegisterClassMap(cm => { cm.MapProperty(x => x.DocumentId) @@ -112,7 +114,7 @@ public record MongoContentEntity : Content, IVersionedEntity NewVersion = new ContentVersion(NewStatus.Value, Data), ScheduleJob = ScheduleJob, SchemaId = SchemaId, - Version = Version + Version = Version, }; } else @@ -130,7 +132,7 @@ public record MongoContentEntity : Content, IVersionedEntity NewVersion = null, ScheduleJob = ScheduleJob, SchemaId = SchemaId, - Version = Version + Version = Version, }; } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository.cs index d8588e8be..7435e9235 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository.cs @@ -32,11 +32,19 @@ public partial class MongoContentRepository( : MongoBase, IContentRepository, IInitializable { private readonly MongoContentCollection collectionComplete = - new MongoContentCollection($"States_Contents_All3{shardKey}", database, log, - ReadPreference.Primary, options.Value.OptimizeForSelfHosting); + new MongoContentCollection( + $"States_Contents_All3{shardKey}", + database, + log, + ReadPreference.Primary, + options.Value.OptimizeForSelfHosting); private readonly MongoContentCollection collectionPublished = - new MongoContentCollection($"States_Contents_Published3{shardKey}", database, log, - ReadPreference.Secondary, options.Value.OptimizeForSelfHosting); + new MongoContentCollection( + $"States_Contents_Published3{shardKey}", + database, + log, + ReadPreference.Secondary, + options.Value.OptimizeForSelfHosting); private readonly ContentsOptions options = options.Value; public bool CanUseTransactions { get; private set; } @@ -80,10 +88,10 @@ public partial class MongoContentRepository( return GetCollection(scope).QueryScheduledWithoutDataAsync(now, ct); } - public IAsyncEnumerable StreamIds(DomainId appId, DomainId schemaId, SearchScope scope, + public IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, SearchScope scope, CancellationToken ct = default) { - return GetCollection(scope).StreamIds(appId, schemaId, ct); + return GetCollection(scope).StreamIds(appId, schemaIds, ct); } public Task> QueryAsync(App app, List schemas, Q q, SearchScope scope, @@ -122,10 +130,10 @@ public partial class MongoContentRepository( return GetCollection(scope).QueryIdsAsync(app, schema, filterNode, ct); } - public Task ResetScheduledAsync(DomainId appId, DomainId contentId, SearchScope scope, + public Task ResetScheduledAsync(DomainId appId, DomainId id, SearchScope scope, CancellationToken ct = default) { - return GetCollection(SearchScope.All).ResetScheduledAsync(appId, contentId, ct); + return GetCollection(SearchScope.All).ResetScheduledAsync(appId, id, ct); } public Task CreateIndexAsync(DomainId appId, DomainId schemaId, IndexDefinition index, diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository_SnapshotStore.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository_SnapshotStore.cs index 0a396c043..dbd78a5ce 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository_SnapshotStore.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoContentRepository_SnapshotStore.cs @@ -30,13 +30,10 @@ public partial class MongoContentRepository : ISnapshotStore, IDel { using (Telemetry.Activities.StartActivity("MongoContentRepository/ReadAsync")) { - var existing = - await collectionComplete.FindAsync(key, ct); - - // Support for all versions, where we do not have full snapshots in the collection. - if (existing?.IsSnapshot == true) + var entity = await collectionComplete.FindAsync(key, ct); + if (entity?.IsSnapshot == true) { - return new SnapshotResult(existing.DocumentId, existing.ToState(), existing.Version); + return new SnapshotResult(entity.DocumentId, entity.ToState(), entity.Version); } return new SnapshotResult(default, null!, EtagVersion.Empty); @@ -68,12 +65,6 @@ public partial class MongoContentRepository : ISnapshotStore, IDel { using (Telemetry.Activities.StartActivity("MongoContentRepository/RemoveAsync")) { - // Some data is corrupt and might throw an exception if we do not ignore it. - if (key == DomainId.Empty) - { - return; - } - await Task.WhenAll( collectionComplete.RemoveAsync(key, ct), collectionPublished.RemoveAsync(key, ct)); @@ -83,14 +74,14 @@ public partial class MongoContentRepository : ISnapshotStore, IDel async Task ISnapshotStore.WriteAsync(SnapshotWriteJob job, CancellationToken ct) { - using (Telemetry.Activities.StartActivity("MongoContentRepository/WriteAsync")) + // Some data is corrupt and might throw an exception if we do not ignore it. + if (!IsValid(job.Value)) { - // Some data is corrupt and might throw an exception if we do not ignore it. - if (!IsValid(job.Value)) - { - return; - } + return; + } + using (Telemetry.Activities.StartActivity("MongoContentRepository/WriteAsync")) + { if (!CanUseTransactions) { // If transactions are not supported we update the documents without version checks, @@ -117,6 +108,8 @@ public partial class MongoContentRepository : ISnapshotStore, IDel async Task ISnapshotStore.WriteManyAsync(IEnumerable> jobs, CancellationToken ct) { + var validJobs = jobs.Where(x => IsValid(x.Value)).ToList(); + using (Telemetry.Activities.StartActivity("MongoContentRepository/WriteManyAsync")) { var collectionUpdates = new Dictionary, List>(); @@ -128,26 +121,21 @@ public partial class MongoContentRepository : ISnapshotStore, IDel foreach (var job in jobs) { - var isValid = IsValid(job.Value); - - if (isValid && ShouldWritePublished(job.Value)) + if (job.Value.ShouldWritePublished()) { await collectionPublished.AddCollectionsAsync( await MongoContentEntity.CreatePublishedAsync(job, appProvider, ct), add, ct); } - if (isValid) - { - await collectionComplete.AddCollectionsAsync( - await MongoContentEntity.CreateCompleteAsync(job, appProvider, ct), add, ct); - } + await collectionComplete.AddCollectionsAsync( + await MongoContentEntity.CreateCompleteAsync(job, appProvider, ct), add, ct); } var parallelOptions = new ParallelOptions { CancellationToken = ct, // This is just an estimate, but we do not want ot have unlimited parallelism. - MaxDegreeOfParallelism = 8 + MaxDegreeOfParallelism = 8, }; // Make one update per collection. @@ -161,7 +149,7 @@ public partial class MongoContentRepository : ISnapshotStore, IDel private async Task UpsertPublishedAsync(SnapshotWriteJob job, CancellationToken ct) { - if (ShouldWritePublished(job.Value)) + if (job.Value.ShouldWritePublished()) { var entityJob = job.As(await MongoContentEntity.CreatePublishedAsync(job, appProvider, ct)); @@ -176,7 +164,7 @@ public partial class MongoContentRepository : ISnapshotStore, IDel private async Task UpsertVersionedPublishedAsync(IClientSessionHandle session, SnapshotWriteJob job, CancellationToken ct) { - if (ShouldWritePublished(job.Value)) + if (job.Value.ShouldWritePublished()) { var entityJob = job.As(await MongoContentEntity.CreatePublishedAsync(job, appProvider, ct)); @@ -204,12 +192,6 @@ public partial class MongoContentRepository : ISnapshotStore, IDel await collectionComplete.UpsertVersionedAsync(session, entityJob, ct); } - private static bool ShouldWritePublished(WriteContent value) - { - // Only published content is written to the published collection. - return value.CurrentVersion.Status == Status.Published && !value.IsDeleted; - } - private static bool IsValid(WriteContent state) { // Some data is corrupt and might throw an exception during migration if we do not skip them. diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoShardedContentRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoShardedContentRepository.cs index db651d74a..73b20253d 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoShardedContentRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/MongoShardedContentRepository.cs @@ -18,7 +18,8 @@ using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Contents; -public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Func factory) : ShardedSnapshotStore(sharding, factory, x => x.AppId.Id), IContentRepository, IDeleter +public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Func factory) + : ShardedSnapshotStore(sharding, factory, x => x.AppId.Id), IContentRepository, IDeleter { public Task FindContentAsync(App app, Schema schema, DomainId id, SearchScope scope, CancellationToken ct = default) @@ -68,6 +69,12 @@ public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Fu return Shard(appId).StreamAll(appId, schemaIds, scope, ct); } + public IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, SearchScope scope, + CancellationToken ct = default) + { + return Shard(appId).StreamIds(appId, schemaIds, scope, ct); + } + public IAsyncEnumerable StreamReferencing(DomainId appId, DomainId references, int take, SearchScope scope, CancellationToken ct = default) { @@ -103,16 +110,4 @@ public sealed class MongoShardedContentRepository(IShardingStrategy sharding, Fu } } } - - public async IAsyncEnumerable StreamIds(DomainId appId, DomainId schemaId, SearchScope scope, - [EnumeratorCancellation] CancellationToken ct = default) - { - foreach (var shard in Shards) - { - await foreach (var id in shard.StreamIds(appId, schemaId, scope, ct)) - { - yield return id; - } - } - } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryAsStream.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryAsStream.cs index 1a9247963..43c208022 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryAsStream.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryAsStream.cs @@ -10,6 +10,7 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Contents; using Squidex.Infrastructure; +using static Google.Protobuf.WellKnownTypes.Field.Types; namespace Squidex.Domain.Apps.Entities.Contents.Operations; @@ -24,37 +25,37 @@ public sealed class QueryAsStream : OperationBase public async IAsyncEnumerable StreamAll(DomainId appId, HashSet? schemaIds, [EnumeratorCancellation] CancellationToken ct) { + if (schemaIds is { Count: 0 }) + { + yield break; + } + var filter = CreateFilter(appId, schemaIds); + var find = Collection.Find(filter).SelectFields(null).ToAsyncEnumerable(ct); - using (var cursor = await Collection.Find(filter).SelectFields(null).ToCursorAsync(ct)) + await foreach (var entity in find.WithCancellation(ct)) { - while (await cursor.MoveNextAsync(ct)) - { - foreach (var entity in cursor.Current) - { - yield return entity; - } - } + yield return entity; } } - public async IAsyncEnumerable StreamAllIds(DomainId appId, DomainId schemaId, + public async IAsyncEnumerable StreamAllIds(DomainId appId, HashSet? schemaIds, [EnumeratorCancellation] CancellationToken ct) { - var filter = CreateFilter(appId, [schemaId]); + if (schemaIds is { Count: 0 }) + { + yield break; + } // Only query the ID from the database to improve performance. var projection = Builders.Projection.Include(x => x.Id); - using (var cursor = await Collection.Find(filter).Project(projection).ToCursorAsync(ct)) + var filter = CreateFilter(appId, schemaIds); + var find = Collection.Find(filter).Project(projection); + + await foreach (var entity in find.ToAsyncEnumerable(ct).WithCancellation(ct)) { - while (await cursor.MoveNextAsync(ct)) - { - foreach (var entity in cursor.Current) - { - yield return entity.Id; - } - } + yield return entity.Id; } } @@ -64,7 +65,7 @@ public sealed class QueryAsStream : OperationBase { Filter.Gt(x => x.LastModified, default), Filter.Gt(x => x.Id, default), - Filter.Eq(x => x.IndexedAppId, appId) + Filter.Eq(x => x.IndexedAppId, appId), }; if (schemaIds != null) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs index 4e02ad8d7..abbe83f23 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryById.cs @@ -20,7 +20,6 @@ internal sealed class QueryById : OperationBase var filter = Filter.Eq(x => x.DocumentId, DomainId.Combine(schema.AppId, id)); var contentEntity = await Collection.Find(filter).SelectFields(null).FirstOrDefaultAsync(ct); - if (contentEntity == null || contentEntity.IndexedSchemaId != schema.Id) { return null; diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryByQuery.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryByQuery.cs index 70616998c..49d8007f8 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryByQuery.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryByQuery.cs @@ -42,9 +42,9 @@ internal sealed class QueryByQuery(MongoCountCollection countCollection) : Opera var filter = BuildFilter(app.Id, schema.Id, adjustedFilter); var contentEntities = await Collection.FindStatusAsync(filter, ct); - var contentResults = contentEntities.Select(x => new ContentIdStatus(x.IndexedSchemaId, x.Id, x.Status)).ToList(); + var contentStatuses = contentEntities.Select(x => new ContentIdStatus(x.IndexedSchemaId, x.Id, x.Status)).ToList(); - return contentResults; + return contentStatuses; } public async Task> QueryAsync(App app, List schemas, Q q, @@ -125,7 +125,7 @@ internal sealed class QueryByQuery(MongoCountCollection countCollection) : Opera Filter.Exists(x => x.LastModified), Filter.Exists(x => x.Id), Filter.Eq(x => x.IndexedAppId, appId), - Filter.Eq(x => x.IndexedSchemaId, schemaId) + Filter.Eq(x => x.IndexedSchemaId, schemaId), }; if (filter?.HasField(Field.Of(x => nameof(x.IsDeleted))) != true) @@ -149,7 +149,7 @@ internal sealed class QueryByQuery(MongoCountCollection countCollection) : Opera Filter.Gt(x => x.LastModified, default), Filter.Gt(x => x.Id, default), Filter.Eq(x => x.IndexedAppId, appId), - Filter.In(x => x.IndexedSchemaId, schemaIds) + Filter.In(x => x.IndexedSchemaId, schemaIds), }; var isDefault = false; diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryInDedicatedCollection.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryInDedicatedCollection.cs index 88c7ebf19..7764037e3 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryInDedicatedCollection.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryInDedicatedCollection.cs @@ -41,7 +41,7 @@ internal sealed class QueryInDedicatedCollection(IMongoClient mongoClient, strin new CreateIndexModel(Index .Ascending(x => x.IndexedSchemaId) .Ascending(x => x.IsDeleted) - .Descending(x => x.LastModified)) + .Descending(x => x.LastModified)), ]); return schemaCollection; @@ -190,7 +190,7 @@ internal sealed class QueryInDedicatedCollection(IMongoClient mongoClient, strin var filters = new List> { Filter.Exists(x => x.LastModified), - Filter.Exists(x => x.Id) + Filter.Exists(x => x.Id), }; if (filter?.HasField(Field.Of(x => nameof(x.IsDeleted))) != true) @@ -212,7 +212,7 @@ internal sealed class QueryInDedicatedCollection(IMongoClient mongoClient, strin var filters = new List> { Filter.Gt(x => x.LastModified, default), - Filter.Gt(x => x.Id, default) + Filter.Gt(x => x.Id, default), }; if (query?.Filter?.HasField(Field.Of(x => nameof(x.IsDeleted))) != true) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryReferrers.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryReferrers.cs index 39cdd3bc5..c537e28c0 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryReferrers.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Operations/QueryReferrers.cs @@ -39,16 +39,11 @@ internal sealed class QueryReferrers : OperationBase [EnumeratorCancellation] CancellationToken ct) { var filter = BuildFilter(appId, reference); + var find = Collection.Find(filter).Limit(take).SelectFields(null); - using (var cursor = await Collection.Find(filter).Limit(take).SelectFields(null).ToCursorAsync(ct)) + await foreach (var entity in find.ToAsyncEnumerable(ct).WithCancellation(ct)) { - while (await cursor.MoveNextAsync(ct)) - { - foreach (var entity in cursor.Current) - { - yield return entity; - } - } + yield return entity; } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasIndexDefinition.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasIndexDefinition.cs similarity index 91% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasIndexDefinition.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasIndexDefinition.cs index e24a2dfa7..d9f53af0b 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasIndexDefinition.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasIndexDefinition.cs @@ -8,7 +8,7 @@ using System.Net.Http.Json; using Squidex.Hosting.Configuration; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public static class AtlasIndexDefinition { @@ -49,7 +49,7 @@ public static class AtlasIndexDefinition ["sv"] = "lucene.swedish", ["th"] = "lucene.thai", ["tr"] = "lucene.turkish", - ["uk"] = "lucene.ukrainian" + ["uk"] = "lucene.ukrainian", }; public sealed class ErrorResponse @@ -142,7 +142,7 @@ public static class AtlasIndexDefinition { type = "document", fields = new Dictionary(), - dynamic = false + dynamic = false, }; var index = new @@ -158,29 +158,29 @@ public static class AtlasIndexDefinition ["_ai"] = new { type = "string", - analyzer = "lucene.keyword" + analyzer = "lucene.keyword", }, ["_si"] = new { type = "string", - analyzer = "lucene.keyword" + analyzer = "lucene.keyword", }, ["_ci"] = new { type = "string", - analyzer = "lucene.keyword" + analyzer = "lucene.keyword", }, ["fa"] = new { - type = "boolean" + type = "boolean", }, ["fp"] = new { - type = "boolean" + type = "boolean", }, - ["t"] = texts - } - } + ["t"] = texts, + }, + }, }; foreach (var (field, analyzer) in FieldAnalyzers) @@ -190,7 +190,7 @@ public static class AtlasIndexDefinition type = "string", analyzer, searchAnalyzer = analyzer, - store = false + store = false, }; } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasOptions.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasOptions.cs similarity index 94% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasOptions.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasOptions.cs index ce963a721..e7a9d5d2b 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasOptions.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasOptions.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class AtlasOptions { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasTextIndex.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasTextIndex.cs similarity index 89% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasTextIndex.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasTextIndex.cs index 24beace92..bbf9b22b9 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/AtlasTextIndex.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/AtlasTextIndex.cs @@ -12,12 +12,10 @@ using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Infrastructure; using LuceneQueryAnalyzer = Lucene.Net.QueryParsers.Classic.QueryParser; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory atlasClient, IOptions atlasOptions, string shardKey) : MongoTextIndexBase>(database, shardKey, new CommandFactory>(BuildTexts)) { @@ -58,7 +56,7 @@ public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory a { ["must"] = new BsonArray { - QueryVisitor.Visit(luceneQuery) + QueryVisitor.Visit(luceneQuery), }, ["filter"] = new BsonArray { @@ -67,18 +65,18 @@ public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory a ["text"] = new BsonDocument { ["path"] = "_ai", - ["query"] = app.Id.ToString() - } + ["query"] = app.Id.ToString(), + }, }, new BsonDocument { ["equals"] = new BsonDocument { ["path"] = serveField, - ["value"] = true - } - } - } + ["value"] = true, + }, + }, + }, }; if (query.PreferredSchemaId != null) @@ -90,9 +88,9 @@ public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory a ["text"] = new BsonDocument { ["path"] = "_si", - ["query"] = query.PreferredSchemaId.Value.ToString() - } - } + ["query"] = query.PreferredSchemaId.Value.ToString(), + }, + }, }; } else if (query.RequiredSchemaIds?.Count > 0) @@ -103,8 +101,8 @@ public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory a ["text"] = new BsonDocument { ["path"] = "_si", - ["query"] = x.ToString() - } + ["query"] = x.ToString(), + }, })); compound["minimumShouldMatch"] = 1; @@ -112,7 +110,7 @@ public sealed class AtlasTextIndex(IMongoDatabase database, IHttpClientFactory a var searchQuery = new BsonDocument { - ["compound"] = compound + ["compound"] = compound, }; if (index != null) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/CommandFactory.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/CommandFactory.cs similarity index 97% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/CommandFactory.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/CommandFactory.cs index 9bf94b89e..dcbe6d193 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/CommandFactory.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/CommandFactory.cs @@ -7,10 +7,9 @@ using MongoDB.Bson; using MongoDB.Driver; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Infrastructure; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class CommandFactory(Func, T> textBuilder) : MongoBase> where T : class { @@ -48,7 +47,7 @@ public sealed class CommandFactory(Func, T> textBu .SetOnInsert(x => x.Stage, upsert.Stage) .SetOnInsert(x => x.SchemaId, upsert.SchemaId.Id)) { - IsUpsert = true + IsUpsert = true, }); if (upsert.GeoObjects?.Count > 0) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneQueryVisitor.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneQueryVisitor.cs similarity index 90% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneQueryVisitor.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneQueryVisitor.cs index d28c879f4..aa81c8bc1 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneQueryVisitor.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneQueryVisitor.cs @@ -12,12 +12,13 @@ using Lucene.Net.Search; using Lucene.Net.Util; using MongoDB.Bson; using Squidex.Infrastructure; +using LuceneQuery = Lucene.Net.Search.Query; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class LuceneQueryVisitor(Func? fieldConverter = null) { - public BsonDocument Visit(Query query) + public BsonDocument Visit(LuceneQuery query) { switch (query) { @@ -65,14 +66,14 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu { ["path"] = GetPath(termRangeQuery.Field), [minField] = BsonValue.Create(min), - [maxField] = BsonValue.Create(max) + [maxField] = BsonValue.Create(max), }; ApplyBoost(termRangeQuery, doc); return new BsonDocument { - ["range"] = doc + ["range"] = doc, }; } @@ -85,14 +86,14 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu { ["path"] = GetPath(numericRangeQuery.Field), [minField] = BsonValue.Create(numericRangeQuery.Min), - [maxField] = BsonValue.Create(numericRangeQuery.Max) + [maxField] = BsonValue.Create(numericRangeQuery.Max), }; ApplyBoost(numericRangeQuery, doc); return new BsonDocument { - ["range"] = doc + ["range"] = doc, }; } @@ -104,7 +105,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu { var fuzzy = new BsonDocument { - ["maxEdits"] = fuzzyQuery.MaxEdits + ["maxEdits"] = fuzzyQuery.MaxEdits, }; if (fuzzyQuery.PrefixLength > 0) @@ -117,7 +118,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["text"] = doc + ["text"] = doc, }; } @@ -127,7 +128,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["wildcard"] = doc + ["wildcard"] = doc, }; } @@ -137,7 +138,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["wildcard"] = doc + ["wildcard"] = doc, }; } @@ -147,7 +148,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu var doc = new BsonDocument { - ["path"] = GetPath(terms[0].Field) + ["path"] = GetPath(terms[0].Field), }; if (terms.Length == 1) @@ -168,7 +169,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["phrase"] = doc + ["phrase"] = doc, }; } @@ -178,7 +179,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["text"] = doc + ["text"] = doc, }; } @@ -233,16 +234,16 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu return new BsonDocument { - ["compound"] = doc + ["compound"] = doc, }; } - private BsonDocument CreateDefaultDoc(Query query, Term term) + private BsonDocument CreateDefaultDoc(LuceneQuery query, Term term) { var doc = new BsonDocument { ["path"] = GetPath(term.Field), - ["query"] = term.Text + ["query"] = term.Text, }; ApplyBoost(query, doc); @@ -261,7 +262,7 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu { return new BsonDocument { - ["wildcard"] = field + ["wildcard"] = field, }; } @@ -269,13 +270,13 @@ public sealed class LuceneQueryVisitor(Func? fieldConverter = nu } #pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator - private static void ApplyBoost(Query query, BsonDocument doc) + private static void ApplyBoost(LuceneQuery query, BsonDocument doc) { if (query.Boost != 1) { doc["score"] = new BsonDocument { - ["boost"] = query.Boost + ["boost"] = query.Boost, }; } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneSearchDefinitionExtensions.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneSearchDefinitionExtensions.cs similarity index 96% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneSearchDefinitionExtensions.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneSearchDefinitionExtensions.cs index 6888a1bdc..1c73a3e6a 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/LuceneSearchDefinitionExtensions.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/LuceneSearchDefinitionExtensions.cs @@ -9,7 +9,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public static class LuceneSearchDefinitionExtensions { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoShardedTextIndex.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoShardedTextIndex.cs similarity index 93% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoShardedTextIndex.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoShardedTextIndex.cs index 268a52278..b456a8636 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoShardedTextIndex.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoShardedTextIndex.cs @@ -7,12 +7,10 @@ using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Infrastructure; using Squidex.Infrastructure.States; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class MongoShardedTextIndex(IShardingStrategy sharding, Func> factory) : ShardedService>(sharding, factory), ITextIndex, IDeleter where T : class { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndex.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndex.cs similarity index 96% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndex.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndex.cs index d45889d24..cc4103e9e 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndex.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndex.cs @@ -7,11 +7,9 @@ using MongoDB.Driver; using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Infrastructure; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class MongoTextIndex(IMongoDatabase database, string shardKey) : MongoTextIndexBase>(database, shardKey, new CommandFactory>(BuildTexts)) { @@ -59,7 +57,7 @@ public sealed class MongoTextIndex(IMongoDatabase database, string shardKey) : M SearchTerms = Tokenizer.Query(query.Text), SearchScope = scope, Results = [], - Take = query.Take + Take = query.Take, }; if (query.RequiredSchemaIds?.Count > 0) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexBase.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexBase.cs similarity index 96% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexBase.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexBase.cs index 9c50d92c7..b5669aeab 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexBase.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexBase.cs @@ -10,11 +10,9 @@ using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Infrastructure; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public abstract class MongoTextIndexBase(IMongoDatabase database, string shardKey, CommandFactory factory) : MongoRepositoryBase>(database), ITextIndex, IDeleter where T : class { @@ -48,7 +46,7 @@ public abstract class MongoTextIndexBase(IMongoDatabase database, string shar .Ascending(x => x.AppId) .Ascending(x => x.SchemaId) .Ascending(x => x.GeoField) - .Geo2DSphere(x => x.GeoObject)) + .Geo2DSphere(x => x.GeoObject)), ], ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntity.cs similarity index 96% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntity.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntity.cs index 28cad20bc..2012c92e0 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntity.cs @@ -10,7 +10,7 @@ using MongoDB.Bson.Serialization.Attributes; using NetTopologySuite.Geometries; using Squidex.Infrastructure; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class MongoTextIndexEntity { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntityText.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntityText.cs similarity index 93% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntityText.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntityText.cs index 75261481f..37db7fd55 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexEntityText.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexEntityText.cs @@ -7,7 +7,7 @@ using MongoDB.Bson.Serialization.Attributes; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class MongoTextIndexEntityText { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexerState.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexerState.cs similarity index 78% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexerState.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexerState.cs index a1f1bc238..c53b7f29b 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/MongoTextIndexerState.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/MongoTextIndexerState.cs @@ -9,14 +9,12 @@ using MongoDB.Bson.Serialization; using MongoDB.Driver; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Repositories; -using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Domain.Apps.Entities.Contents.Text.State; using Squidex.Domain.Apps.Entities.MongoDb; using Squidex.Infrastructure; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public sealed class MongoTextIndexerState( IMongoDatabase database, @@ -46,23 +44,29 @@ public sealed class MongoTextIndexerState( async Task IDeleter.DeleteAppAsync(App app, CancellationToken ct) { - var filter = - Filter.And( - Filter.Gte(x => x.UniqueContentId, new UniqueContentId(app.Id, DomainId.Empty)), - Filter.Lt(x => x.UniqueContentId, BsonUniqueContentIdSerializer.NextAppId(app.Id))); + var ids = + contentRepository.StreamIds(app.Id, null, SearchScope.All, ct) + .Select(x => new UniqueContentId(app.Id, x)); - await Collection.DeleteManyAsync(filter, ct); + await DeleteInBatchesAsync(ids, ct); } async Task IDeleter.DeleteSchemaAsync(App app, Schema schema, CancellationToken ct) { - var ids = contentRepository.StreamIds(app.Id, schema.Id, SearchScope.All, ct).Batch(1000, ct); + var ids = + contentRepository.StreamIds(app.Id, [schema.Id], SearchScope.All, ct) + .Select(x => new UniqueContentId(app.Id, x)); - await foreach (var batch in ids.WithCancellation(ct)) + await DeleteInBatchesAsync(ids, ct); + } + + private async Task DeleteInBatchesAsync(IAsyncEnumerable ids, + CancellationToken ct) + { + await foreach (var batch in ids.Batch(1000, ct).WithCancellation(ct)) { - var filter = - Filter.In(x => x.UniqueContentId, batch.Select(x => new UniqueContentId(app.Id, x))); + var filter = Filter.In(x => x.UniqueContentId, batch); await Collection.DeleteManyAsync(filter, ct); } @@ -95,7 +99,7 @@ public sealed class MongoTextIndexerState( new ReplaceOneModel( Filter.Eq(x => x.UniqueContentId, update.UniqueContentId), update) { - IsUpsert = true + IsUpsert = true, }); } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/Tokenizer.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/Tokenizer.cs similarity index 98% rename from backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/Tokenizer.cs rename to backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/Tokenizer.cs index 8b3138bf5..0ca657e93 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Text/Tokenizer.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Contents/Text/Tokenizer.cs @@ -16,7 +16,7 @@ using Squidex.Domain.Apps.Core; using Squidex.Infrastructure; using Squidex.Infrastructure.ObjectPool; -namespace Squidex.Domain.Apps.Entities.Text; +namespace Squidex.Domain.Apps.Entities.Contents.Text; public static class Tokenizer { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/EntityClassMap.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/EntityClassMap.cs index 89646ee6a..40fd379aa 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/EntityClassMap.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/EntityClassMap.cs @@ -6,7 +6,6 @@ // ========================================================================== using MongoDB.Bson.Serialization; -using Squidex.Domain.Apps.Core; using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities; @@ -15,17 +14,6 @@ internal static class EntityClassMap { public static void Register() { - BsonClassMap.TryRegisterClassMap(cm => - { - cm.MapProperty(x => x.AppId) - .SetElementName("ai") - .SetIsRequired(true); - - cm.MapProperty(x => x.IsDeleted) - .SetElementName("dl") - .SetIgnoreIfDefault(false); - }); - BsonClassMap.TryRegisterClassMap(cm => { cm.MapProperty(x => x.Id) diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/History/MongoHistoryEventRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/History/MongoHistoryEventRepository.cs index 1337a1904..68dd4f8eb 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/History/MongoHistoryEventRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/History/MongoHistoryEventRepository.cs @@ -49,7 +49,7 @@ public sealed class MongoHistoryEventRepository(IMongoDatabase database) : Mongo Index .Ascending(x => x.OwnerId) .Descending(x => x.Created) - .Descending(x => x.Version)) + .Descending(x => x.Version)), ], ct); } @@ -59,15 +59,17 @@ public sealed class MongoHistoryEventRepository(IMongoDatabase database) : Mongo await Collection.DeleteManyAsync(Filter.Eq(x => x.OwnerId, app.Id), ct); } - public async Task> QueryByChannelAsync(DomainId ownerId, string channelPrefix, int count, + public async Task> QueryByChannelAsync(DomainId ownerId, string? channel, int count, CancellationToken ct = default) { var find = - !string.IsNullOrWhiteSpace(channelPrefix) ? - Collection.Find(x => x.OwnerId == ownerId && x.Channel == channelPrefix) : + !string.IsNullOrWhiteSpace(channel) ? + Collection.Find(x => x.OwnerId == ownerId && x.Channel == channel) : Collection.Find(x => x.OwnerId == ownerId); - return await find.SortByDescending(x => x.Created).ThenByDescending(x => x.Version).Limit(count).ToListAsync(ct); + var result = await find.SortByDescending(x => x.Created).ThenByDescending(x => x.Version).Limit(count).ToListAsync(ct); + + return result; } public Task InsertManyAsync(IEnumerable historyEvents, @@ -77,7 +79,7 @@ public sealed class MongoHistoryEventRepository(IMongoDatabase database) : Mongo .Select(x => new ReplaceOneModel(Filter.Eq(y => y.Id, x.Id), x) { - IsUpsert = true + IsUpsert = true, }) .ToList(); diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleEventRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleEventRepository.cs index 73a7e73ce..76261c9d5 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleEventRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleEventRepository.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Runtime.CompilerServices; using MongoDB.Driver; using NodaTime; using Squidex.Domain.Apps.Core.Apps; @@ -37,8 +38,8 @@ public sealed class MongoRuleEventRepository(IMongoDatabase database) : MongoRep .Ascending(x => x.Expires), new CreateIndexOptions { - ExpireAfter = TimeSpan.Zero - }) + ExpireAfter = TimeSpan.Zero, + }), ], ct); } @@ -48,10 +49,15 @@ public sealed class MongoRuleEventRepository(IMongoDatabase database) : MongoRep await Collection.DeleteManyAsync(Filter.Eq(x => x.AppId, app.Id), ct); } - public Task QueryPendingAsync(Instant now, Func callback, - CancellationToken ct = default) + public async IAsyncEnumerable QueryPendingAsync(Instant now, + [EnumeratorCancellation] CancellationToken ct = default) { - return Collection.Find(x => x.NextAttempt < now).ForEachAsync(callback, ct); + var ruleEvents = Collection.Find(x => x.NextAttempt < now).ToAsyncEnumerable(ct); + + await foreach (var ruleEvent in ruleEvents.WithCancellation(ct)) + { + yield return ruleEvent; + } } public async Task> QueryByAppAsync(DomainId appId, DomainId? ruleId = null, int skip = 0, int take = 20, @@ -75,7 +81,7 @@ public sealed class MongoRuleEventRepository(IMongoDatabase database) : MongoRep return ResultList.Create(ruleEventTotal, ruleEventEntities); } - public async Task FindAsync(DomainId id, + public async Task FindAsync(DomainId id, CancellationToken ct = default) { var ruleEvent = diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleRepository.cs index ab232a3be..ceba7e313 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Rules/MongoRuleRepository.cs @@ -28,7 +28,7 @@ public sealed class MongoRuleRepository(IMongoDatabase database) : MongoSnapshot [ new CreateIndexModel( Index - .Ascending(x => x.IndexedAppId)) + .Ascending(x => x.IndexedAppId)), ], ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemaRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemaRepository.cs index f6249bc92..7dab863a2 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemaRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemaRepository.cs @@ -29,7 +29,7 @@ public sealed class MongoSchemaRepository(IMongoDatabase database) : MongoSnapsh new CreateIndexModel( Index .Ascending(x => x.IndexedAppId) - .Ascending(x => x.IndexedName)) + .Ascending(x => x.IndexedName)), ], ct); } @@ -51,6 +51,7 @@ public sealed class MongoSchemaRepository(IMongoDatabase database) : MongoSnapsh { var entities = await Collection.Find(x => x.IndexedAppId == appId && !x.IndexedDeleted) + .Sort(Sort.Ascending(x => x.IndexedName)) .ToListAsync(ct); return entities.Select(x => x.Document).ToList(); diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemasHash.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemasHash.cs index 9f33e8911..f3464a917 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemasHash.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Schemas/MongoSchemasHash.cs @@ -6,14 +6,11 @@ // ========================================================================== using MongoDB.Driver; -using NodaTime; using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Events; using Squidex.Events; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.ObjectPool; namespace Squidex.Domain.Apps.Entities.Schemas; @@ -56,7 +53,7 @@ public sealed class MongoSchemasHash(IMongoDatabase database) : MongoRepositoryB .Set($"s.{schemaEvent.SchemaId.Id}", @event.Headers.EventStreamNumber()) .Set(x => x.Updated, @event.Headers.TimestampAsInstant())) { - IsUpsert = true + IsUpsert = true, }); } } @@ -69,56 +66,20 @@ public sealed class MongoSchemasHash(IMongoDatabase database) : MongoRepositoryB return Collection.BulkWriteAsync(writes, BulkUnordered); } - public async Task<(Instant Create, string Hash)> GetCurrentHashAsync(App app, + public async Task GetCurrentHashAsync(App app, CancellationToken ct = default) { Guard.NotNull(app); var entity = await Collection.Find(x => x.AppId == app.Id).FirstOrDefaultAsync(ct); - if (entity == null) { - return (default, string.Empty); + return SchemasHashKey.Empty; } - var ids = - entity.SchemaVersions.Select(x => (x.Key, x.Value)) - .Union(Enumerable.Repeat((app.Id.ToString(), app.Version), 1)); - - var hash = CreateHash(ids); - - return (entity.Updated, hash); - } - - public ValueTask ComputeHashAsync(App app, IEnumerable schemas, - CancellationToken ct = default) - { - var ids = - schemas.Select(x => (x.Id.ToString(), x.Version)) - .Union(Enumerable.Repeat((app.Id.ToString(), app.Version), 1)); - - var hash = CreateHash(ids); - - return new ValueTask(hash); - } - - private static string CreateHash(IEnumerable<(string, long)> ids) - { - var sb = DefaultPools.StringBuilder.Get(); - try - { - foreach (var (id, version) in ids.OrderBy(x => x.Item1)) - { - sb.Append(id); - sb.Append(version); - sb.Append(';'); - } - - return sb.ToString().ToSha256Base64(); - } - finally - { - DefaultPools.StringBuilder.Return(sb); - } + return SchemasHashKey.Create( + app, + entity.SchemaVersions.ToDictionary(x => DomainId.Create(x.Key), x => x.Value), + entity.Updated); } } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamEntity.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamEntity.cs index fbfe66fe4..f24605644 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamEntity.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamEntity.cs @@ -35,7 +35,7 @@ public sealed class MongoTeamEntity : MongoState { var users = new HashSet { - Document.CreatedBy.Identifier + Document.CreatedBy.Identifier, }; users.AddRange(Document.Contributors.Keys); diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamRepository.cs b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamRepository.cs index 179222d19..0d74e2971 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Apps/Entities/Teams/MongoTeamRepository.cs @@ -27,7 +27,7 @@ public sealed class MongoTeamRepository(IMongoDatabase database) : MongoSnapshot [ new CreateIndexModel( Index - .Ascending(x => x.IndexedUserIds)) + .Ascending(x => x.IndexedUserIds)), ], ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoRoleStore.cs b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoRoleStore.cs index 512b25d0e..52292afff 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoRoleStore.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoRoleStore.cs @@ -12,7 +12,7 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using Squidex.Infrastructure; -namespace Squidex.Domain.Users.MongoDb; +namespace Squidex.Domain.Users; public sealed class MongoRoleStore(IMongoDatabase database) : MongoRepositoryBase(database), IRoleStore { @@ -43,7 +43,7 @@ public sealed class MongoRoleStore(IMongoDatabase database) : MongoRepositoryBas .Ascending(x => x.NormalizedName), new CreateIndexOptions { - Unique = true + Unique = true, }), cancellationToken: ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoTokenStoreInitializer.cs b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoTokenStoreInitializer.cs new file mode 100644 index 000000000..f02d12283 --- /dev/null +++ b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoTokenStoreInitializer.cs @@ -0,0 +1,72 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using OpenIddict.Abstractions; +using OpenIddict.MongoDb; +using OpenIddict.MongoDb.Models; +using Squidex.Hosting; +using Squidex.Infrastructure.Timers; + +namespace Squidex.Domain.Users; + +public sealed class MongoTokenStoreInitializer(IOptions options, IServiceProvider serviceProvider) + : IInitializable, IBackgroundProcess +{ + private readonly OpenIddictMongoDbOptions options = options.Value; + private CompletionTimer timer; + + public async Task InitializeAsync( + CancellationToken ct) + { + await SetupIndexAsync(ct); + } + + public async Task StartAsync( + CancellationToken ct) + { + timer = new CompletionTimer((int)TimeSpan.FromHours(6).TotalMilliseconds, PruneAsync); + + await PruneAsync(ct); + } + + public Task StopAsync( + CancellationToken ct) + { + return timer?.StopAsync() ?? Task.CompletedTask; + } + + private async Task PruneAsync( + CancellationToken ct) + { + await using (var scope = serviceProvider.CreateAsyncScope()) + { + var tokenManager = scope.ServiceProvider.GetRequiredService(); + + await tokenManager.PruneAsync(DateTimeOffset.UtcNow.AddDays(-40), ct); + } + } + + private async Task SetupIndexAsync( + CancellationToken ct) + { + await using (var scope = serviceProvider.CreateAsyncScope()) + { + var database = await scope.ServiceProvider.GetRequiredService().GetDatabaseAsync(ct); + + var collection = database.GetCollection(options.TokensCollectionName); + + await collection.Indexes.CreateOneAsync( + new CreateIndexModel( + Builders.IndexKeys + .Ascending(x => x.ReferenceId)), + cancellationToken: ct); + } + } +} diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUser.cs b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUser.cs index 03e203f3f..9054ffda7 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUser.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUser.cs @@ -12,7 +12,7 @@ using Squidex.Infrastructure; #pragma warning disable MA0048 // File name must match type name -namespace Squidex.Domain.Users.MongoDb; +namespace Squidex.Domain.Users; public sealed class MongoUser : IdentityUser { diff --git a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUserStore.cs b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUserStore.cs index 86d7d9308..e8651a370 100644 --- a/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUserStore.cs +++ b/backend/src/Squidex.Data.MongoDb/Domain/Users/MongoUserStore.cs @@ -13,7 +13,7 @@ using MongoDB.Bson.Serialization.Serializers; using MongoDB.Driver; using Squidex.Infrastructure; -namespace Squidex.Domain.Users.MongoDb; +namespace Squidex.Domain.Users; public sealed class MongoUserStore(IMongoDatabase database) : MongoRepositoryBase(database), @@ -54,7 +54,7 @@ public sealed class MongoUserStore(IMongoDatabase database) : .SetArguments(new[] { nameof(Claim.Type), - nameof(Claim.Value) + nameof(Claim.Value), }); cm.MapMember(x => x.Type); @@ -74,7 +74,7 @@ public sealed class MongoUserStore(IMongoDatabase database) : { nameof(UserLogin.LoginProvider), nameof(UserLogin.ProviderKey), - nameof(UserLogin.ProviderDisplayName) + nameof(UserLogin.ProviderDisplayName), }); cm.AutoMap(); @@ -142,15 +142,15 @@ public sealed class MongoUserStore(IMongoDatabase database) : .Ascending(x => x.NormalizedUserName), new CreateIndexOptions { - Unique = true + Unique = true, }), new CreateIndexModel( Index .Ascending(x => x.NormalizedEmail), new CreateIndexOptions { - Unique = true - }) + Unique = true, + }), ], ct); } diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Batching.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Batching.cs index 186e699ad..db3bbbeb2 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Batching.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Batching.cs @@ -14,6 +14,6 @@ public static class Batching public static readonly FindOptions Options = new FindOptions { // Use a relatively small batch size to keep the memory pressure low. - BatchSize = 200 + BatchSize = 200, }; } diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/BsonDefaultConventions.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/BsonDefaultConventions.cs index f3a3bcfcf..d42ed5c22 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/BsonDefaultConventions.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/BsonDefaultConventions.cs @@ -27,7 +27,7 @@ public static class BsonDefaultConventions ConventionRegistry.Register("IgnoreExtraElements", new ConventionPack { - new IgnoreExtraElementsConvention(true) + new IgnoreExtraElementsConvention(true), }, t => true); // Allow all types, independent from the actual assembly. diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntry.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntity.cs similarity index 95% rename from backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntry.cs rename to backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntity.cs index a802e915f..5825510bb 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntry.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoCacheEntity.cs @@ -9,7 +9,7 @@ using MongoDB.Bson.Serialization.Attributes; namespace Squidex.Infrastructure.Caching; -public sealed class MongoCacheEntry +public sealed class MongoCacheEntity { [BsonId] [BsonElement("_id")] diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoDistributedCache.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoDistributedCache.cs index 165628693..b7a6961e9 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoDistributedCache.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Caching/MongoDistributedCache.cs @@ -10,22 +10,23 @@ using MongoDB.Driver; namespace Squidex.Infrastructure.Caching; -public sealed class MongoDistributedCache(IMongoDatabase database) : MongoRepositoryBase(database), IDistributedCache +public sealed class MongoDistributedCache(IMongoDatabase database, TimeProvider timeProvider) + : MongoRepositoryBase(database), IDistributedCache { protected override string CollectionName() { return "Cache"; } - protected override Task SetupCollectionAsync(IMongoCollection collection, + protected override Task SetupCollectionAsync(IMongoCollection collection, CancellationToken ct) { return Collection.Indexes.CreateOneAsync( - new CreateIndexModel( + new CreateIndexModel( Index.Ascending(x => x.Expires), new CreateIndexOptions { - ExpireAfter = TimeSpan.Zero + ExpireAfter = TimeSpan.Zero, }), null, ct); } @@ -65,9 +66,10 @@ public sealed class MongoDistributedCache(IMongoDatabase database) : MongoReposi public async Task GetAsync(string key, CancellationToken token = default) { - var entry = await Collection.Find(x => x.Key == key).FirstOrDefaultAsync(token); + var now = timeProvider.GetUtcNow().UtcDateTime; - if (entry != null && entry.Expires > DateTime.UtcNow) + var entry = await Collection.Find(x => x.Key == key).FirstOrDefaultAsync(token); + if (entry != null && entry.Expires > now) { return entry.Value; } @@ -78,7 +80,7 @@ public sealed class MongoDistributedCache(IMongoDatabase database) : MongoReposi public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default) { - var expires = DateTime.UtcNow; + var expires = timeProvider.GetUtcNow().UtcDateTime; if (options.AbsoluteExpiration.HasValue) { @@ -92,6 +94,10 @@ public sealed class MongoDistributedCache(IMongoDatabase database) : MongoReposi { expires += options.SlidingExpiration.Value; } + else + { + expires = DateTime.MaxValue; + } return Collection.UpdateOneAsync(x => x.Key == key, Update diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequest.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestEntity.cs similarity index 75% rename from backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequest.cs rename to backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestEntity.cs index 6d5926c37..fc5356c46 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequest.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestEntity.cs @@ -8,10 +8,11 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using NodaTime; +using Squidex.Infrastructure.Reflection; namespace Squidex.Infrastructure.Log; -public sealed class MongoRequest +public sealed class MongoRequestEntity { [BsonId] [BsonElement("_id")] @@ -29,13 +30,13 @@ public sealed class MongoRequest [BsonElement(nameof(Properties))] public Dictionary Properties { get; set; } - public static MongoRequest FromRequest(Request request) + public static MongoRequestEntity FromRequest(Request request) { - return new MongoRequest { Key = request.Key, Timestamp = request.Timestamp, Properties = request.Properties }; + return SimpleMapper.Map(request, new MongoRequestEntity()); } public Request ToRequest() { - return new Request { Key = Key, Timestamp = Timestamp, Properties = Properties }; + return SimpleMapper.Map(this, new Request()); } } diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestLogRepository.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestLogRepository.cs index 837bbc89d..56dce2c29 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestLogRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/Log/MongoRequestLogRepository.cs @@ -11,39 +11,32 @@ using NodaTime; namespace Squidex.Infrastructure.Log; -public sealed class MongoRequestLogRepository : MongoRepositoryBase, IRequestLogRepository +public sealed class MongoRequestLogRepository(IMongoDatabase database, IOptions options) + : MongoRepositoryBase(database), IRequestLogRepository { - private readonly RequestLogStoreOptions options; - - public MongoRequestLogRepository(IMongoDatabase database, IOptions options) - : base(database) - { - Guard.NotNull(options); - - this.options = options.Value; - } + private readonly RequestLogStoreOptions options = options.Value; protected override string CollectionName() { return "RequestLog"; } - protected override Task SetupCollectionAsync(IMongoCollection collection, + protected override Task SetupCollectionAsync(IMongoCollection collection, CancellationToken ct) { return collection.Indexes.CreateManyAsync( [ - new CreateIndexModel( + new CreateIndexModel( Index .Ascending(x => x.Key) .Ascending(x => x.Timestamp)), - new CreateIndexModel( + new CreateIndexModel( Index .Ascending(x => x.Timestamp), new CreateIndexOptions { - ExpireAfter = TimeSpan.FromDays(options.StoreRetentionInDays) - }) + ExpireAfter = TimeSpan.FromDays(options.StoreRetentionInDays), + }), ], ct); } @@ -52,7 +45,7 @@ public sealed class MongoRequestLogRepository : MongoRepositoryBase x.Key, key), ct); } - public IAsyncEnumerable QueryAllAsync(string key, DateTime fromDate, DateTime toDate, + public IAsyncEnumerable QueryAllAsync(string key, Instant fromTime, Instant toTime, CancellationToken ct = default) { Guard.NotNullOrEmpty(key); - var timestampStart = Instant.FromDateTimeUtc(fromDate); - var timestampEnd = Instant.FromDateTimeUtc(toDate.AddDays(1)); - - var find = Collection.Find(x => x.Key == key && x.Timestamp >= timestampStart && x.Timestamp < timestampEnd); - - var documents = find.ToAsyncEnumerable(ct); + var documents = + Collection.Find(x => + x.Key == key && + x.Timestamp >= fromTime && + x.Timestamp <= toTime) + .ToAsyncEnumerable(ct); return documents.Select(x => x.ToRequest()); } diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/MongoExtensions.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/MongoExtensions.cs index 7ff2a0637..4c5689519 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/MongoExtensions.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/MongoExtensions.cs @@ -24,7 +24,7 @@ public static class MongoExtensions { var options = new ListCollectionNamesOptions { - Filter = new BsonDocument("name", collectionName) + Filter = new BsonDocument("name", collectionName), }; var collections = await database.ListCollectionNamesAsync(options, ct); @@ -55,7 +55,7 @@ public static class MongoExtensions return true; } - public static async IAsyncEnumerable ToAsyncEnumerable(this IFindFluent find, + public static async IAsyncEnumerable ToAsyncEnumerable(this IFindFluent find, [EnumeratorCancellation] CancellationToken ct = default) { using var cursor = await find.ToCursorAsync(ct); @@ -209,7 +209,7 @@ public static class MongoExtensions var command = new BsonDocumentCommand(new BsonDocument { - { "buildInfo", 1 } + { "buildInfo", 1 }, }); var document = await database.RunCommandAsync(command, cancellationToken: ct); diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/States/MongoSnapshotStoreBase.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/States/MongoSnapshotStoreBase.cs index 773ed06ef..2d21e73aa 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/States/MongoSnapshotStoreBase.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/States/MongoSnapshotStoreBase.cs @@ -66,7 +66,7 @@ public abstract class MongoSnapshotStoreBase(IMongoDatabase database) var writes = jobs.Select(x => new ReplaceOneModel(Filter.Eq(y => y.DocumentId, x.Key), CreateDocument(x.Key, x.Value, x.NewVersion)) { - IsUpsert = true + IsUpsert = true, }).ToList(); if (writes.Count == 0) @@ -112,7 +112,7 @@ public abstract class MongoSnapshotStoreBase(IMongoDatabase database) { Document = doc, DocumentId = id, - Version = version + Version = version, }; result.Prepare(); diff --git a/backend/src/Squidex.Data.MongoDb/Infrastructure/UsageTracking/MongoUsageRepository.cs b/backend/src/Squidex.Data.MongoDb/Infrastructure/UsageTracking/MongoUsageRepository.cs index 6437d60fd..6f1ecc78f 100644 --- a/backend/src/Squidex.Data.MongoDb/Infrastructure/UsageTracking/MongoUsageRepository.cs +++ b/backend/src/Squidex.Data.MongoDb/Infrastructure/UsageTracking/MongoUsageRepository.cs @@ -45,46 +45,26 @@ public sealed class MongoUsageRepository(IMongoDatabase database) : MongoReposit return Collection.DeleteManyAsync(Filter.Regex(x => x.Key, new BsonRegularExpression(pattern)), ct); } - public async Task TrackUsagesAsync(UsageUpdate update, - CancellationToken ct = default) - { - Guard.NotNull(update); - - if (update.Counters.Count > 0) - { - var (filter, updateStatement) = CreateOperation(update); - - await Collection.UpdateOneAsync(filter, updateStatement, Upsert, ct); - } - } - public async Task TrackUsagesAsync(UsageUpdate[] updates, CancellationToken ct = default) { Guard.NotNull(updates); - if (updates.Length == 1) - { - await TrackUsagesAsync(updates[0], ct); - } - else if (updates.Length > 0) - { - var writes = new List>(updates.Length); + var writes = new List>(updates.Length); - foreach (var update in updates) + foreach (var update in updates) + { + if (update.Counters.Count > 0) { - if (update.Counters.Count > 0) - { - var (filter, updateStatement) = CreateOperation(update); + var (filter, updateStatement) = CreateOperation(update); - writes.Add(new UpdateOneModel(filter, updateStatement) { IsUpsert = true }); - } + writes.Add(new UpdateOneModel(filter, updateStatement) { IsUpsert = true }); } + } - if (writes.Count > 0) - { - await Collection.BulkWriteAsync(writes, BulkUnordered, ct); - } + if (writes.Count > 0) + { + await Collection.BulkWriteAsync(writes, BulkUnordered, ct); } } @@ -113,7 +93,12 @@ public sealed class MongoUsageRepository(IMongoDatabase database) : MongoReposit var dateTimeFrom = fromDate.ToDateTime(default); var dateTimeTo = toDate.ToDateTime(default); - var entities = await Collection.Find(x => x.Key == key && x.Date >= dateTimeFrom && x.Date <= dateTimeTo).ToListAsync(ct); + var entities = await Collection + .Find(x => + x.Key == key && + x.Date >= dateTimeFrom && + x.Date <= dateTimeTo) + .ToListAsync(ct); return entities.Select(x => new StoredUsage(x.Category, x.Date.ToDateOnly(), x.Counters)).ToList(); } diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/AddAppIdToEventStream.cs b/backend/src/Squidex.Data.MongoDb/Migrations/AddAppIdToEventStream.cs index 054f6bde5..e0c7a33d1 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/AddAppIdToEventStream.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/AddAppIdToEventStream.cs @@ -12,7 +12,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.Tasks; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class AddAppIdToEventStream(IMongoDatabase database) : MongoBase, IMigration { @@ -83,7 +83,7 @@ public sealed class AddAppIdToEventStream(IMongoDatabase database) : MongoBase(filter, document) { - IsUpsert = true + IsUpsert = true, }); } diff --git a/backend/src/Migrations/Migrations/Backup/BackupJob.cs b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupJob.cs similarity index 94% rename from backend/src/Migrations/Migrations/Backup/BackupJob.cs rename to backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupJob.cs index 862dbe2d2..02c04725d 100644 --- a/backend/src/Migrations/Migrations/Backup/BackupJob.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupJob.cs @@ -8,7 +8,7 @@ using NodaTime; using Squidex.Infrastructure; -namespace Migrations.Migrations.Backup; +namespace Squidex.Migrations.Backup; public sealed class BackupJob { diff --git a/backend/src/Migrations/Migrations/Backup/BackupState.cs b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupState.cs similarity index 90% rename from backend/src/Migrations/Migrations/Backup/BackupState.cs rename to backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupState.cs index 27aea66cb..015a11d3b 100644 --- a/backend/src/Migrations/Migrations/Backup/BackupState.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupState.cs @@ -7,7 +7,7 @@ using Squidex.Domain.Apps.Entities.Jobs; -namespace Migrations.Migrations.Backup; +namespace Squidex.Migrations.Backup; public sealed class BackupState { @@ -17,7 +17,7 @@ public sealed class BackupState { var result = new JobsState { - Jobs = Jobs.Select(ToState).ToList() + Jobs = Jobs.Select(ToState).ToList(), }; return result; @@ -39,12 +39,12 @@ public sealed class BackupState BackupStatus.Created => JobStatus.Created, BackupStatus.Failed => JobStatus.Failed, BackupStatus.Started => JobStatus.Started, - _ => JobStatus.Failed + _ => JobStatus.Failed, }, Log = [ - new JobLogMessage(source.Stopped ?? source.Started, $"Total events: {source.HandledEvents}, assets: {source.HandledAssets}") - ] + new JobLogMessage(source.Stopped ?? source.Started, $"Total events: {source.HandledEvents}, assets: {source.HandledAssets}"), + ], }; } } diff --git a/backend/src/Migrations/Migrations/Backup/BackupStatus.cs b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupStatus.cs similarity index 89% rename from backend/src/Migrations/Migrations/Backup/BackupStatus.cs rename to backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupStatus.cs index c796ecf4a..cb7dcea44 100644 --- a/backend/src/Migrations/Migrations/Backup/BackupStatus.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/BackupStatus.cs @@ -5,12 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Migrations.Migrations.Backup; +namespace Squidex.Migrations.Backup; public enum BackupStatus { Created, Started, Completed, - Failed + Failed, } diff --git a/backend/src/Migrations/Migrations/Backup/ConvertBackup.cs b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/ConvertBackup.cs similarity index 96% rename from backend/src/Migrations/Migrations/Backup/ConvertBackup.cs rename to backend/src/Squidex.Data.MongoDb/Migrations/Backup/ConvertBackup.cs index 41dba333d..e26b669ad 100644 --- a/backend/src/Migrations/Migrations/Backup/ConvertBackup.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/Backup/ConvertBackup.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Entities.Jobs; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.States; -namespace Migrations.Migrations.Backup; +namespace Squidex.Migrations.Backup; public sealed class ConvertBackup( ISnapshotStore stateBackups, diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertDocumentIds.cs b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertDocumentIds.cs index d9ebe2e0d..3e62d1ca5 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertDocumentIds.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertDocumentIds.cs @@ -11,7 +11,7 @@ using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.Tasks; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class ConvertDocumentIds(IMongoDatabase databaseDefault, IMongoDatabase databaseContent) : MongoBase, IMigration { @@ -21,7 +21,7 @@ public sealed class ConvertDocumentIds(IMongoDatabase databaseDefault, IMongoDat { None, Assets, - Contents + Contents, } public override string ToString() @@ -109,7 +109,7 @@ public sealed class ConvertDocumentIds(IMongoDatabase databaseDefault, IMongoDat writes.Add(new ReplaceOneModel(filter, document) { - IsUpsert = true + IsUpsert = true, }); } diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertOldSnapshotStores.cs b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertOldSnapshotStores.cs index a18d94bdf..f0f31c019 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertOldSnapshotStores.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertOldSnapshotStores.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class ConvertOldSnapshotStores(IMongoDatabase database) : MongoBase, IMigration { @@ -22,7 +22,7 @@ public sealed class ConvertOldSnapshotStores(IMongoDatabase database) : MongoBas { "States_Apps", "States_Rules", - "States_Schemas" + "States_Schemas", }.Select(x => database.GetCollection(x)); var update = Update.Rename("State", "Doc"); diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertRuleEventsJson.cs b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertRuleEventsJson.cs index 195d3a8b3..2fc5d2c1a 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/ConvertRuleEventsJson.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/ConvertRuleEventsJson.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class ConvertRuleEventsJson(IMongoDatabase database) : MongoBase, IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/CopyRuleStatistics.cs b/backend/src/Squidex.Data.MongoDb/Migrations/CopyRuleStatistics.cs index a33e29728..83f9049b0 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/CopyRuleStatistics.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/CopyRuleStatistics.cs @@ -12,7 +12,7 @@ using Squidex.Domain.Apps.Entities.Rules; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class CopyRuleStatistics(IMongoDatabase database, IRuleUsageTracker ruleUsageTracker) : IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/DeleteContentCollections.cs b/backend/src/Squidex.Data.MongoDb/Migrations/DeleteContentCollections.cs index ccaedf40f..6a688f4d9 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/DeleteContentCollections.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/DeleteContentCollections.cs @@ -8,7 +8,7 @@ using MongoDB.Driver; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class DeleteContentCollections(IMongoDatabase database) : IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetMetadata.cs b/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetMetadata.cs index c8cb0e6a9..36e557729 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetMetadata.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetMetadata.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class RenameAssetMetadata(IMongoDatabase database) : MongoBase, IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetSlugField.cs b/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetSlugField.cs index 2c0b45395..0d65fe510 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetSlugField.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/RenameAssetSlugField.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class RenameAssetSlugField(IMongoDatabase database) : MongoBase, IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/Migrations/RestructureContentCollection.cs b/backend/src/Squidex.Data.MongoDb/Migrations/RestructureContentCollection.cs index d6dcd098a..d36c2f4a1 100644 --- a/backend/src/Squidex.Data.MongoDb/Migrations/RestructureContentCollection.cs +++ b/backend/src/Squidex.Data.MongoDb/Migrations/RestructureContentCollection.cs @@ -10,7 +10,7 @@ using MongoDB.Driver; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; -namespace Squidex.Migrations.MongoDb; +namespace Squidex.Migrations; public sealed class RestructureContentCollection(IMongoDatabase contentDatabase) : MongoBase, IMigration { diff --git a/backend/src/Squidex.Data.MongoDb/ServiceExtensions.cs b/backend/src/Squidex.Data.MongoDb/ServiceExtensions.cs index 3321261c8..b4e911727 100644 --- a/backend/src/Squidex.Data.MongoDb/ServiceExtensions.cs +++ b/backend/src/Squidex.Data.MongoDb/ServiceExtensions.cs @@ -42,10 +42,7 @@ using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Repositories; using Squidex.Domain.Apps.Entities.Teams; using Squidex.Domain.Apps.Entities.Teams.Repositories; -using Squidex.Domain.Apps.Entities.Text; using Squidex.Domain.Users; -using Squidex.Domain.Users.InMemory; -using Squidex.Domain.Users.MongoDb; using Squidex.Events; using Squidex.Events.Mongo; using Squidex.Infrastructure; @@ -56,7 +53,8 @@ using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.States; using Squidex.Infrastructure.UsageTracking; -using Squidex.Migrations.MongoDb; +using Squidex.Migrations; +using Squidex.Migrations.Backup; using YDotNet.Server.MongoDB; namespace Squidex; @@ -93,7 +91,7 @@ public static class ServiceExtensions return new GridFSBucket(mongoDatabase, new GridFSBucketOptions { - BucketName = mongoGridFsBucketName + BucketName = mongoGridFsBucketName, }); }); } @@ -104,8 +102,18 @@ public static class ServiceExtensions var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database")!; var mongoContentDatabaseName = config.GetOptionalValue("store:mongoDb:contentDatabase", mongoDatabaseName)!; + var contentDatabase = new Func(c => + { + return GetDatabase(c, mongoDatabaseName); + }); + + services.AddSingletonAs(c => GetMongoClient(mongoConfiguration)) + .As(); + + services.AddSingletonAs(c => GetDatabase(c, mongoDatabaseName)) + .As(); + services.AddMongoAssetKeyValueStore(); - services.AddSingleton(typeof(ISnapshotStore<>), typeof(MongoSnapshotStore<>)); services.AddYDotNet() .AddMongoStorage(options => @@ -119,44 +127,17 @@ public static class ServiceExtensions options.CollectionName = "Chat"; }); + services.AddOpenIddict() + .AddCore(builder => + { + builder.UseMongoDb().SetTokensCollectionName("Identity_Tokens"); + }); + services.AddMessaging() .AddMongoDataStore(config); - services.AddSingletonAs(c => GetMongoClient(mongoConfiguration)) - .As(); - - services.AddSingletonAs(c => GetDatabase(c, mongoDatabaseName)) - .As(); - - services.AddSingletonAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs(c => new DeleteContentCollections(GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs(c => new RestructureContentCollection(GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs(c => new ConvertDocumentIds(GetDatabase(c, mongoDatabaseName), GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); + services.AddSingleton(typeof(ISnapshotStore<>), typeof(MongoSnapshotStore<>)); + services.AddMigrations(contentDatabase); services.AddSingletonAs() .As(); @@ -164,45 +145,48 @@ public static class ServiceExtensions services.AddHealthChecks() .AddCheck("MongoDB", tags: ["node"]); - services.AddSingletonAs() - .As(); - - services.AddSingletonAs() - .As(); + services.AddSingletonAs() + .As().As>().As(); - services.AddSingletonAs() - .As().As(); + services.AddSingletonAs() + .As().As>().As(); services.AddSingletonAs() .As().As(); + services.AddSingletonAs() + .As(); + services.AddSingletonAs() .As>(); - services.AddSingletonAs() - .As>().As(); - - services.AddSingletonAs() - .As().As>().As(); - - services.AddSingletonAs() - .As().As>().As(); - - services.AddSingletonAs() - .As().As>(); - services.AddSingletonAs() .As().As>().As(); + services.AddSingletonAs() + .As().As(); + services.AddSingletonAs() .As().As>().As(); services.AddSingletonAs() .AsOptional().As().As(); + services.AddSingletonAs() + .As().As>(); + services.AddSingletonAs() .As().As(); + services.AddSingletonAs() + .AsSelf(); + + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As>().As(); + services.AddSingletonAs(c => { return new MongoShardedAssetRepository(GetSharding(config, "store:mongoDB:assetShardCount"), @@ -217,17 +201,6 @@ public static class ServiceExtensions shardKey => ActivatorUtilities.CreateInstance(c, shardKey, contentDatabase)); }).As().As>().As(); - services.AddOpenIddict() - .AddCore(builder => - { - builder.UseMongoDb() - .SetScopesCollectionName("Identity_Scopes") - .SetTokensCollectionName("Identity_Tokens"); - - builder.SetDefaultScopeEntity(); - builder.SetDefaultApplicationEntity(); - }); - var atlasOptions = config.GetSection("store:mongoDb:atlas").Get() ?? new (); if (atlasOptions.IsConfigured() && atlasOptions.FullTextEnabled) @@ -242,7 +215,7 @@ public static class ServiceExtensions { return new HttpClientHandler { - Credentials = new NetworkCredential(atlasOptions.PublicKey, atlasOptions.PrivateKey, "cloud.mongodb.com") + Credentials = new NetworkCredential(atlasOptions.PublicKey, atlasOptions.PrivateKey, "cloud.mongodb.com"), }; }); @@ -269,6 +242,42 @@ public static class ServiceExtensions }, int.MinValue); } + private static void AddMigrations(this IServiceCollection services, Func contentDatabase) + { + services.AddSingletonAs() + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs(c => new DeleteContentCollections(contentDatabase(c))) + .As(); + + services.AddTransientAs(c => new RestructureContentCollection(contentDatabase(c))) + .As(); + + services.AddTransientAs(c => new ConvertDocumentIds(c.GetRequiredService(), contentDatabase(c))) + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + + services.AddTransientAs() + .As(); + } + private static IMongoClient GetMongoClient(string configuration) { return Singletons.GetOrAdd(configuration, connectionString => diff --git a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj index 06bf6d509..49f48016d 100644 --- a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj +++ b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj @@ -25,15 +25,15 @@ - - - - - - + + + + + + - + diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs index 177ee0f51..cdd874e22 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs @@ -45,7 +45,7 @@ public sealed class AppClients : ReadonlyDictionary var newClient = new AppClient(id, secret) { - Role = role.Or(Role.Editor) + Role = role.Or(Role.Editor), }; if (!this.TryAdd(id, newClient, out var updated)) @@ -71,7 +71,7 @@ public sealed class AppClients : ReadonlyDictionary var newClient = client with { - AllowAnonymous = allowAnonymous ?? client.AllowAnonymous + AllowAnonymous = allowAnonymous ?? client.AllowAnonymous, }; if (!string.IsNullOrWhiteSpace(name)) diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs index d17b2762f..5982c8983 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs @@ -16,7 +16,7 @@ public sealed class LanguagesConfig : IFieldPartitioning public static readonly LanguagesConfig English = new ( new Dictionary { - [Language.EN] = new LanguageConfig() + [Language.EN] = new LanguageConfig(), }, Language.EN); @@ -64,7 +64,7 @@ public sealed class LanguagesConfig : IFieldPartitioning var newLanguages = new Dictionary(values) { - [language] = new LanguageConfig(isOptional, ReadonlyList.Create(fallbacks)) + [language] = new LanguageConfig(isOptional, ReadonlyList.Create(fallbacks)), }; return Build(newLanguages, master); diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs index e6568a247..b887c1d7a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs @@ -28,7 +28,7 @@ public sealed record Role(string Name, PermissionSet? Permissions = null, JsonOb PermissionIds.AppSchemasRead, PermissionIds.AppSearch, PermissionIds.AppTranslate, - PermissionIds.AppUsage + PermissionIds.AppUsage, ]; public const string Editor = nameof(Editor); diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs index 361d5640c..58c30bfcc 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs @@ -51,7 +51,7 @@ public sealed class Roles WithoutPrefix(PermissionIds.AppRules), WithoutPrefix(PermissionIds.AppSchemas), WithoutPrefix(PermissionIds.AppWorkflows)), - JsonValue.Object()) + JsonValue.Object()), }; public static readonly Roles Empty = new Roles(new ReadonlyDictionary()); diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Assets/AssetType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Assets/AssetType.cs index ef4c6b33a..3432f26cd 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Assets/AssetType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Assets/AssetType.cs @@ -12,5 +12,5 @@ public enum AssetType Unknown, Image, Audio, - Video + Video, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs index dde9be417..59eacf5cd 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/GeoJsonParseResult.cs @@ -12,5 +12,5 @@ public enum GeoJsonParseResult Success, InvalidLatitude, InvalidLongitude, - InvalidValue + InvalidValue, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs index 0f778140d..193e56194 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/StatusChange.cs @@ -11,5 +11,5 @@ public enum StatusChange { Change, Published, - Unpublished + Unpublished, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs index 65738fd66..2606c7e1e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs @@ -36,7 +36,7 @@ public sealed record Workflow(Status Initial, ReadonlyDictionary { - [Status.Draft] = WorkflowTransition.Always + [Status.Draft] = WorkflowTransition.Always, }.ToReadonlyDictionary(), StatusColors.Archived, NoUpdate.Always), [Status.Draft] = @@ -44,7 +44,7 @@ public sealed record Workflow(Status Initial, ReadonlyDictionary { [Status.Archived] = WorkflowTransition.Always, - [Status.Published] = WorkflowTransition.Always + [Status.Published] = WorkflowTransition.Always, }.ToReadonlyDictionary(), StatusColors.Draft), [Status.Published] = @@ -52,9 +52,9 @@ public sealed record Workflow(Status Initial, ReadonlyDictionary { [Status.Archived] = WorkflowTransition.Always, - [Status.Draft] = WorkflowTransition.Always + [Status.Draft] = WorkflowTransition.Always, }.ToReadonlyDictionary(), - StatusColors.Published) + StatusColors.Published), }.ToReadonlyDictionary(), null, name); } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WriteContent.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WriteContent.cs index 14e580356..1f9b2a936 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WriteContent.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WriteContent.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Google.Protobuf.WellKnownTypes; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Contents; @@ -50,7 +51,7 @@ public record WriteContent : AppEntity ScheduleJob = ScheduleJob, SchemaId = SchemaId, Status = CurrentVersion.Status, - Version = Version + Version = Version, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs b/backend/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs index 6e98ea198..25d6377da 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs @@ -12,7 +12,7 @@ public static class PartitioningExtensions private static readonly HashSet AllowedPartitions = new HashSet(StringComparer.OrdinalIgnoreCase) { Partitioning.Language.Key, - Partitioning.Invariant.Key + Partitioning.Invariant.Key, }; public static bool IsValidPartitioning(this string? value) diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEventType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEventType.cs index 7eca9e27a..63f714ac0 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEventType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedAssetEventType.cs @@ -12,5 +12,5 @@ public enum EnrichedAssetEventType Created, Deleted, Annotated, - Updated + Updated, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEventType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEventType.cs index 68c20a3e4..a3195323c 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEventType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedContentEventType.cs @@ -15,5 +15,5 @@ public enum EnrichedContentEventType StatusChanged, Updated, Unpublished, - ReferenceUpdated + ReferenceUpdated, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventType.cs index 94304d8f2..68739f9a0 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/EnrichedEvents/EnrichedSchemaEventType.cs @@ -13,5 +13,5 @@ public enum EnrichedSchemaEventType Deleted, Published, Unpublished, - Updated + Updated, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayCalculatedDefaultValue.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayCalculatedDefaultValue.cs index c74a7a6b8..f87fcaa2d 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayCalculatedDefaultValue.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayCalculatedDefaultValue.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum ArrayCalculatedDefaultValue { EmptyArray, - Null + Null, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetPreviewMode.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetPreviewMode.cs index 83120fb30..21e91c5a7 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetPreviewMode.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetPreviewMode.cs @@ -11,5 +11,5 @@ public enum AssetPreviewMode { ImageAndFileName, Image, - FileName + FileName, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldEditor.cs index 2400eb788..9530d3064 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldEditor.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum BooleanFieldEditor { Checkbox, - Toggle + Toggle, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeCalculatedDefaultValue.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeCalculatedDefaultValue.cs index e9f938e65..07da69316 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeCalculatedDefaultValue.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeCalculatedDefaultValue.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum DateTimeCalculatedDefaultValue { Now, - Today + Today, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldEditor.cs index ecc9d7f50..4e52f49ea 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldEditor.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum DateTimeFieldEditor { Date, - DateTime + DateTime, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs index 7f92cd16a..8966311be 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs @@ -29,7 +29,7 @@ public sealed class FieldNames : ReadonlyList "status.next", "version", "translationStatus", - "translationStatusAverage" + "translationStatusAverage", ]; public static readonly new FieldNames Empty = new FieldNames([]); diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs index 8278622bb..7bb86036a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs @@ -31,7 +31,7 @@ public sealed record FieldRule { return new FieldRule(FieldRuleAction.Disable, field) { - Condition = condition + Condition = condition, }; } @@ -39,7 +39,7 @@ public sealed record FieldRule { return new FieldRule(FieldRuleAction.Hide, field) { - Condition = condition + Condition = condition, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRuleAction.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRuleAction.cs index e06961134..85f746aea 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRuleAction.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRuleAction.cs @@ -11,5 +11,5 @@ public enum FieldRuleAction { Disable, Hide, - Require + Require, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldEditor.cs index ac9689f58..5321fc959 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldEditor.cs @@ -9,5 +9,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum GeolocationFieldEditor { - Map + Map, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs index 34d146dce..5b8f190c3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/FieldSurrogate.cs @@ -36,7 +36,7 @@ public sealed class FieldSurrogate IsHidden = source.IsHidden, IsDisabled = source.IsDisabled, Partitioning = source.Partitioning.Key, - Properties = source.RawProperties + Properties = source.RawProperties, }; } @@ -49,7 +49,7 @@ public sealed class FieldSurrogate IsLocked = source.IsLocked, IsHidden = source.IsHidden, IsDisabled = source.IsDisabled, - Properties = source.RawProperties + Properties = source.RawProperties, }; } @@ -66,7 +66,7 @@ public sealed class FieldSurrogate FieldCollection = new FieldCollection(nested), IsLocked = IsLocked, IsHidden = IsHidden, - IsDisabled = IsDisabled + IsDisabled = IsDisabled, }; } else @@ -75,7 +75,7 @@ public sealed class FieldSurrogate { IsLocked = IsLocked, IsHidden = IsHidden, - IsDisabled = IsDisabled + IsDisabled = IsDisabled, }; } } @@ -86,7 +86,7 @@ public sealed class FieldSurrogate { IsLocked = IsLocked, IsHidden = IsHidden, - IsDisabled = IsDisabled + IsDisabled = IsDisabled, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldEditor.cs index 6d42e3456..98b5359b4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldEditor.cs @@ -12,5 +12,5 @@ public enum NumberFieldEditor Input, Radio, Dropdown, - Stars + Stars, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs index 97d80d1ed..01e752215 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldEditor.cs @@ -14,5 +14,5 @@ public enum ReferencesFieldEditor Tags, Checkboxes, Input, - Radio + Radio, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs index 002e4a57b..4d2133c33 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs @@ -175,7 +175,7 @@ public record Schema : AppEntity { FieldCollection = FieldCollection.Remove(fieldId), FieldsInLists = FieldsInLists.Remove(field.Name), - FieldsInReferences = FieldsInReferences.Remove(field.Name) + FieldsInReferences = FieldsInReferences.Remove(field.Name), }; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs index b830ed8f6..48c0208a9 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaType.cs @@ -11,5 +11,5 @@ public enum SchemaType { Default, Singleton, - Component + Component, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringContentType.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringContentType.cs index fb3b661a2..fa69fd98b 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringContentType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringContentType.cs @@ -11,5 +11,5 @@ public enum StringContentType { Unspecified, Html, - Markdown + Markdown, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldEditor.cs index edf20c28c..9e0324d4f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldEditor.cs @@ -18,5 +18,5 @@ public enum StringFieldEditor RichText, Slug, StockPhoto, - TextArea + TextArea, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldEditor.cs index 5a45582b2..2509800ec 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldEditor.cs @@ -11,5 +11,5 @@ public enum TagsFieldEditor { Tags, Checkboxes, - Dropdown + Dropdown, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldNormalization.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldNormalization.cs index 39bbd5324..5612bbd96 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldNormalization.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldNormalization.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum TagsFieldNormalization { None, - Schema + Schema, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldEditor.cs index 3d3061927..5a6674038 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldEditor.cs @@ -9,5 +9,5 @@ namespace Squidex.Domain.Apps.Core.Schemas; public enum UIFieldEditor { - Separator + Separator, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs index 95b5b2866..6498a4c63 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/ResolveFromPreviousPartitioning.cs @@ -36,7 +36,7 @@ public sealed class ResolveFromPreviousPartitioning(LanguagesConfig languages) : { source = new ContentFieldData { - [languages.Master] = value + [languages.Master] = value, }; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/UpdateValues.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/UpdateValues.cs index da2e2d86b..3cb1465ea 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/UpdateValues.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ConvertContent/UpdateValues.cs @@ -37,7 +37,7 @@ public sealed class UpdateValues(ContentData existingData, IScriptEngine scriptE // Reuse the vars to save allocations. vars ??= new ScriptVars { - ["$data"] = existingData + ["$data"] = existingData, }; // Give access to the current update statement to carry extra values from the request. diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs index 946b0a629..f1beee237 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs @@ -148,7 +148,7 @@ public static class SchemaSynchronizer Name = targetField.Name, Partitioning = partitioning, Properties = targetField.RawProperties, - FieldId = id + FieldId = id, }; sourceIds.Add(id); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/AssetQueryModel.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/AssetQueryModel.cs index 046813cc7..a6b4c9384 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/AssetQueryModel.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/AssetQueryModel.cs @@ -18,81 +18,81 @@ public static class AssetQueryModel { new FilterField(FilterSchema.String, "id") { - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }, new FilterField(FilterSchema.Boolean, "isDeleted") { - Description = FieldDescriptions.EntityIsDeleted + Description = FieldDescriptions.EntityIsDeleted, }, new FilterField(FilterSchema.DateTime, "created") { - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }, new FilterField(SharedSchemas.User, "createdBy") { - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }, new FilterField(FilterSchema.DateTime, "lastModified") { - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }, new FilterField(SharedSchemas.User, "lastModifiedBy") { - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }, new FilterField(FilterSchema.String, "status") { - Description = FieldDescriptions.ContentStatus + Description = FieldDescriptions.ContentStatus, }, new FilterField(FilterSchema.String, "version") { - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }, new FilterField(FilterSchema.String, "fileHash") { - Description = FieldDescriptions.AssetFileHash + Description = FieldDescriptions.AssetFileHash, }, new FilterField(FilterSchema.String, "fileName") { - Description = FieldDescriptions.AssetFileName + Description = FieldDescriptions.AssetFileName, }, new FilterField(FilterSchema.Number, "fileSize") { - Description = FieldDescriptions.AssetFileSize + Description = FieldDescriptions.AssetFileSize, }, new FilterField(FilterSchema.Number, "fileVersion") { - Description = FieldDescriptions.AssetFileVersion + Description = FieldDescriptions.AssetFileVersion, }, new FilterField(FilterSchema.Boolean, "isProtected") { - Description = FieldDescriptions.AssetIsProtected + Description = FieldDescriptions.AssetIsProtected, }, new FilterField(FilterSchema.Any, "metadata") { - Description = FieldDescriptions.AssetMetadata + Description = FieldDescriptions.AssetMetadata, }, new FilterField(FilterSchema.String, "mimeType") { - Description = FieldDescriptions.AssetMimeType + Description = FieldDescriptions.AssetMimeType, }, new FilterField(FilterSchema.String, "slug") { - Description = FieldDescriptions.AssetSlug + Description = FieldDescriptions.AssetSlug, }, new FilterField(FilterSchema.StringArray, "tags") { - Description = FieldDescriptions.AssetTags + Description = FieldDescriptions.AssetTags, }, new FilterField(FilterSchema.String, "type") { - Description = FieldDescriptions.AssetType - } + Description = FieldDescriptions.AssetType, + }, }; var schema = new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; return new QueryModel { Schema = schema }; diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/ContentQueryModel.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/ContentQueryModel.cs index 154d6698a..d0ebd09c5 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/ContentQueryModel.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/ContentQueryModel.cs @@ -20,40 +20,40 @@ public static class ContentQueryModel { new FilterField(FilterSchema.String, "id") { - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }, new FilterField(FilterSchema.Boolean, "isDeleted") { - Description = FieldDescriptions.EntityIsDeleted + Description = FieldDescriptions.EntityIsDeleted, }, new FilterField(FilterSchema.DateTime, "created") { - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }, new FilterField(SharedSchemas.User, "createdBy") { - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }, new FilterField(FilterSchema.DateTime, "lastModified") { - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }, new FilterField(SharedSchemas.User, "lastModifiedBy") { - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }, new FilterField(FilterSchema.Number, "version") { - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }, new FilterField(SharedSchemas.Status, "status") { - Description = FieldDescriptions.ContentStatus + Description = FieldDescriptions.ContentStatus, }, new FilterField(SharedSchemas.Status, "newStatus", IsNullable: true) { - Description = FieldDescriptions.ContentNewStatus - } + Description = FieldDescriptions.ContentNewStatus, + }, }; var translationStatusSchema = BuildTranslationStatus(partitionResolver); @@ -66,13 +66,13 @@ public static class ContentQueryModel fields.Add(new FilterField(dataSchema, "data") { - Description = FieldDescriptions.ContentData + Description = FieldDescriptions.ContentData, }); } var filterSchema = new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; return new QueryModel { Schema = filterSchema }; @@ -86,13 +86,13 @@ public static class ContentQueryModel { fields.Add(new FilterField(FilterSchema.Number, key) { - Description = string.Format(CultureInfo.InvariantCulture, FieldDescriptions.TranslationStatusLanguage, key) + Description = string.Format(CultureInfo.InvariantCulture, FieldDescriptions.TranslationStatusLanguage, key), }); } return new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs index 860b89254..875820032 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterExtensions.cs @@ -54,7 +54,7 @@ public static class FilterExtensions var filterable = new FilterField( new FilterSchema(FilterSchemaType.Object) { - Fields = partitionFields.ToReadonlyList() + Fields = partitionFields.ToReadonlyList(), }, field.Name, FieldDescription(schemaName, field)); @@ -64,7 +64,7 @@ public static class FilterExtensions var dataSchema = new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; return dataSchema; diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs index 5e9931e75..dd39b2ad3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateFilters/FilterVisitor.cs @@ -63,7 +63,7 @@ internal sealed class FilterVisitor : IFieldVisitor { - ["schemaIds"] = field.Properties.SchemaIds ?? [] + ["schemaIds"] = field.Properties.SchemaIds ?? [], }; property.UniqueItems = !field.Properties.AllowDuplicates; @@ -263,7 +263,7 @@ internal sealed class JsonTypeVisitor : IFieldVisitor { - [Rule.Id] = Rule - }.ToReadonlyDictionary() + [Rule.Id] = Rule, + }.ToReadonlyDictionary(), }; } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs index 4edd077ac..2de5917c4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleEventFormatter.cs @@ -95,7 +95,7 @@ public partial class RuleEventFormatter(IJsonSerializer serializer, IEnumerable< { var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; return await templateEngine.RenderAsync(template, vars); @@ -109,7 +109,7 @@ public partial class RuleEventFormatter(IJsonSerializer serializer, IEnumerable< Event = @event, AppId = @event.AppId.Id, AppName = @event.AppId.Name, - User = Admin() + User = Admin(), }; var result = (await scriptEngine.ExecuteAsync(vars, script)).ToString(); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleFieldEditor.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleFieldEditor.cs index 8ceedb60c..7cc737623 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleFieldEditor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleFieldEditor.cs @@ -17,5 +17,5 @@ public enum RuleFieldEditor Password, Text, TextArea, - Url + Url, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleResult.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleResult.cs index ffe6c439b..9285210aa 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleResult.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleResult.cs @@ -12,5 +12,5 @@ public enum RuleResult Pending, Success, Failed, - Timeout + Timeout, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs index 9d050df60..f99eb8855 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs @@ -334,7 +334,7 @@ public sealed class RuleService( EnrichedEvent = enrichedEvent, EnrichmentError = null, Rule = state.Rule, - SkipReason = SkipReason.ConditionDoesNotMatch + SkipReason = SkipReason.ConditionDoesNotMatch, }; continue; @@ -370,7 +370,7 @@ public sealed class RuleService( EventName = enrichedEvent.Name, ExecutionPartition = enrichedEvent.Partition, Expires = expires, - RuleId = rule.Id + RuleId = rule.Id, }; try diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs index be357ae7e..4636f20e4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs @@ -63,7 +63,7 @@ public sealed class RuleTypeProvider : ITypeProvider Description = metadata.Description, IconColor = metadata.IconColor, IconImage = metadata.IconImage, - ReadMore = metadata.ReadMore + ReadMore = metadata.ReadMore, }; foreach (var property in actionType.GetProperties()) @@ -72,7 +72,7 @@ public sealed class RuleTypeProvider : ITypeProvider { var actionProperty = new RuleActionProperty { - Name = property.Name.ToCamelCase() + Name = property.Name.ToCamelCase(), }; var display = property.GetCustomAttribute(); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/SkipReason.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/SkipReason.cs index 98b7d1b89..23a600dc4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/SkipReason.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/SkipReason.cs @@ -20,5 +20,5 @@ public enum SkipReason NoTrigger = 1 << 6, TooOld = 1 << 7, WrongEvent = 1 << 8, - WrongEventForTrigger = 1 << 9 + WrongEventForTrigger = 1 << 9, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/StringAsyncJintExtension.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/StringAsyncJintExtension.cs index 5042ab815..dbd1fcadd 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/StringAsyncJintExtension.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Extensions/StringAsyncJintExtension.cs @@ -55,7 +55,7 @@ public sealed class StringAsyncJintExtension(ITranslator translator, IChatAgent var request = new ChatRequest { - Prompt = prompt + Prompt = prompt, }; var result = await chatAgent.PromptAsync(request, ct: ct); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintUser.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintUser.cs index a0feb7e35..0edc93bba 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintUser.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/Internal/JintUser.cs @@ -60,7 +60,7 @@ public static class JintUser ["isClient"] = isClient, ["isUser"] = !isClient, ["name"] = name, - ["claims"] = claims + ["claims"] = claims, }; return JsValue.FromObject(engine, result); diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JsonType.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JsonType.cs index 3ca6777cd..83d245c2f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JsonType.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/JsonType.cs @@ -15,5 +15,5 @@ public enum JsonType Function, Number, Object, - String + String, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptScope.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptScope.cs index adf7dc071..75e655e0c 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptScope.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptScope.cs @@ -19,5 +19,5 @@ public enum ScriptScope ContentTrigger = 32, SchemaTrigger = 128, Transform = 256, - UsageTrigger = 512 + UsageTrigger = 512, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs index 4a1e83306..8d820ef96 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Scripting/ScriptingCompleter.cs @@ -28,7 +28,7 @@ public sealed partial class ScriptingCompleter(IEnumerable de { Fields = ReadonlyList.Create( new FilterField(new FilterSchema(FilterSchemaType.Object), "my-field"), - new FilterField(FilterSchema.String, "my-field.iv")) + new FilterField(FilterSchema.String, "my-field.iv")), }; public IReadOnlyList Trigger(string type) @@ -484,7 +484,7 @@ public sealed partial class ScriptingCompleter(IEnumerable de { AllowedValues = allowedValues, Description = description, - DeprecationReason = decprecationReason + DeprecationReason = decprecationReason, }; for (int i = 0; i < parts.Length; i++) diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 71c4d400b..07efdf50a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -28,8 +28,8 @@ - - + + diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs index c79dc35e5..580e0a3aa 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Templates/FluidTemplateEngine.cs @@ -20,7 +20,7 @@ public sealed class FluidTemplateEngine : ITemplateEngine { options.MemberAccessStrategy = new UnsafeMemberAccessStrategy { - MemberNameStrategy = MemberNameStrategies.CamelCase + MemberNameStrategy = MemberNameStrategies.CamelCase, }; foreach (var extension in extensions) diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationAction.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationAction.cs index 8dcaac7da..e11be6d7b 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationAction.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationAction.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.ValidateContent; public enum ValidationAction { Upsert, - Publish + Publish, } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationMode.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationMode.cs index b41bfc8db..071496df0 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationMode.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ValidationMode.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Core.ValidateContent; public enum ValidationMode { Default, - Optimized + Optimized, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs index f9b07e806..425cd2874 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs @@ -34,7 +34,7 @@ public static class AppProviderExtensions result ??= []; result[schemaId] = schema; } - else if (result == null || !result.TryGetValue(schemaId, out _)) + else if (result == null || !result.ContainsKey(schemaId)) { var resolvedEntity = await appProvider.GetSchemaAsync(appId, schemaId, false, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppChatTools.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppChatTools.cs index 104e17db8..e9234e8cc 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppChatTools.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppChatTools.cs @@ -42,9 +42,9 @@ public sealed class AppChatTools(IJsonSerializer serializer, IUrlGenerator urlGe Id = x.Key, ClientId = $"{context.App.Name}:{x.Key}", ClientSecret = "obfuscated", - x.Value.Role + x.Value.Role, }), - Url = urlGenerator.ClientsUI(context.App.NamedId()) + Url = urlGenerator.ClientsUI(context.App.NamedId()), }; var json = serializer.Serialize(result, true); @@ -66,9 +66,9 @@ public sealed class AppChatTools(IJsonSerializer serializer, IUrlGenerator urlGe { Iso2Code = x.Key, IsMaster = context.App.Languages.Master.Equals(x.Key), - x.Value.IsOptional + x.Value.IsOptional, }), - Url = urlGenerator.LanguagesUI(context.App.NamedId()) + Url = urlGenerator.LanguagesUI(context.App.NamedId()), }; var json = serializer.Serialize(result, true); @@ -88,9 +88,9 @@ public sealed class AppChatTools(IJsonSerializer serializer, IUrlGenerator urlGe Roles = context.App.Roles.Custom.Select(x => new { - x.Name + x.Name, }), - Url = urlGenerator.RolesUI(context.App.NamedId()) + Url = urlGenerator.RolesUI(context.App.NamedId()), }; var json = serializer.Serialize(result, true); @@ -111,7 +111,7 @@ public sealed class AppChatTools(IJsonSerializer serializer, IUrlGenerator urlGe { Name = context.App.Plan?.PlanId, }, - Url = urlGenerator.PlansUI(context.App.NamedId()) + Url = urlGenerator.PlansUI(context.App.NamedId()), }; var json = serializer.Serialize(result, true); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppEventDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppEventDeleter.cs index ffdeed326..aa03876b2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppEventDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppEventDeleter.cs @@ -17,7 +17,7 @@ public sealed class AppEventDeleter(IEventStore eventStore) : IDeleter public Task DeleteAppAsync(App app, CancellationToken ct) { - var streamFilter = StreamFilter.Prefix($"([a-zA-Z0-9]+)-{app.Id}"); + var streamFilter = StreamFilter.Prefix($"%-{app.Id}"); return eventStore.DeleteAsync(streamFilter, ct); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs index 879166abe..484c69ba3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/AppPermanentDeleter.cs @@ -28,7 +28,7 @@ public sealed class AppPermanentDeleter( private readonly HashSet consumingTypes = [ typeRegistry.GetName(), - typeRegistry.GetName() + typeRegistry.GetName(), ]; public StreamFilter EventsFilter { get; } = StreamFilter.Prefix("app-"); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs index 98e894741..bfc27b256 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/DefaultAppLogStore.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Text; using CsvHelper; using CsvHelper.Configuration; +using NodaTime; using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; @@ -34,7 +35,7 @@ public sealed class DefaultAppLogStore(IRequestLogStore requestLogStore) : IAppL private static readonly CsvConfiguration CsvConfiguration = new CsvConfiguration(CultureInfo.InvariantCulture) { DetectDelimiter = false, - Delimiter = "|" + Delimiter = "|", }; Task IDeleter.DeleteAppAsync(App app, @@ -53,7 +54,7 @@ public sealed class DefaultAppLogStore(IRequestLogStore requestLogStore) : IAppL var storedRequest = new Request { - Key = appId.ToString() + Key = appId.ToString(), }; Append(storedRequest, FieldAuthClientId, request.UserClientId); @@ -74,7 +75,7 @@ public sealed class DefaultAppLogStore(IRequestLogStore requestLogStore) : IAppL return requestLogStore.LogAsync(storedRequest, ct); } - public async Task ReadLogAsync(DomainId appId, DateTime fromDate, DateTime toDate, Stream stream, + public async Task ReadLogAsync(DomainId appId, Instant fromTime, Instant toTime, Stream stream, CancellationToken ct = default) { Guard.NotNull(appId); @@ -100,7 +101,7 @@ public sealed class DefaultAppLogStore(IRequestLogStore requestLogStore) : IAppL await csv.NextRecordAsync(); - await foreach (var request in requestLogStore.QueryAllAsync(appId.ToString(), fromDate, toDate, ct)) + await foreach (var request in requestLogStore.QueryAllAsync(appId.ToString(), fromTime, toTime, ct)) { csv.WriteField(request.Timestamp.ToString()); csv.WriteField(GetString(request, FieldRequestPath)); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs index a1181ecbf..c1051e8f3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/IAppLogStore.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using NodaTime; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Apps; @@ -14,6 +15,6 @@ public interface IAppLogStore Task LogAsync(DomainId appId, RequestLog request, CancellationToken ct = default); - Task ReadLogAsync(DomainId appId, DateTime fromDate, DateTime toDate, Stream stream, + Task ReadLogAsync(DomainId appId, Instant fromTime, Instant toTime, Stream stream, CancellationToken ct = default); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs index ba732eab2..50b9535da 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Indexes/AppsIndex.cs @@ -239,7 +239,7 @@ public sealed class AppsIndex( return appCache.RemoveAsync( [ GetCacheKey(id), - GetCacheKey(name) + GetCacheKey(name), ]); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs index 90e298e27..06fc43509 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs @@ -83,7 +83,7 @@ public sealed class RolePermissionsProvider var schemaNames = new List { - Permission.Any + Permission.Any, }; schemaNames.AddRange(schemas.Select(x => x.Name)); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs index 045016666..d9c368e3c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/TemplateCommandMiddleware.cs @@ -80,7 +80,7 @@ public sealed class TemplateCommandMiddleware( new RulesSynchronizer(cliLog), new SchemasSynchronizer(cliLog), new WorkflowsSynchronizer(cliLog), - new ContentsSynchronizer(cliLog) + new ContentsSynchronizer(cliLog), }; foreach (var target in targets) @@ -121,7 +121,7 @@ public sealed class TemplateCommandMiddleware( AppName = app.Name, ClientId = $"{app.Name}:{client.Key}", ClientSecret = client.Value.Secret, - Url = url + Url = url, })); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs index 71bc7766e..12e40555f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetChangedTriggerHandler.cs @@ -43,7 +43,7 @@ public sealed class AssetChangedTriggerHandler( { var result = new EnrichedAssetEvent { - Type = EnrichedAssetEventType.Created + Type = EnrichedAssetEventType.Created, }; SimpleMapper.Map(asset, result); @@ -125,7 +125,7 @@ public sealed class AssetChangedTriggerHandler( // Script vars are just wrappers over dictionaries for better performance. var vars = new EventScriptVars { - Event = @event + Event = @event, }; return scriptEngine.Evaluate(vars, assetTrigger.Condition); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs index ba49159b2..8eddbdefb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetPermanentDeleter.cs @@ -17,7 +17,7 @@ public sealed class AssetPermanentDeleter(IAssetFileStore assetFileStore, TypeRe { private readonly HashSet consumingTypes = [ - typeRegistry.GetName() + typeRegistry.GetName(), ]; public StreamFilter EventsFilter { get; } = StreamFilter.Prefix("asset-"); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Commands/BulkUpdateAssetType.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Commands/BulkUpdateAssetType.cs index f3780e43c..77247f468 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Commands/BulkUpdateAssetType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Commands/BulkUpdateAssetType.cs @@ -11,5 +11,5 @@ public enum BulkUpdateAssetType { Annotate, Move, - Delete + Delete, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs index 7bb26ed29..41e0d3493 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetDomainObject.cs @@ -239,7 +239,7 @@ public partial class AssetDomainObject( MimeType = command.File.MimeType, FileName = command.File.FileName, FileSize = command.File.FileSize, - Slug = command.File.FileName.ToAssetSlug() + Slug = command.File.FileName.ToAssetSlug(), }); } @@ -249,7 +249,7 @@ public partial class AssetDomainObject( { MimeType = command.File.MimeType, FileVersion = Snapshot.FileVersion + 1, - FileSize = command.File.FileSize + FileSize = command.File.FileSize, }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderOperation.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderOperation.cs index f0c9c9ee0..526d52418 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderOperation.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/AssetFolderOperation.cs @@ -37,7 +37,7 @@ public sealed class AssetFolderOperation : OperationContextBase { App = app, Command = command, - CommandId = id + CommandId = id, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/Guards/ScriptingExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/Guards/ScriptingExtensions.cs index 494eed2a9..517ed512a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/Guards/ScriptingExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/DomainObject/Guards/ScriptingExtensions.cs @@ -19,7 +19,7 @@ public static class ScriptingExtensions { AsContext = true, CanDisallow = true, - CanReject = true + CanReject = true, }; public static async Task ExecuteCreateScriptAsync(this AssetOperation operation, CreateAsset create, @@ -49,9 +49,9 @@ public static class ScriptingExtensions MimeType = create.File.MimeType, ParentId = create.ParentId, ParentPath = parentPath, - Tags = create.Tags + Tags = create.Tags, }, - Operation = "Create" + Operation = "Create", }; var asset = new AssetEntityScriptVars @@ -67,7 +67,7 @@ public static class ScriptingExtensions MimeType = create.File.MimeType, ParentId = create.ParentId, ParentPath = await GetPathAsync(operation, create.ParentId, ct), - Tags = create.Tags?.ToReadonlyList() + Tags = create.Tags?.ToReadonlyList(), }; await ExecuteScriptAsync(operation, script, vars, asset, ct); @@ -95,9 +95,9 @@ public static class ScriptingExtensions FileName = update.File.FileName, FileSize = update.File.FileSize, MimeType = update.File.MimeType, - Tags = update.Tags + Tags = update.Tags, }, - Operation = "Update" + Operation = "Update", }; return ExecuteScriptAsync(operation, script, vars, null, ct); @@ -123,9 +123,9 @@ public static class ScriptingExtensions Metadata = annotate.Metadata, FileName = annotate.FileName, FileSlug = annotate.Slug, - Tags = annotate.Tags + Tags = annotate.Tags, }, - Operation = "Annotate" + Operation = "Annotate", }; return ExecuteScriptAsync(operation, script, vars, null, ct); @@ -149,9 +149,9 @@ public static class ScriptingExtensions Command = new AssetCommandScriptVars { ParentId = move.ParentId, - ParentPath = parentPath + ParentPath = parentPath, }, - Operation = "Move" + Operation = "Move", }; await ExecuteScriptAsync(operation, script, vars, null, ct); @@ -172,9 +172,9 @@ public static class ScriptingExtensions { Command = new AssetCommandScriptVars { - Permanent = delete.Permanent + Permanent = delete.Permanent, }, - Operation = "Delete" + Operation = "Delete", }; return ExecuteScriptAsync(operation, script, vars, null, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/CalculateTokens.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/CalculateTokens.cs index 949fe0678..7a9ac2f8e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/CalculateTokens.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/CalculateTokens.cs @@ -29,7 +29,7 @@ public sealed class CalculateTokens(IUrlGenerator urlGenerator, IJsonSerializer { a = asset.AppId.Name, i = asset.Id.ToString(), - u = url + u = url, }; var json = serializer.SerializeToBytes(token); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/ScriptAsset.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/ScriptAsset.cs index 8c040ccb0..1ac32eb7a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/ScriptAsset.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/Queries/Steps/ScriptAsset.cs @@ -33,7 +33,7 @@ public sealed class ScriptAsset(IScriptEngine scriptEngine) : IAssetEnricherStep { AppId = context.App.Id, AppName = context.App.Name, - User = context.UserPrincipal + User = context.UserPrincipal, }; var preScript = context.App.AssetScripts.QueryPre; @@ -42,7 +42,7 @@ public sealed class ScriptAsset(IScriptEngine scriptEngine) : IAssetEnricherStep { var options = new ScriptOptions { - AsContext = true + AsContext = true, }; await scriptEngine.ExecuteAsync(vars, preScript, options, ct); @@ -74,8 +74,8 @@ public sealed class ScriptAsset(IScriptEngine scriptEngine) : IAssetEnricherStep MimeType = asset.MimeType, ParentId = asset.ParentId, ParentPath = null, - Tags = asset.Tags?.ToReadonlyList() - } + Tags = asset.Tags?.ToReadonlyList(), + }, }; vars.CopyFrom(sharedVars); @@ -84,7 +84,7 @@ public sealed class ScriptAsset(IScriptEngine scriptEngine) : IAssetEnricherStep { AsContext = true, CanDisallow = true, - CanReject = true + CanReject = true, }; await scriptEngine.ExecuteAsync(vars, script, options, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs index 487e65ec4..b4ac8dbc4 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/RecursiveDeleter.cs @@ -26,7 +26,7 @@ public sealed class RecursiveDeleter( { private readonly HashSet consumingTypes = [ - typeRegistry.GetName() + typeRegistry.GetName(), ]; public StreamFilter EventsFilter { get; } = StreamFilter.Prefix("assetFolder-"); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupJob.cs index ae01b1fd3..a5846913d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupJob.cs @@ -44,10 +44,10 @@ public sealed class BackupJob( new Dictionary { [ArgAppId] = app.Id.ToString(), - [ArgAppName] = app.Name + [ArgAppName] = app.Name, }) with { - AppId = app.NamedId() + AppId = app.NamedId(), }; } @@ -85,7 +85,7 @@ public sealed class BackupJob( var backupUsers = new UserMapping(context.Actor); var backupContext = new BackupContext(appId, backupUsers, writer); - var streamFilter = StreamFilter.Prefix($"[^\\-]*-{appId}"); + var streamFilter = StreamFilter.Prefix($"%-{appId}"); await foreach (var storedEvent in eventStore.QueryAllAsync(streamFilter, ct: ct)) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs index 591330d3c..ca9bd7db2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/BackupVersion.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Entities.Backup; public enum BackupVersion { V2, - V1 + V1, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs index 672ca3700..e02c4644c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/Model/CompatibleStoredEvent.cs @@ -37,7 +37,7 @@ public sealed class CompatibleStoredEvent Data = CompatibleEventData.V1(stored.Data), EventPosition = stored.EventPosition.Token!, EventStreamNumber = stored.EventStreamNumber, - StreamName = stored.StreamName + StreamName = stored.StreamName, }; } @@ -79,7 +79,7 @@ public sealed class CompatibleEventData { Type = data.Type, EventPayload = data.Payload, - EventHeaders = data.Headers + EventHeaders = data.Headers, }; } @@ -110,7 +110,7 @@ public sealed class NewEvent EventType = stored.Data.Type, EventHeaders = stored.Data.Headers, EventPayload = stored.Data.Payload, - StreamName = stored.StreamName + StreamName = stored.StreamName, }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs index 31bf2748b..073ede539 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs @@ -69,7 +69,7 @@ public sealed class RestoreJob( new Dictionary { [ArgUrl] = url.ToString(), - [ArgName] = appName ?? string.Empty + [ArgName] = appName ?? string.Empty, }); } @@ -92,7 +92,7 @@ public sealed class RestoreJob( // Required argument. Url = url, // Optional argument. - NewAppName = context.Job.Arguments.GetValueOrDefault(ArgName) + NewAppName = context.Job.Arguments.GetValueOrDefault(ArgName), }; // Use a readable name to describe the job. @@ -190,7 +190,7 @@ public sealed class RestoreJob( ContributorId = run.Actor.Identifier, IgnoreActor = true, IgnorePlans = true, - Role = Role.Owner + Role = Role.Owner, }); await run.LogAsync("Assigned current user."); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Billing/ConfigPlansProvider.cs b/backend/src/Squidex.Domain.Apps.Entities/Billing/ConfigPlansProvider.cs index 1d8a0fcc1..93811463b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Billing/ConfigPlansProvider.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Billing/ConfigPlansProvider.cs @@ -16,7 +16,7 @@ public sealed class ConfigPlansProvider : IBillingPlans MaxApiCalls = -1, MaxAssetSize = -1, MaxContributors = -1, - BlockingApiCalls = -1 + BlockingApiCalls = -1, }; private readonly Dictionary plansById = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Assets.cs b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Assets.cs index c1a714f65..edad59ab0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Assets.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Assets.cs @@ -102,7 +102,7 @@ public sealed partial class UsageGate : IAssetUsageTracker var counters = new Counters { [AssetsKeys.TotalSize] = fileSize, - [AssetsKeys.TotalAssets] = count + [AssetsKeys.TotalAssets] = count, }; var appKey = AppAssetsKey(appId); @@ -110,7 +110,7 @@ public sealed partial class UsageGate : IAssetUsageTracker var tasks = new List { usageTracker.TrackAsync(date, appKey, null, counters, ct), - usageTracker.TrackAsync(SummaryDate, appKey, null, counters, ct) + usageTracker.TrackAsync(SummaryDate, appKey, null, counters, ct), }; var (_, _, teamId) = await GetPlanForAppAsync(appId, true, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Rules.cs b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Rules.cs index 5e04d5be9..1d5c4154d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Rules.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.Rules.cs @@ -96,14 +96,14 @@ public sealed partial class UsageGate : IRuleUsageTracker { [RulesKeys.TotalCreated] = created, [RulesKeys.TotalSucceeded] = succeeded, - [RulesKeys.TotalFailed] = failed + [RulesKeys.TotalFailed] = failed, }; var appKey = AppRulesKey(appId); var tasks = new List { - usageTracker.TrackAsync(SummaryDate, appKey, ruleId.ToString(), counters, ct) + usageTracker.TrackAsync(SummaryDate, appKey, ruleId.ToString(), counters, ct), }; if (date != default) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs index de17a8f4e..fa5b39d91 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Billing/UsageGate.cs @@ -63,7 +63,7 @@ public sealed partial class UsageGate( AppId = appId, Usage = usage, UsageLimit = blockLimit, - Users = GetUsers(app) + Users = GetUsers(app), }; await messaging.PublishAsync(notification, ct: ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentTriggerHandler.cs index ad379e25c..cd26c4fc2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/CommentTriggerHandler.cs @@ -49,7 +49,7 @@ public sealed class CommentTriggerHandler(IScriptEngine scriptEngine, IUserResol { var enrichedEvent = new EnrichedCommentEvent { - MentionedUser = user + MentionedUser = user, }; enrichedEvent.Name = "UserMentioned"; @@ -73,7 +73,7 @@ public sealed class CommentTriggerHandler(IScriptEngine scriptEngine, IUserResol // Script vars are just wrappers over dictionaries for better performance. var vars = new EventScriptVars { - Event = @event + Event = @event, }; return scriptEngine.Evaluate(vars, commentTrigger.Condition); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs index 624e53681..e36c232fb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Collaboration/EmailUserNotifications.cs @@ -59,7 +59,7 @@ public sealed class EmailUserNotifications( { ApiCalls = usage, ApiCallsLimit = usageLimit, - AppName = app.DisplayName() + AppName = app.DisplayName(), }; return SendEmailAsync("Usage", diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs index a2c2b959b..7ce875592 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/BackupContents.cs @@ -208,7 +208,7 @@ public sealed class BackupContents(Rebuilder rebuilder, IUrlGenerator urlGenerat return new Urls { Assets = urlGenerator.AssetContentBase(), - AssetsApp = urlGenerator.AssetContentBase(appName) + AssetsApp = urlGenerator.AssetContentBase(appName), }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContentType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContentType.cs index 06b9098ac..3f122c4ed 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContentType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Commands/BulkUpdateContentType.cs @@ -16,5 +16,5 @@ public enum BulkUpdateContentType Patch, Update, Validate, - EnrichDefaults + EnrichDefaults, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs index e155bbe41..941baf0c8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentChangedTriggerHandler.cs @@ -55,7 +55,7 @@ public sealed class ContentChangedTriggerHandler( { var result = new EnrichedContentEvent { - Type = EnrichedContentEventType.Created + Type = EnrichedContentEventType.Created, }; SimpleMapper.Map(content, result); @@ -101,7 +101,7 @@ public sealed class ContentChangedTriggerHandler( { var result = new EnrichedContentEvent { - Type = EnrichedContentEventType.ReferenceUpdated + Type = EnrichedContentEventType.ReferenceUpdated, }; SimpleMapper.Map(content, result); @@ -291,7 +291,7 @@ public sealed class ContentChangedTriggerHandler( // Script vars are just wrappers over dictionaries for better performance. var vars = new EventScriptVars { - Event = @event + Event = @event, }; return scriptEngine.Evaluate(vars, schema.Condition); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEventDeleter.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEventDeleter.cs index 98e3d0566..bc64b2d55 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEventDeleter.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentEventDeleter.cs @@ -25,7 +25,7 @@ public sealed class ContentEventDeleter(IContentRepository contentRepository, IE public async Task DeleteSchemAsync(App app, Schema schema, CancellationToken ct) { - await foreach (var id in contentRepository.StreamIds(app.Id, schema.Id, SearchScope.All, ct)) + await foreach (var id in contentRepository.StreamIds(app.Id, [schema.Id], SearchScope.All, ct)) { var streamFilter = StreamFilter.Prefix($"content-{DomainId.Combine(app.Id, id)}"); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs index 48b4f874d..56090ef55 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentSchedulerProcess.cs @@ -76,7 +76,7 @@ public sealed class ContentSchedulerProcess( ContentId = id, SchemaId = content.SchemaId, Status = job.Status, - StatusJobId = job.Id + StatusJobId = job.Id, }; await commandBus.PublishAsync(command, default); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs index 0e92584e8..0954481de 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ContentsSearchSource.cs @@ -40,7 +40,7 @@ public sealed class ContentsSearchSource( var textQuery = new TextQuery($"{query}~", 10) { - RequiredSchemaIds = schemaIds + RequiredSchemaIds = schemaIds, }; var ids = await contentTextIndexer.SearchAsync(context.App, textQuery, context.Scope(), ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs index 2346221e6..c4c1aa6a7 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentDomainObject.State.cs @@ -37,7 +37,7 @@ public partial class ContentDomainObject AppId = e.AppId, ScheduleJob = null, SchemaId = e.SchemaId, - CurrentVersion = new ContentVersion(e.Status, e.Data) + CurrentVersion = new ContentVersion(e.Status, e.Data), }; break; @@ -97,14 +97,14 @@ public partial class ContentDomainObject case ContentUpdated e when snapshot.NewVersion != null: newSnapshot = snapshot with { - NewVersion = snapshot.NewVersion with { Data = e.Data.UseSameFields(Data()) } + NewVersion = snapshot.NewVersion with { Data = e.Data.UseSameFields(Data()) }, }; break; case ContentUpdated e: newSnapshot = snapshot with { - CurrentVersion = snapshot.CurrentVersion with { Data = e.Data.UseSameFields(CurrentData()) } + CurrentVersion = snapshot.CurrentVersion with { Data = e.Data.UseSameFields(CurrentData()) }, }; break; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperation.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperation.cs index f8c9d3633..bc3cd330c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperation.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DomainObject/ContentOperation.cs @@ -43,7 +43,7 @@ public sealed class ContentOperation(IServiceProvider serviceProvider, Func ExecuteCreateScriptAsync(this ContentOperation operation, ContentData data, Status status, @@ -38,7 +38,7 @@ public static class ScriptingExtensions OldStatus = default, Operation = "Create", Status = status, - StatusOld = default + StatusOld = default, }); return TransformAsync(operation, script, vars, ct); @@ -63,7 +63,7 @@ public static class ScriptingExtensions OldStatus = operation.Snapshot.EditingStatus, Operation = "Update", Status = operation.Snapshot.EditingStatus, - StatusOld = default + StatusOld = default, }); return TransformAsync(operation, script, vars, ct); @@ -89,7 +89,7 @@ public static class ScriptingExtensions Operation = change.ToString(), Status = status, StatusOld = operation.Snapshot.EditingStatus, - Validate = Validate(operation, status) + Validate = Validate(operation, status), }); return TransformAsync(operation, script, vars, ct); @@ -115,7 +115,7 @@ public static class ScriptingExtensions Operation = "Delete", Permanent = permanent, Status = operation.Snapshot.EditingStatus, - StatusOld = default + StatusOld = default, }); return ExecuteAsync(operation, script, vars, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs index ac368b0f7..b43a26e6a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs @@ -112,7 +112,7 @@ public sealed class DynamicContentWorkflow(IScriptEngine scriptEngine, IAppProvi { var vars = new DataScriptVars { - Data = data + Data = data, }; return scriptEngine.Evaluate(vars, condition.Expression); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLResolver.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLResolver.cs index 51a704bcd..b4cff707e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLResolver.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLResolver.cs @@ -10,7 +10,6 @@ using GraphQL; using GraphQL.DI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using NodaTime; using Squidex.Caching; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; @@ -32,7 +31,7 @@ public sealed class CachingGraphQLResolver( { private readonly GraphQLOptions options = options.Value; - private sealed record CacheEntry(GraphQLSchema Model, string Hash, Instant Created); + private sealed record CacheEntry(GraphQLSchema Model, SchemasHashKey HashKey); public float SortOrder => 0; @@ -73,20 +72,18 @@ public sealed class CachingGraphQLResolver( }, async entry => { - var (created, hash) = await schemasHash.GetCurrentHashAsync(app); + var hashKey = await schemasHash.GetCurrentHashAsync(app); - return created < entry.Created || string.Equals(hash, entry.Hash, StringComparison.OrdinalIgnoreCase); + return hashKey.Equals(entry.HashKey); }); } private async Task CreateModelAsync(App app) { var schemasList = await serviceProvider.GetRequiredService().GetSchemasAsync(app.Id); - var schemasKey = await schemasHash.ComputeHashAsync(app, schemasList); + var schemasKey = SchemasHashKey.Create(app, schemasList); - var now = SystemClock.Instance.GetCurrentInstant(); - - return new CacheEntry(new Builder(app, options).BuildSchema(schemasList), schemasKey, now); + return new CacheEntry(new Builder(app, options).BuildSchema(schemasList), schemasKey); } private static object CreateCacheKey(DomainId appId, string etag) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationMutations.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationMutations.cs index 2c4522fb5..820daee03 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationMutations.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationMutations.cs @@ -46,7 +46,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = contentType, Resolver = ContentActions.Create.Resolver, Description = $"Creates an {schemaInfo.DisplayName} content.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -56,7 +56,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = contentType, Resolver = ContentActions.Update.Resolver, Description = $"Update an {schemaInfo.DisplayName} content by id.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -66,7 +66,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = contentType, Resolver = ContentActions.Upsert.Resolver, Description = $"Upsert an {schemaInfo.DisplayName} content by id.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -76,7 +76,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = contentType, Resolver = ContentActions.Patch.Resolver, Description = $"Patch an {schemaInfo.DisplayName} content by id.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -86,7 +86,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = contentType, Resolver = ContentActions.ChangeStatus.Resolver, Description = $"Change a {schemaInfo.DisplayName} content.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -96,7 +96,7 @@ internal sealed class ApplicationMutations : ObjectGraphType ResolvedType = EntitySavedGraphType.NonNull, Resolver = ContentActions.Delete.Resolver, Description = $"Delete an {schemaInfo.DisplayName} content.", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); AddField(new FieldTypeWithSchemaNamedId @@ -107,7 +107,7 @@ internal sealed class ApplicationMutations : ObjectGraphType Resolver = ContentActions.ChangeStatus.Resolver, Description = $"Publish a {schemaInfo.DisplayName} content.", DeprecationReason = $"Use 'change{schemaInfo.TypeName}Content' instead", - SchemaId = schemaInfo.Schema.NamedId() + SchemaId = schemaInfo.Schema.NamedId(), }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs index 6669f6865..0aa800a01 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs @@ -59,7 +59,7 @@ internal sealed class ApplicationQueries : ObjectGraphType Resolver = Find.Resolver, DeprecationReason = deprecatedReason, Description = $"Find an {schemaInfo.DisplayName} content by id.", - SchemaId = schemaInfo.Schema.Id + SchemaId = schemaInfo.Schema.Id, }); } @@ -73,7 +73,7 @@ internal sealed class ApplicationQueries : ObjectGraphType Resolver = FindSingleton.Resolver, DeprecationReason = null, Description = $"Find an {schemaInfo.DisplayName} singleton.", - SchemaId = schemaInfo.Schema.Id + SchemaId = schemaInfo.Schema.Id, }); } @@ -87,7 +87,7 @@ internal sealed class ApplicationQueries : ObjectGraphType Resolver = QueryOrReferencing.Query, DeprecationReason = deprecatedReason, Description = $"Query {schemaInfo.DisplayName} content items.", - SchemaId = schemaInfo.Schema.Id + SchemaId = schemaInfo.Schema.Id, }); var contentResultTyp = builder.GetContentResultType(schemaInfo); @@ -105,7 +105,7 @@ internal sealed class ApplicationQueries : ObjectGraphType Resolver = QueryOrReferencing.QueryWithTotal, DeprecationReason = deprecatedReason, Description = $"Query {schemaInfo.DisplayName} content items with total count.", - SchemaId = schemaInfo.Schema.Id + SchemaId = schemaInfo.Schema.Id, }); } @@ -124,7 +124,7 @@ internal sealed class ApplicationQueries : ObjectGraphType Arguments = QueryByIds.Arguments, ResolvedType = new NonNullGraphType(new ListGraphType(new NonNullGraphType(unionType))), Resolver = QueryByIds.Resolver, - Description = "Query content items by IDs across schemeas." + Description = "Query content items by IDs across schemeas.", }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationSubscriptions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationSubscriptions.cs index 713589d88..a4a4de172 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationSubscriptions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationSubscriptions.cs @@ -22,7 +22,7 @@ internal sealed class ApplicationSubscriptions : ObjectGraphType ResolvedType = SharedTypes.EnrichedAssetEvent, Resolver = null, StreamResolver = AssetActions.Subscription.Resolver, - Description = "Subscribe to asset events." + Description = "Subscribe to asset events.", }); AddField(new FieldType @@ -32,7 +32,7 @@ internal sealed class ApplicationSubscriptions : ObjectGraphType ResolvedType = SharedTypes.EnrichedContentEvent, Resolver = null, StreamResolver = ContentActions.Subscription.Resolver, - Description = "Subscribe to content events." + Description = "Subscribe to content events.", }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs index a5cfe3527..351e761b3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs @@ -30,7 +30,7 @@ internal static class AssetActions { Name = "path", Description = FieldDescriptions.JsonPath, - DefaultValue = null + DefaultValue = null, }, ]; @@ -56,7 +56,7 @@ internal static class AssetActions { Name = "id", Description = "The ID of the asset (usually GUID).", - DefaultValue = null + DefaultValue = null, }, ]; @@ -77,25 +77,25 @@ internal static class AssetActions { Name = "top", Description = FieldDescriptions.QueryTop, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "skip", Description = FieldDescriptions.QuerySkip, - DefaultValue = 0 + DefaultValue = 0, }, new QueryArgument(Scalars.String) { Name = "filter", Description = FieldDescriptions.QueryFilter, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "orderby", Description = FieldDescriptions.QueryOrderBy, - DefaultValue = null + DefaultValue = null, }, ]; @@ -128,7 +128,7 @@ internal static class AssetActions { Name = "type", Description = FieldDescriptions.EventType, - DefaultValue = null + DefaultValue = null, }, ]; @@ -147,7 +147,7 @@ internal static class AssetActions var subscription = new AssetSubscription { - Type = fieldContext.GetArgument("type") + Type = fieldContext.GetArgument("type"), }; var observable = diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs index 85b872f2d..41d62d44f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs @@ -28,7 +28,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "id", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.Id, - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }); AddField(new FieldType @@ -36,7 +36,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "version", ResolvedType = Scalars.NonNullInt, Resolver = EntityResolvers.Version, - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }); AddField(new FieldType @@ -44,7 +44,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "created", ResolvedType = Scalars.NonNullDateTime, Resolver = EntityResolvers.Created, - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }); AddField(new FieldType @@ -52,7 +52,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "createdBy", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.CreatedBy, - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -60,7 +60,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "createdByUser", ResolvedType = UserGraphType.NonNull, Resolver = EntityResolvers.CreatedByUser, - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -68,7 +68,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "lastModified", ResolvedType = Scalars.NonNullDateTime, Resolver = EntityResolvers.LastModified, - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }); AddField(new FieldType @@ -76,7 +76,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "lastModifiedBy", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.LastModifiedBy, - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -84,7 +84,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "lastModifiedByUser", ResolvedType = UserGraphType.NonNull, Resolver = EntityResolvers.LastModifiedByUser, - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -92,7 +92,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "mimeType", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.MimeType), - Description = FieldDescriptions.AssetMimeType + Description = FieldDescriptions.AssetMimeType, }); AddField(new FieldType @@ -100,7 +100,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "url", ResolvedType = Scalars.NonNullString, Resolver = Url, - Description = FieldDescriptions.AssetUrl + Description = FieldDescriptions.AssetUrl, }); AddField(new FieldType @@ -108,7 +108,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "thumbnailUrl", ResolvedType = Scalars.String, Resolver = ThumbnailUrl, - Description = FieldDescriptions.AssetThumbnailUrl + Description = FieldDescriptions.AssetThumbnailUrl, }); AddField(new FieldType @@ -116,7 +116,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "fileName", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.FileName), - Description = FieldDescriptions.AssetFileName + Description = FieldDescriptions.AssetFileName, }); AddField(new FieldType @@ -124,7 +124,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "fileHash", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.FileHash), - Description = FieldDescriptions.AssetFileHash + Description = FieldDescriptions.AssetFileHash, }); AddField(new FieldType @@ -132,7 +132,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "fileType", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.FileName.FileType()), - Description = FieldDescriptions.AssetFileType + Description = FieldDescriptions.AssetFileType, }); AddField(new FieldType @@ -140,7 +140,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "fileSize", ResolvedType = Scalars.NonNullInt, Resolver = Resolve(x => x.FileSize), - Description = FieldDescriptions.AssetFileSize + Description = FieldDescriptions.AssetFileSize, }); AddField(new FieldType @@ -148,7 +148,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "fileVersion", ResolvedType = Scalars.NonNullInt, Resolver = Resolve(x => x.FileVersion), - Description = FieldDescriptions.AssetFileVersion + Description = FieldDescriptions.AssetFileVersion, }); AddField(new FieldType @@ -156,7 +156,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "parentId", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.ParentId.ToString()), - Description = FieldDescriptions.AssetParentId + Description = FieldDescriptions.AssetParentId, }); AddField(new FieldType @@ -164,7 +164,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "slug", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.Slug), - Description = FieldDescriptions.AssetSlug + Description = FieldDescriptions.AssetSlug, }); AddField(new FieldType @@ -172,7 +172,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "isProtected", ResolvedType = Scalars.NonNullBoolean, Resolver = Resolve(x => x.IsProtected), - Description = FieldDescriptions.AssetIsProtected + Description = FieldDescriptions.AssetIsProtected, }); AddField(new FieldType @@ -181,7 +181,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType ResolvedType = Scalars.NonNullBoolean, Resolver = Resolve(x => x.Type == AssetType.Image), Description = FieldDescriptions.AssetIsImage, - DeprecationReason = "Use 'type' field instead." + DeprecationReason = "Use 'type' field instead.", }); AddField(new FieldType @@ -190,7 +190,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType ResolvedType = Scalars.Int, Resolver = Resolve(x => x.Metadata.GetInt32(KnownMetadataKeys.PixelWidth)), Description = FieldDescriptions.AssetPixelWidth, - DeprecationReason = "Use 'metadata' field instead." + DeprecationReason = "Use 'metadata' field instead.", }); AddField(new FieldType @@ -199,7 +199,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType ResolvedType = Scalars.Int, Resolver = Resolve(x => x.Metadata.GetInt32(KnownMetadataKeys.PixelHeight)), Description = FieldDescriptions.AssetPixelHeight, - DeprecationReason = "Use 'metadata' field instead." + DeprecationReason = "Use 'metadata' field instead.", }); AddField(new FieldType @@ -207,7 +207,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "type", ResolvedType = Scalars.NonNullAssetType, Resolver = Resolve(x => x.Type), - Description = FieldDescriptions.AssetType + Description = FieldDescriptions.AssetType, }); AddField(new FieldType @@ -216,7 +216,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Arguments = AssetActions.Metadata.Arguments, ResolvedType = Scalars.Json, Resolver = AssetActions.Metadata.Resolver, - Description = FieldDescriptions.AssetMetadata + Description = FieldDescriptions.AssetMetadata, }); AddField(new FieldType @@ -224,7 +224,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "metadataText", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.MetadataText), - Description = FieldDescriptions.AssetMetadataText + Description = FieldDescriptions.AssetMetadataText, }); AddField(new FieldType @@ -232,7 +232,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "tags", ResolvedType = Scalars.NonNullStrings, Resolver = Resolve(x => x.TagNames), - Description = FieldDescriptions.AssetTags + Description = FieldDescriptions.AssetTags, }); AddField(new FieldType @@ -240,7 +240,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "editToken", ResolvedType = Scalars.String, Resolver = Resolve(x => x.EditToken), - Description = FieldDescriptions.EditToken + Description = FieldDescriptions.EditToken, }); AddField(new FieldType @@ -248,7 +248,7 @@ internal sealed class AssetGraphType : SharedObjectGraphType Name = "sourceUrl", ResolvedType = Scalars.String, Resolver = SourceUrl, - Description = FieldDescriptions.AssetSourceUrl + Description = FieldDescriptions.AssetSourceUrl, }); Description = "An asset"; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetsResultGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetsResultGraphType.cs index dc19e246a..bcbe28926 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetsResultGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetsResultGraphType.cs @@ -25,7 +25,7 @@ internal sealed class AssetsResultGraphType : SharedObjectGraphType x.Total), - Description = FieldDescriptions.AssetsTotal + Description = FieldDescriptions.AssetsTotal, }); AddField(new FieldType @@ -33,7 +33,7 @@ internal sealed class AssetsResultGraphType : SharedObjectGraphType x), - Description = FieldDescriptions.AssetsItems + Description = FieldDescriptions.AssetsItems, }); Description = "List of assets and total count of assets."; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/EnrichedAssetEventGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/EnrichedAssetEventGraphType.cs index b13631174..533b84c5b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/EnrichedAssetEventGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/EnrichedAssetEventGraphType.cs @@ -27,7 +27,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Type), - Description = FieldDescriptions.EventType + Description = FieldDescriptions.EventType, }); AddField(new FieldType @@ -35,7 +35,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Id.ToString()), - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }); AddField(new FieldType @@ -43,7 +43,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Version), - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }); AddField(new FieldType @@ -51,7 +51,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Created.ToDateTimeUtc()), - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }); AddField(new FieldType @@ -59,7 +59,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.CreatedBy.ToString()), - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -67,7 +67,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.CreatedBy), - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -75,7 +75,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.LastModified.ToDateTimeUtc()), - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }); AddField(new FieldType @@ -83,7 +83,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.LastModifiedBy.ToString()), - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -91,7 +91,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.LastModifiedBy), - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -99,7 +99,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.MimeType), - Description = FieldDescriptions.AssetMimeType + Description = FieldDescriptions.AssetMimeType, }); AddField(new FieldType @@ -107,7 +107,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.FileName), - Description = FieldDescriptions.AssetFileName + Description = FieldDescriptions.AssetFileName, }); AddField(new FieldType @@ -131,7 +131,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.FileHash), - Description = FieldDescriptions.AssetFileHash + Description = FieldDescriptions.AssetFileHash, }); AddField(new FieldType @@ -139,7 +139,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.FileName.FileType()), - Description = FieldDescriptions.AssetFileType + Description = FieldDescriptions.AssetFileType, }); AddField(new FieldType @@ -147,7 +147,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.FileSize), - Description = FieldDescriptions.AssetFileSize + Description = FieldDescriptions.AssetFileSize, }); AddField(new FieldType @@ -155,7 +155,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.FileVersion), - Description = FieldDescriptions.AssetFileVersion + Description = FieldDescriptions.AssetFileVersion, }); AddField(new FieldType @@ -163,7 +163,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Slug), - Description = FieldDescriptions.AssetSlug + Description = FieldDescriptions.AssetSlug, }); AddField(new FieldType @@ -171,7 +171,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.IsProtected), - Description = FieldDescriptions.AssetIsProtected + Description = FieldDescriptions.AssetIsProtected, }); AddField(new FieldType @@ -180,7 +180,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.AssetType == AssetType.Image), Description = FieldDescriptions.AssetIsImage, - DeprecationReason = "Use 'type' field instead." + DeprecationReason = "Use 'type' field instead.", }); AddField(new FieldType @@ -188,7 +188,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.AssetType), - Description = FieldDescriptions.AssetType + Description = FieldDescriptions.AssetType, }); AddField(new FieldType @@ -197,7 +197,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Metadata.GetInt32(KnownMetadataKeys.PixelWidth)), Description = FieldDescriptions.AssetPixelWidth, - DeprecationReason = "Use 'metadata' field instead." + DeprecationReason = "Use 'metadata' field instead.", }); AddField(new FieldType @@ -206,7 +206,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType x.Metadata.GetInt32(KnownMetadataKeys.PixelHeight)), Description = FieldDescriptions.AssetPixelHeight, - DeprecationReason = "Use 'metadata' field instead." + DeprecationReason = "Use 'metadata' field instead.", }); AddField(new FieldType @@ -215,7 +215,7 @@ internal sealed class EnrichedAssetEventGraphType : SharedObjectGraphType ResolvedType = Scalars.Json, Resolver = FieldVisitor.JsonPath, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = fieldInfo.Field.Name + SourceName = fieldInfo.Field.Name, }); } @@ -55,7 +55,7 @@ internal sealed class ComponentGraphType : ObjectGraphType ResolvedType = resolvedType, Resolver = resolver, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = fieldInfo.Field.Name + SourceName = fieldInfo.Field.Name, }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs index 9282428eb..f20bc699d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs @@ -32,7 +32,7 @@ internal static class ContentActions { Name = "path", Description = FieldDescriptions.JsonPath, - DefaultValue = null + DefaultValue = null, }, ]; @@ -57,7 +57,7 @@ internal static class ContentActions { Name = "path", Description = FieldDescriptions.JsonPath, - DefaultValue = null + DefaultValue = null, }, ]; @@ -69,13 +69,13 @@ internal static class ContentActions { Name = "id", Description = FieldDescriptions.EntityId, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "version", Description = FieldDescriptions.QueryVersion, - DefaultValue = null + DefaultValue = null, }, ]; @@ -107,7 +107,7 @@ internal static class ContentActions { Name = "version", Description = FieldDescriptions.QueryVersion, - DefaultValue = null + DefaultValue = null, }, ]; @@ -159,43 +159,43 @@ internal static class ContentActions { Name = "top", Description = FieldDescriptions.QueryTop, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "skip", Description = FieldDescriptions.QuerySkip, - DefaultValue = 0 + DefaultValue = 0, }, new QueryArgument(Scalars.String) { Name = "filter", Description = FieldDescriptions.QueryFilter, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "orderby", Description = FieldDescriptions.QueryOrderBy, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "search", Description = FieldDescriptions.QuerySearch, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "collation", Description = FieldDescriptions.QueryCollation, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "random", Description = FieldDescriptions.QueryRandom, - DefaultValue = null + DefaultValue = null, }, ]; @@ -289,25 +289,25 @@ internal static class ContentActions { Name = "data", Description = FieldDescriptions.ContentRequestData, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Boolean) { Name = "publish", Description = FieldDescriptions.ContentRequestPublish, - DefaultValue = false + DefaultValue = false, }, new QueryArgument(Scalars.String) { Name = "status", Description = FieldDescriptions.ContentRequestOptionalStatus, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "id", Description = FieldDescriptions.ContentRequestOptionalId, - DefaultValue = null + DefaultValue = null, }, ]; } @@ -317,7 +317,7 @@ internal static class ContentActions var command = new CreateContent { // The data is converted from input args. - Data = c.GetArgument("data") + Data = c.GetArgument("data"), }; var status = c.GetArgument("status"); @@ -345,37 +345,37 @@ internal static class ContentActions { Name = "id", Description = FieldDescriptions.EntityId, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = FieldDescriptions.ContentRequestData, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Boolean) { Name = "publish", Description = FieldDescriptions.ContentRequestPublish, - DefaultValue = false + DefaultValue = false, }, new QueryArgument(Scalars.Boolean) { Name = "patch", Description = FieldDescriptions.ContentRequestPatch, - DefaultValue = false + DefaultValue = false, }, new QueryArgument(Scalars.String) { Name = "status", Description = FieldDescriptions.ContentRequestOptionalStatus, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "expectedVersion", Description = FieldDescriptions.EntityExpectedVersion, - DefaultValue = EtagVersion.Any + DefaultValue = EtagVersion.Any, }, ]; } @@ -416,19 +416,19 @@ internal static class ContentActions { Name = "id", Description = FieldDescriptions.EntityId, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = FieldDescriptions.ContentRequestData, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "expectedVersion", Description = FieldDescriptions.EntityExpectedVersion, - DefaultValue = EtagVersion.Any + DefaultValue = EtagVersion.Any, }, ]; } @@ -438,7 +438,7 @@ internal static class ContentActions return new PatchContent { // The data is converted from input args. - Data = c.GetArgument("data")! + Data = c.GetArgument("data")!, }; }); } @@ -453,19 +453,19 @@ internal static class ContentActions { Name = "id", Description = FieldDescriptions.EntityId, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = FieldDescriptions.ContentRequestData, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "expectedVersion", Description = FieldDescriptions.EntityExpectedVersion, - DefaultValue = EtagVersion.Any + DefaultValue = EtagVersion.Any, }, ]; } @@ -475,7 +475,7 @@ internal static class ContentActions return new PatchContent { // The data is converted from input args. - Data = c.GetArgument("data")! + Data = c.GetArgument("data")!, }; }); } @@ -488,25 +488,25 @@ internal static class ContentActions { Name = "id", Description = FieldDescriptions.EntityId, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.NonNullString) { Name = "status", Description = FieldDescriptions.ContentRequestStatus, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.DateTime) { Name = "dueTime", Description = FieldDescriptions.ContentRequestDueTime, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "expectedVersion", Description = FieldDescriptions.EntityExpectedVersion, - DefaultValue = EtagVersion.Any + DefaultValue = EtagVersion.Any, }, ]; @@ -531,13 +531,13 @@ internal static class ContentActions { Name = "id", Description = "The ID of the content (usually GUID).", - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.Int) { Name = "expectedVersion", Description = FieldDescriptions.EntityExpectedVersion, - DefaultValue = EtagVersion.Any + DefaultValue = EtagVersion.Any, }, ]; @@ -555,13 +555,13 @@ internal static class ContentActions { Name = "type", Description = FieldDescriptions.EventType, - DefaultValue = null + DefaultValue = null, }, new QueryArgument(Scalars.String) { Name = "schemaName", Description = FieldDescriptions.ContentSchemaName, - DefaultValue = null + DefaultValue = null, }, ]; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs index cf88b586b..56b29e239 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs @@ -57,7 +57,7 @@ internal static class ContentFields Name = "id", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.Id, - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }; public static readonly FieldType IdNoResolver = Id.WithouthResolver(); @@ -67,7 +67,7 @@ internal static class ContentFields Name = "version", ResolvedType = Scalars.NonNullInt, Resolver = EntityResolvers.Version, - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }; public static readonly FieldType VersionNoResolver = Version.WithouthResolver(); @@ -77,7 +77,7 @@ internal static class ContentFields Name = "created", ResolvedType = Scalars.NonNullDateTime, Resolver = EntityResolvers.Created, - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }; public static readonly FieldType CreatedNoResolver = Created.WithouthResolver(); @@ -87,7 +87,7 @@ internal static class ContentFields Name = "createdBy", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.CreatedBy, - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }; public static readonly FieldType CreatedByNoResolver = CreatedBy.WithouthResolver(); @@ -97,7 +97,7 @@ internal static class ContentFields Name = "createdByUser", ResolvedType = UserGraphType.NonNull, Resolver = EntityResolvers.CreatedByUser, - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }; public static readonly FieldType CreatedByUserNoResolver = CreatedByUser.WithouthResolver(); @@ -107,7 +107,7 @@ internal static class ContentFields Name = "lastModified", ResolvedType = Scalars.NonNullDateTime, Resolver = EntityResolvers.LastModified, - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }; public static readonly FieldType LastModifiedNoResolver = LastModified.WithouthResolver(); @@ -117,7 +117,7 @@ internal static class ContentFields Name = "lastModifiedBy", ResolvedType = Scalars.NonNullString, Resolver = EntityResolvers.LastModifiedBy, - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }; public static readonly FieldType LastModifiedByNoResolver = LastModifiedBy.WithouthResolver(); @@ -127,7 +127,7 @@ internal static class ContentFields Name = "lastModifiedByUser", ResolvedType = UserGraphType.NonNull, Resolver = EntityResolvers.LastModifiedByUser, - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }; public static readonly FieldType LastModifiedByUserNoResolver = LastModifiedByUser.WithouthResolver(); @@ -137,7 +137,7 @@ internal static class ContentFields Name = "status", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.Status.ToString().ToUpperInvariant()), - Description = FieldDescriptions.ContentStatus + Description = FieldDescriptions.ContentStatus, }; public static readonly FieldType StatusNoResolver = Status.WithouthResolver(); @@ -147,7 +147,7 @@ internal static class ContentFields Name = "statusColor", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x.StatusColor), - Description = FieldDescriptions.ContentStatusColor + Description = FieldDescriptions.ContentStatusColor, }; public static readonly FieldType StatusColorNoResolver = StatusColor.WithouthResolver(); @@ -157,7 +157,7 @@ internal static class ContentFields Name = "newStatus", ResolvedType = Scalars.String, Resolver = Resolve(x => x.NewStatus?.ToString().ToUpperInvariant()), - Description = FieldDescriptions.ContentNewStatus + Description = FieldDescriptions.ContentNewStatus, }; public static readonly FieldType NewStatusNoResolver = NewStatus.WithouthResolver(); @@ -167,7 +167,7 @@ internal static class ContentFields Name = "newStatusColor", ResolvedType = Scalars.String, Resolver = Resolve(x => x.NewStatusColor), - Description = FieldDescriptions.ContentStatusColor + Description = FieldDescriptions.ContentStatusColor, }; public static readonly FieldType NewStatusColorNoResolver = NewStatusColor.WithouthResolver(); @@ -177,7 +177,7 @@ internal static class ContentFields Name = "schemaId", ResolvedType = Scalars.NonNullString, Resolver = Resolve(x => x[Component.Discriminator].ToString()), - Description = FieldDescriptions.ContentSchemaId + Description = FieldDescriptions.ContentSchemaId, }; public static readonly FieldType SchemaIdNoResolver = SchemaId.WithouthResolver(); @@ -187,7 +187,7 @@ internal static class ContentFields Name = "schemaName", ResolvedType = Scalars.String, Resolver = Resolve(x => GetSchemaName(x)), - Description = FieldDescriptions.ContentSchemaName + Description = FieldDescriptions.ContentSchemaName, }; public static readonly FieldType SchemaNameNoResolver = SchemaName.WithouthResolver(); @@ -197,7 +197,7 @@ internal static class ContentFields Name = "url", ResolvedType = Scalars.NonNullString, Resolver = ContentResolvers.Url, - Description = FieldDescriptions.ContentUrl + Description = FieldDescriptions.ContentUrl, }; public static readonly FieldType UrlNoResolver = Url.WithouthResolver(); @@ -207,7 +207,7 @@ internal static class ContentFields Name = "editToken", ResolvedType = Scalars.String, Resolver = Resolve(x => x.EditToken), - Description = FieldDescriptions.EditToken + Description = FieldDescriptions.EditToken, }; public static readonly FieldType EditTokenNoResolver = EditToken.WithouthResolver(); @@ -217,7 +217,7 @@ internal static class ContentFields Name = "data__dynamic", ResolvedType = Scalars.Json, Resolver = Resolve(x => x.Data), - Description = FieldDescriptions.ContentData + Description = FieldDescriptions.ContentData, }; public static readonly FieldType DataDynamicNoResolver = DataDynamic.WithouthResolver(); @@ -227,7 +227,7 @@ internal static class ContentFields Name = "text", ResolvedType = Scalars.String, Resolver = Resolvers.Sync(x => x), - Description = FieldDescriptions.StringFieldText + Description = FieldDescriptions.StringFieldText, }; public static readonly FieldType StringFieldAssets = new FieldType @@ -235,7 +235,7 @@ internal static class ContentFields Name = "assets", ResolvedType = new NonNullGraphType(SharedTypes.AssetsList), Resolver = ResolveStringFieldAssets, - Description = FieldDescriptions.StringFieldAssets + Description = FieldDescriptions.StringFieldAssets, }; public static readonly FieldType RichTextFieldValue = new FieldType @@ -243,7 +243,7 @@ internal static class ContentFields Name = "value", ResolvedType = Scalars.Json, Resolver = Resolvers.Sync(x => x.Root), - Description = FieldDescriptions.RichTextFieldValue + Description = FieldDescriptions.RichTextFieldValue, }; public static readonly FieldType RichTextFieldAssets = new FieldType @@ -251,7 +251,7 @@ internal static class ContentFields Name = "assets", ResolvedType = new NonNullGraphType(SharedTypes.AssetsList), Resolver = ResolveRichTextFieldAssets, - Description = FieldDescriptions.RichTextFieldAssets + Description = FieldDescriptions.RichTextFieldAssets, }; public static readonly FieldType RichTextFieldMarkdown = new FieldType @@ -259,7 +259,7 @@ internal static class ContentFields Name = "markdown", ResolvedType = Scalars.NonNullString, Resolver = Resolvers.Sync(x => x.ToMarkdown()), - Description = FieldDescriptions.RichTextFieldMarkdown + Description = FieldDescriptions.RichTextFieldMarkdown, }; public static readonly FieldType RichTextFieldText = new FieldType @@ -267,7 +267,7 @@ internal static class ContentFields Name = "text", ResolvedType = Scalars.NonNullString, Resolver = Resolvers.Sync(x => x.ToText()), - Description = FieldDescriptions.RichTextFieldMarkdown + Description = FieldDescriptions.RichTextFieldMarkdown, }; public static readonly FieldType RichTextFieldHtml = new FieldType @@ -279,12 +279,12 @@ internal static class ContentFields { Name = "indentation", Description = FieldDescriptions.Indentation, - DefaultValue = 4 + DefaultValue = 4, }, ], ResolvedType = Scalars.NonNullString, Resolver = Resolvers.Sync((x, ctx, _) => x.ToHtml(ctx.GetArgument("indentation", 4))), - Description = FieldDescriptions.RichTextFieldHtml + Description = FieldDescriptions.RichTextFieldHtml, }; private static IFieldResolver Resolve(Func resolver) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs index 0bd818004..0bfef16c3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs @@ -60,7 +60,7 @@ internal sealed class ContentGraphType : ObjectGraphType Name = "data", ResolvedType = new NonNullGraphType(contentDataType), Resolver = ContentResolvers.Data, - Description = FieldDescriptions.ContentData + Description = FieldDescriptions.ContentData, }); } @@ -73,7 +73,7 @@ internal sealed class ContentGraphType : ObjectGraphType Name = "flatData", ResolvedType = new NonNullGraphType(contentDataTypeFlat), Resolver = ContentResolvers.FlatData, - Description = FieldDescriptions.ContentFlatData + Description = FieldDescriptions.ContentFlatData, }); } @@ -104,7 +104,7 @@ internal sealed class ContentGraphType : ObjectGraphType ResolvedType = new ListGraphType(new NonNullGraphType(contentType)), Resolver = ContentActions.QueryOrReferencing.Referencing, Description = $"Query {referencingSchemaInfo.DisplayName} content items.", - SchemaId = referencingSchemaInfo.Schema.Id + SchemaId = referencingSchemaInfo.Schema.Id, }); var contentResultTyp = builder.GetContentResultType(referencingSchemaInfo); @@ -121,7 +121,7 @@ internal sealed class ContentGraphType : ObjectGraphType ResolvedType = contentResultTyp, Resolver = ContentActions.QueryOrReferencing.ReferencingWithTotal, Description = $"Query {referencingSchemaInfo.DisplayName} content items with total count.", - SchemaId = referencingSchemaInfo.Schema.Id + SchemaId = referencingSchemaInfo.Schema.Id, }); } @@ -137,7 +137,7 @@ internal sealed class ContentGraphType : ObjectGraphType ResolvedType = new ListGraphType(new NonNullGraphType(contentType)), Resolver = ContentActions.QueryOrReferencing.References, Description = $"Query {referencesSchemaInfo.DisplayName} content items.", - SchemaId = referencesSchemaInfo.Schema.Id + SchemaId = referencesSchemaInfo.Schema.Id, }); var contentResultTyp = builder.GetContentResultType(referencesSchemaInfo); @@ -154,7 +154,7 @@ internal sealed class ContentGraphType : ObjectGraphType ResolvedType = contentResultTyp, Resolver = ContentActions.QueryOrReferencing.ReferencesWithTotal, Description = $"Query {referencesSchemaInfo.DisplayName} content items with total count.", - SchemaId = referencesSchemaInfo.Schema.Id + SchemaId = referencesSchemaInfo.Schema.Id, }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentResultGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentResultGraphType.cs index e8eaa228b..750f99c47 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentResultGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentResultGraphType.cs @@ -24,7 +24,7 @@ internal sealed class ContentResultGraphType : ObjectGraphType ResolvedType = Scalars.Json, Resolver = FieldVisitor.JsonPath, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = fieldInfo.Field.Name + SourceName = fieldInfo.Field.Name, }); } @@ -44,7 +44,7 @@ internal sealed class DataFlatGraphType : ObjectGraphType ResolvedType = resolvedType, Resolver = resolver, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = fieldInfo.Field.Name + SourceName = fieldInfo.Field.Name, }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataGraphType.cs index 4c7080f3a..4e34d5d9b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataGraphType.cs @@ -27,7 +27,7 @@ internal sealed class DataGraphType : ObjectGraphType var fieldGraphType = new ObjectGraphType { // The name is used for equal comparison. Therefore it is important to treat it as readonly. - Name = fieldInfo.LocalizedTypeDynamic + Name = fieldInfo.LocalizedTypeDynamic, }; foreach (var partitionKey in partitioning.AllKeys) @@ -39,7 +39,7 @@ internal sealed class DataGraphType : ObjectGraphType ResolvedType = Scalars.Json, Resolver = FieldVisitor.JsonPath, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = partitionKey + SourceName = partitionKey, }); } @@ -50,7 +50,7 @@ internal sealed class DataGraphType : ObjectGraphType Name = fieldInfo.FieldNameDynamic, ResolvedType = fieldGraphType, Resolver = ContentResolvers.Field, - SourceName = fieldInfo.Field.Name + SourceName = fieldInfo.Field.Name, }); } @@ -61,7 +61,7 @@ internal sealed class DataGraphType : ObjectGraphType var fieldGraphType = new ObjectGraphType { // The name is used for equal comparison. Therefore it is important to treat it as readonly. - Name = fieldInfo.LocalizedType + Name = fieldInfo.LocalizedType, }; foreach (var partitionKey in partitioning.AllKeys) @@ -73,7 +73,7 @@ internal sealed class DataGraphType : ObjectGraphType ResolvedType = resolvedType, Resolver = resolver, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = partitionKey + SourceName = partitionKey, }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs index 430caf68a..16b159273 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs @@ -31,7 +31,7 @@ internal sealed class DataInputGraphType : InputObjectGraphType var fieldGraphType = new InputObjectGraphType { // The name is used for equal comparison. Therefore it is important to treat it as readonly. - Name = fieldInfo.LocalizedInputType + Name = fieldInfo.LocalizedInputType, }; var partitioning = builder.ResolvePartition(((RootField)fieldInfo.Field).Partitioning); @@ -44,7 +44,7 @@ internal sealed class DataInputGraphType : InputObjectGraphType ResolvedType = resolvedType, Resolver = null, Description = fieldInfo.Field.RawProperties.Hints, - SourceName = partitionKey + SourceName = partitionKey, }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs index af69a617b..77532f0d4 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs @@ -32,7 +32,7 @@ internal sealed class EmbeddableStringGraphType : ObjectGraphType Name = "contents", ResolvedType = new NonNullGraphType(new ListGraphType(new NonNullGraphType(referenceType))), Resolver = ContentFields.ResolveStringFieldContents, - Description = FieldDescriptions.StringFieldReferences + Description = FieldDescriptions.StringFieldReferences, }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EnrichedContentEventGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EnrichedContentEventGraphType.cs index 9a12d2ebc..ea990222e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EnrichedContentEventGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EnrichedContentEventGraphType.cs @@ -24,7 +24,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Type), - Description = FieldDescriptions.EventType + Description = FieldDescriptions.EventType, }); AddField(new FieldType @@ -32,7 +32,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Id.ToString()), - Description = FieldDescriptions.EntityId + Description = FieldDescriptions.EntityId, }); AddField(new FieldType @@ -40,7 +40,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Version), - Description = FieldDescriptions.EntityVersion + Description = FieldDescriptions.EntityVersion, }); AddField(new FieldType @@ -48,7 +48,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Created.ToDateTimeUtc()), - Description = FieldDescriptions.EntityCreated + Description = FieldDescriptions.EntityCreated, }); AddField(new FieldType @@ -56,7 +56,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.CreatedBy.ToString()), - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -64,7 +64,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.CreatedBy), - Description = FieldDescriptions.EntityCreatedBy + Description = FieldDescriptions.EntityCreatedBy, }); AddField(new FieldType @@ -72,7 +72,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.LastModified.ToDateTimeUtc()), - Description = FieldDescriptions.EntityLastModified + Description = FieldDescriptions.EntityLastModified, }); AddField(new FieldType @@ -80,7 +80,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.LastModifiedBy.ToString()), - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -88,7 +88,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.LastModifiedBy), - Description = FieldDescriptions.EntityLastModifiedBy + Description = FieldDescriptions.EntityLastModifiedBy, }); AddField(new FieldType @@ -96,7 +96,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Status.ToString()), - Description = FieldDescriptions.ContentStatus + Description = FieldDescriptions.ContentStatus, }); AddField(new FieldType @@ -104,7 +104,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.NewStatus?.ToString()), - Description = FieldDescriptions.ContentNewStatus + Description = FieldDescriptions.ContentNewStatus, }); AddField(new FieldType @@ -112,7 +112,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.Data), - Description = FieldDescriptions.ContentData + Description = FieldDescriptions.ContentData, }); AddField(new FieldType @@ -120,7 +120,7 @@ internal sealed class EnrichedContentEventGraphType : SharedObjectGraphType x.DataOld), - Description = FieldDescriptions.ContentDataOld + Description = FieldDescriptions.ContentDataOld, }); Description = "An content event"; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedGraphType.cs index caf9ef373..fce07c24d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedGraphType.cs @@ -29,7 +29,7 @@ internal sealed class NestedGraphType : ObjectGraphType ResolvedType = Scalars.Json, Resolver = FieldVisitor.JsonPath, Description = nestedFieldInfo.Field.RawProperties.Hints, - SourceName = nestedFieldInfo.Field.Name + SourceName = nestedFieldInfo.Field.Name, }); } @@ -44,7 +44,7 @@ internal sealed class NestedGraphType : ObjectGraphType ResolvedType = resolvedType, Resolver = resolver, Description = nestedFieldInfo.Field.RawProperties.Hints, - SourceName = nestedFieldInfo.Field.Name + SourceName = nestedFieldInfo.Field.Name, }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/RichTextGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/RichTextGraphType.cs index e102163bd..d3dcca51b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/RichTextGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/RichTextGraphType.cs @@ -36,7 +36,7 @@ internal sealed class RichTextGraphType : ObjectGraphType Name = "contents", ResolvedType = new NonNullGraphType(new ListGraphType(new NonNullGraphType(referenceType))), Resolver = ContentFields.ResolveRichTextFieldContents, - Description = FieldDescriptions.RichTextFieldReferences + Description = FieldDescriptions.RichTextFieldReferences, }); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfo.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfo.cs index 14c6ecbda..fe898cd1e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfo.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/SchemaInfo.cs @@ -60,7 +60,7 @@ internal sealed class SchemaInfo yield return new SchemaInfo(schema, typeName, typeNames) { - Fields = FieldInfo.Build(schema.Fields, $"{typeName}Data", typeNames).ToList() + Fields = FieldInfo.Build(schema.Fields, $"{typeName}Data", typeNames).ToList(), }; } } @@ -146,7 +146,7 @@ internal sealed class FieldInfo fieldTypeName, typeNames) { - Fields = nested + Fields = nested, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Directives/CacheDirective.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Directives/CacheDirective.cs index cc16ffca1..d2b8716d8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Directives/CacheDirective.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Directives/CacheDirective.cs @@ -22,7 +22,7 @@ public sealed class CacheDirective : Directive { Name = "duration", Description = "Cache duration in seconds.", - DefaultValue = 600 + DefaultValue = 600, }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntitySavedGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntitySavedGraphType.cs index d29f1fa01..fdb515b3b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntitySavedGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntitySavedGraphType.cs @@ -27,7 +27,7 @@ internal sealed class EntitySavedGraphType : SharedObjectGraphType Name = "id", Resolver = Resolve(x => x.Id), ResolvedType = Scalars.NonNullString, - Description = FieldDescriptions.UserId + Description = FieldDescriptions.UserId, }); AddField(new FieldType @@ -37,7 +37,7 @@ internal sealed class UserGraphType : SharedObjectGraphType Name = "displayName", Resolver = ResolveOrHide(x => x.Claims.DisplayName()), ResolvedType = Scalars.String, - Description = FieldDescriptions.UserDisplayName + Description = FieldDescriptions.UserDisplayName, }); AddField(new FieldType @@ -45,7 +45,7 @@ internal sealed class UserGraphType : SharedObjectGraphType Name = "email", Resolver = ResolveOrHide(x => x.Email), ResolvedType = Scalars.String, - Description = FieldDescriptions.UserEmail + Description = FieldDescriptions.UserEmail, }); Description = "A user that created or modified a content or asset."; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/CreateIndexJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/CreateIndexJob.cs index 2df7ed464..ac64c56fa 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/CreateIndexJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/CreateIndexJob.cs @@ -38,7 +38,7 @@ public sealed class CreateIndexJob(IContentRepository contentRepository) : IJobR [ArgAppId] = app.Id.ToString(), [ArgAppName] = app.Name, [ArgSchemaId] = schema.Id.ToString(), - [ArgSchemaName] = schema.Name + [ArgSchemaName] = schema.Name, }; foreach (var field in index) @@ -51,7 +51,7 @@ public sealed class CreateIndexJob(IContentRepository contentRepository) : IJobR TaskName, args) with { - AppId = app.NamedId() + AppId = app.NamedId(), }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/DropIndexJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/DropIndexJob.cs index f1f66bb5a..6d0214548 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/DropIndexJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Indexes/DropIndexJob.cs @@ -40,10 +40,10 @@ public sealed class DropIndexJob(IContentRepository contentRepository) : IJobRun [ArgAppName] = app.Name, [ArgSchemaId] = schema.Id.ToString(), [ArgSchemaName] = schema.Name, - [ArgIndexName] = name + [ArgIndexName] = name, }) with { - AppId = app.NamedId() + AppId = app.NamedId(), }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs index 756ad666f..5d56a880e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/ContentQueryParser.cs @@ -86,7 +86,7 @@ public class ContentQueryParser( var textQuery = new TextQuery(query.FullText, 1000) { - PreferredSchemaId = schema.Id + PreferredSchemaId = schema.Id, }; var fullTextIds = await textIndex.SearchAsync(context.App, textQuery, context.Scope(), ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/CalculateTokens.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/CalculateTokens.cs index 868fea03f..018967d78 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/CalculateTokens.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/CalculateTokens.cs @@ -25,7 +25,7 @@ public sealed class CalculateTokens(IUrlGenerator urlGenerator, IJsonSerializer a = content.AppId.Name, s = content.SchemaId.Name, i = content.Id.ToString(), - u = url + u = url, }; var json = serializer.SerializeToBytes(token); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs index 46c71f626..794f4e005 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ConvertData.cs @@ -130,7 +130,7 @@ public sealed class ConvertData( IgnoreNonMasterFields = true, IgnoreRequiredFields = false, // If field names are given we run the enrichment only on the specified fields. - FieldNames = fieldNames + FieldNames = fieldNames, }); } @@ -141,7 +141,7 @@ public sealed class ConvertData( { ResolveFallback = !context.IsFrontendClient && !context.NoResolveLanguages(), // If field names are given we run the enrichment only on the specified fields. - FieldNames = fieldNames + FieldNames = fieldNames, }); if (!context.IsFrontendClient) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithWorkflows.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithWorkflows.cs index 83543c851..f80c6baa7 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithWorkflows.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/EnrichWithWorkflows.cs @@ -43,7 +43,7 @@ public sealed class EnrichWithWorkflows(IContentWorkflow contentWorkflow) : ICon { content.NextStatuses = [ - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), ]; } else diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ScriptContent.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ScriptContent.cs index 2b4e338c7..f6a6c0112 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ScriptContent.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/Steps/ScriptContent.cs @@ -39,7 +39,7 @@ public sealed class ScriptContent(IScriptEngine scriptEngine) : IContentEnricher AppName = schema.AppId.Name, SchemaId = schema.Id, SchemaName = schema.Name, - User = context.UserPrincipal + User = context.UserPrincipal, }; var preScript = schema.Scripts.QueryPre; @@ -48,7 +48,7 @@ public sealed class ScriptContent(IScriptEngine scriptEngine) : IContentEnricher { var options = new ScriptOptions { - AsContext = true + AsContext = true, }; await scriptEngine.ExecuteAsync(vars, preScript, options, ct); @@ -71,7 +71,7 @@ public sealed class ScriptContent(IScriptEngine scriptEngine) : IContentEnricher Data = content.Data, DataOld = default, Status = content.Status, - StatusOld = default + StatusOld = default, }; vars.CopyFrom(sharedVars); @@ -80,7 +80,7 @@ public sealed class ScriptContent(IScriptEngine scriptEngine) : IContentEnricher { AsContext = true, CanDisallow = true, - CanReject = true + CanReject = true, }; content.Data = await scriptEngine.TransformAsync(vars, script, options, ct); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs index 4414b3962..afe0f64d1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Repositories/IContentRepository.cs @@ -17,10 +17,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.Repositories; public interface IContentRepository { - IAsyncEnumerable StreamIds(DomainId appId, DomainId schemaId, SearchScope scope, + IAsyncEnumerable StreamScheduledWithoutDataAsync(Instant now, SearchScope scope, CancellationToken ct = default); - IAsyncEnumerable StreamScheduledWithoutDataAsync(Instant now, SearchScope scope, + IAsyncEnumerable StreamIds(DomainId appId, HashSet? schemaIds, SearchScope scope, CancellationToken ct = default); IAsyncEnumerable StreamAll(DomainId appId, HashSet? schemaIds, SearchScope scope, @@ -47,7 +47,7 @@ public interface IContentRepository Task HasReferrersAsync(App app, DomainId reference, SearchScope scope, CancellationToken ct = default); - Task ResetScheduledAsync(DomainId appId, DomainId contentId, SearchScope scope, + Task ResetScheduledAsync(DomainId appId, DomainId id, SearchScope scope, CancellationToken ct = default); Task CreateIndexAsync(DomainId appId, DomainId schemaId, IndexDefinition index, diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/SearchScope.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/SearchScope.cs index 479660ba0..ad520b881 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/SearchScope.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/SearchScope.cs @@ -10,5 +10,5 @@ namespace Squidex.Domain.Apps.Entities.Contents; public enum SearchScope { All, - Published + Published, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs index 7b452bddd..b7f43a106 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs @@ -36,7 +36,7 @@ public sealed class SingletonCommandMiddleware : ICommandMiddleware DoNotScript = true, DoNotValidate = true, SchemaId = schemaId, - Status = Status.Published + Status = Status.Published, }; SimpleMapper.Map(createSchema, content); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/QueryParser.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/QueryParser.cs index 0ed801eb6..5bba60191 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/QueryParser.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/QueryParser.cs @@ -23,7 +23,7 @@ public sealed class QueryParser(Func fieldProvider) return new Query { - Text = text + Text = text, }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs index f2d1a0c76..26e23f8e1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/Text/TextIndexingProcess.cs @@ -93,7 +93,7 @@ public sealed class TextIndexingProcess( var state = new TextContentState { - UniqueContentId = uniqueId + UniqueContentId = uniqueId, }; Index(@event, @@ -277,7 +277,7 @@ public sealed class TextIndexingProcess( Stage = stage, ServeAll = all, ServePublished = published, - Texts = data.ToTexts() + Texts = data.ToTexts(), }); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/EntityExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities/EntityExtensions.cs index 81b8e9214..60e389051 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/EntityExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/EntityExtensions.cs @@ -36,7 +36,7 @@ public static class EntityExtensions Created = created, CreatedBy = createdBy, LastModified = timestamp, - LastModifiedBy = @event.Payload.Actor + LastModifiedBy = @event.Payload.Actor, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEvent.cs b/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEvent.cs index d3b54b93f..dea157e4e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEvent.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEvent.cs @@ -28,20 +28,6 @@ public sealed class HistoryEvent public Dictionary Parameters { get; set; } = []; - public HistoryEvent() - { - } - - public HistoryEvent(string channel, string eventType) - { - Guard.NotNullOrEmpty(channel); - Guard.NotNullOrEmpty(eventType); - - Channel = channel; - - EventType = eventType; - } - public HistoryEvent Param(string key, object? value) { var formatted = value?.ToString(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs b/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs index 7fdc7380a..1e96eb42f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/HistoryEventsCreatorBase.cs @@ -44,9 +44,9 @@ public abstract class HistoryEventsCreatorBase : IHistoryEventsCreator protected HistoryEvent ForEvent(IEvent @event, string channel) { - var message = typeRegistry.GetName(@event.GetType()); + var eventType = typeRegistry.GetName(@event.GetType()); - return new HistoryEvent(channel, message); + return new HistoryEvent { Channel = channel, EventType = eventType }; } public Task CreateEventAsync(Envelope @event) diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs index 73acd7923..4a9943f32 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/NotifoService.cs @@ -90,14 +90,14 @@ public class NotifoService : IUserEvents [Providers.WebPush] = new ChannelSettingDto { Send = ChannelSend.Send, - DelayInSeconds = null + DelayInSeconds = null, }, [Providers.Email] = new ChannelSettingDto { Send = ChannelSend.Send, - DelayInSeconds = 5 * 60 - } + DelayInSeconds = 5 * 60, + }, }; var userRequest = new UpsertUserDto @@ -106,7 +106,7 @@ public class NotifoService : IUserEvents FullName = user.Claims.DisplayName(), PreferredLanguage = "en", PreferredTimezone = null, - Settings = settings + Settings = settings, }; if (user.Email.IsEmail()) @@ -118,8 +118,8 @@ public class NotifoService : IUserEvents { Requests = [ - userRequest - ] + userRequest, + ], }); var apiKey = response.First().ApiKey; @@ -161,7 +161,7 @@ public class NotifoService : IUserEvents { var request = new PublishManyDto { - Requests = batch.ToList() + Requests = batch.ToList(), }; await client.Events.PostEventsAsync(options.AppId, request); @@ -212,7 +212,7 @@ public class NotifoService : IUserEvents { var request = new AddAllowedTopicDto { - Prefix = prefix + Prefix = prefix, }; await actualClient.Users.PostAllowedTopicAsync(options.AppId, userId, request); @@ -258,7 +258,7 @@ public class NotifoService : IUserEvents { var publishRequest = new PublishDto { - Properties = [] + Properties = [], }; foreach (var (key, value) in historyEvent.Parameters) @@ -311,16 +311,16 @@ public class NotifoService : IUserEvents { Subject = { - ["en"] = comment.Text - } - } + ["en"] = comment.Text, + }, + }, }; if (comment.AppId != null) { publishRequest.Properties = new NotificationProperties { - ["SquidexApp"] = comment.AppId.Name + ["SquidexApp"] = comment.AppId.Name, }; } @@ -328,7 +328,7 @@ public class NotifoService : IUserEvents { publishRequest.Preformatted.LinkUrl = new LocalizedText() { - ["en"] = comment.Url.ToString() + ["en"] = comment.Url.ToString(), }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/History/Repositories/IHistoryEventRepository.cs b/backend/src/Squidex.Domain.Apps.Entities/History/Repositories/IHistoryEventRepository.cs index 93b32f606..97fa27a70 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/History/Repositories/IHistoryEventRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/History/Repositories/IHistoryEventRepository.cs @@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.History.Repositories; public interface IHistoryEventRepository { - Task> QueryByChannelAsync(DomainId appId, string channelPrefix, int count, + Task> QueryByChannelAsync(DomainId ownerId, string? channel, int count, CancellationToken ct = default); Task InsertManyAsync(IEnumerable historyEvents, diff --git a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs index eb82ef8e9..2a183ba81 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs @@ -159,9 +159,9 @@ public sealed class JobProcessor Description = request.TaskName, Started = default, Status = JobStatus.Created, - TaskName = request.TaskName + TaskName = request.TaskName, }, - OwnerId = ownerId + OwnerId = ownerId, }; log.LogInformation("Starting new backup with backup id '{backupId}' for owner {ownerId}.", context.Job.Id, ownerId); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobStatus.cs b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobStatus.cs index 4a726cf5d..064457c02 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobStatus.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobStatus.cs @@ -13,5 +13,5 @@ public enum JobStatus Started, Completed, Cancelled, - Failed + Failed, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs index 3b655290c..7d6e94588 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Queries/RuleEnricher.cs @@ -50,7 +50,7 @@ public sealed class RuleEnricher(IRuleUsageTracker ruleUsageTracker, IRequestCac result = result with { NumFailed = statistic.TotalFailed, - NumSucceeded = statistic.TotalSucceeded + NumSucceeded = statistic.TotalSucceeded, }; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs index 4e8cce12d..8973f371e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Repositories/IRuleEventRepository.cs @@ -36,12 +36,12 @@ public interface IRuleEventRepository Task CancelByAppAsync(DomainId appId, CancellationToken ct = default); - Task QueryPendingAsync(Instant now, Func callback, + IAsyncEnumerable QueryPendingAsync(Instant now, CancellationToken ct = default); Task> QueryByAppAsync(DomainId appId, DomainId? ruleId = null, int skip = 0, int take = 20, CancellationToken ct = default); - Task FindAsync(DomainId id, + Task FindAsync(DomainId id, CancellationToken ct = default); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerWorker.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerWorker.cs index 4a2eeaeca..398a3ac15 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerWorker.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuerWorker.cs @@ -71,10 +71,11 @@ public sealed class RuleDequeuerWorker : IBackgroundProcess { var now = Clock.GetCurrentInstant(); - await ruleEventRepository.QueryPendingAsync(now, async @event => + var events = ruleEventRepository.QueryPendingAsync(now, ct); + await foreach (var @event in @events.WithCancellation(ct)) { await requestScheduler.ScheduleAsync(@event.Job.ExecutionPartition, @event, ct); - }, ct); + } } catch (Exception ex) { @@ -108,7 +109,7 @@ public sealed class RuleDequeuerWorker : IBackgroundProcess ExecutionResult = response.Status, Finished = now, JobNext = jobDelay, - JobResult = jobResult + JobResult = jobResult, }; await ruleEventRepository.UpdateAsync(@event.Job, update, default); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs index c7b305bb9..112a4aac5 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleEnqueuer.cs @@ -60,8 +60,8 @@ public sealed class RuleEnqueuer( IncludeStale = false, Rules = new Dictionary { - [ruleId] = rule - }.ToReadonlyDictionary() + [ruleId] = rule, + }.ToReadonlyDictionary(), }; // Write in batches of 100 items for better performance. Dispose completes the last write. @@ -101,7 +101,7 @@ public sealed class RuleEnqueuer( IncludeSkipped = false, IncludeStale = false, Rules = rules.ToReadonlyDictionary(x => x.Id), - MaxEvents = maxExtraEvents + MaxEvents = maxExtraEvents, }; await foreach (var result in ruleService.CreateJobsAsync(@event, context)) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobResult.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobResult.cs index d8ade9d57..e029b0107 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobResult.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/RuleJobResult.cs @@ -13,5 +13,5 @@ public enum RuleJobResult Success, Retry, Failed, - Cancelled + Cancelled, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs index d4ceba59f..e9a8db243 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/DefaultRuleRunnerService.cs @@ -46,14 +46,14 @@ public sealed class DefaultRuleRunnerService( IncludeStale = true, Rules = new Dictionary { - [ruleId] = rule - }.ToReadonlyDictionary() + [ruleId] = rule, + }.ToReadonlyDictionary(), }; var simulatedEvents = new List(MaxSimulatedEvents); var streamStart = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromDays(7)).ToDateTimeUtc(); - var streamFilter = StreamFilter.Prefix($"([a-zA-Z0-9]+)-{appId.Id}"); + var streamFilter = StreamFilter.Prefix($"%-{appId.Id}"); await foreach (var storedEvent in eventStore.QueryAllReverseAsync(streamFilter, streamStart, MaxSimulatedEvents, ct)) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs index 0d3b64298..ba8df82c9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/Runner/RuleRunnerJob.cs @@ -79,10 +79,10 @@ public sealed class RuleRunnerJob : IJobRunner new Dictionary { [ArgRuleId] = ruleId.ToString(), - [ArgSnapshot] = snapshot.ToString() + [ArgSnapshot] = snapshot.ToString(), }) with { - AppId = app.NamedId() + AppId = app.NamedId(), }; } @@ -187,7 +187,7 @@ public sealed class RuleRunnerJob : IJobRunner await using var batch = new RuleQueueWriter(ruleEventRepository, ruleUsageTracker, null); // Use a prefix query so that the storage can use an index for the query. - var streamFilter = StreamFilter.Prefix($"([a-zA-Z0-9]+)\\-{run.OwnerId}"); + var streamFilter = StreamFilter.Prefix($"%-{run.OwnerId}"); await foreach (var storedEvent in eventStore.QueryAllAsync(streamFilter, ct: ct)) { diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerWorker.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerWorker.cs index 141d3f345..4ddfdb6d9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerWorker.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTrackerWorker.cs @@ -112,7 +112,7 @@ public sealed class UsageTrackerWorker : IMessageHandler, AppId = target.AppId, CallsCurrent = costs, CallsLimit = limit, - RuleId = key + RuleId = key, }; await state.WriteEventAsync(Envelope.Create(@event)); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs index 01cdca070..dffa018ae 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/UsageTracking/UsageTriggerHandler.cs @@ -35,7 +35,7 @@ public sealed class UsageTriggerHandler : IRuleTriggerHandler { CallsCurrent = usageEvent.CallsCurrent, CallsLimit = usageEvent.CallsLimit, - Name = EventName + Name = EventName, }; await Task.Yield(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs index 6283c7556..a81df5ed3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs @@ -21,7 +21,7 @@ public sealed class FieldRuleCommand { return new FieldRule(Action, Field) { - Condition = Condition + Condition = Condition, }; } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs index ceba6cb19..c77df37a0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs @@ -51,7 +51,7 @@ public interface IUpsertCommand { IsLocked = eventField.IsLocked, IsHidden = eventField.IsHidden, - IsDisabled = eventField.IsDisabled + IsDisabled = eventField.IsDisabled, }; if (field is ArrayField arrayField && eventField.Nested?.Length > 0) @@ -66,7 +66,7 @@ public interface IUpsertCommand { IsLocked = nestedEventField.IsLocked, IsHidden = nestedEventField.IsHidden, - IsDisabled = nestedEventField.IsDisabled + IsDisabled = nestedEventField.IsDisabled, }; arrayFields.Add(nestedField); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.State.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.State.cs index 39292325b..f67e3a165 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.State.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.State.cs @@ -28,7 +28,7 @@ public partial class SchemaDomainObject // The schema usually does not contain any metadata. AppId = e.AppId, // Just update the total count to be more reliabble. - SchemaFieldsTotal = e.Schema.MaxId() + SchemaFieldsTotal = e.Schema.MaxId(), }; break; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs index 475d82bc3..8335433b2 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/DomainObject/SchemaDomainObject.cs @@ -248,7 +248,7 @@ public partial class SchemaDomainObject(DomainId id, IPersistenceFactory var options = new SchemaSynchronizationOptions { NoFieldDeletion = command.NoFieldDeletion, - NoFieldRecreation = command.NoFieldRecreation + NoFieldRecreation = command.NoFieldRecreation, }; var schemaSource = Snapshot; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/ISchemasHash.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/ISchemasHash.cs index c120c5cde..969e24c08 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/ISchemasHash.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/ISchemasHash.cs @@ -5,17 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using NodaTime; using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas; public interface ISchemasHash { - Task<(Instant Create, string Hash)> GetCurrentHashAsync(App app, - CancellationToken ct = default); - - ValueTask ComputeHashAsync(App app, IEnumerable schemas, + Task GetCurrentHashAsync(App app, CancellationToken ct = default); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs index 2a993416c..38a4ba5fa 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Indexes/SchemasIndex.cs @@ -230,7 +230,7 @@ public sealed class SchemasIndex( return schemaCache.RemoveAsync( [ GetCacheKey(appId, id), - GetCacheKey(appId, name) + GetCacheKey(appId, name), ]); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs index 0dda6f81d..611d0e4d3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemaChangedTriggerHandler.cs @@ -77,7 +77,7 @@ public sealed class SchemaChangedTriggerHandler(IScriptEngine scriptEngine) : IR // Script vars are just wrappers over dictionaries for better performance. var vars = new EventScriptVars { - ["event"] = @event + ["event"] = @event, }; return scriptEngine.Evaluate(vars, schemaTrigger.Condition); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasChatTool.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasChatTool.cs index 7d72fda99..40f2e1b8a 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasChatTool.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasChatTool.cs @@ -46,9 +46,9 @@ public sealed class SchemasChatTool(IAppProvider appProvider, IJsonSerializer se x.IsPublished, x.Type, FieldCount = x.Fields.Count, - Url = urlGenerator.SchemaUI(context.App.NamedId(), x.NamedId()) + Url = urlGenerator.SchemaUI(context.App.NamedId(), x.NamedId()), }), - Url = urlGenerator.SchemasUI(context.App.NamedId()) + Url = urlGenerator.SchemasUI(context.App.NamedId()), }; var json = serializer.Serialize(result, true); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasHashKey.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasHashKey.cs new file mode 100644 index 000000000..e6e835dcf --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/SchemasHashKey.cs @@ -0,0 +1,58 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; + +namespace Squidex.Domain.Apps.Entities.Schemas; + +public sealed class SchemasHashKey : ReadonlyDictionary +{ + public static readonly SchemasHashKey Empty = new SchemasHashKey(new Dictionary(), default); + + public Instant Timestamp { get; } + + private SchemasHashKey(IDictionary idVersions, Instant timestamp) + : base(idVersions) + { + Timestamp = timestamp; + } + + public static SchemasHashKey Create(App app, IEnumerable schemas, Instant created = default) + { + Guard.NotNull(app); + Guard.NotNull(schemas); + + return Create(app, schemas.ToDictionary(x => x.Id, x => x.Version), created); + } + + public static SchemasHashKey Create(App app, Dictionary schemas, Instant created = default) + { + Guard.NotNull(app); + Guard.NotNull(schemas); + + var idVersions = new Dictionary + { + [app.Id] = app.Version, + }; + + foreach (var (id, version) in schemas) + { + idVersions[id] = version; + } + + if (created == default) + { + created = SystemClock.Instance.GetCurrentInstant(); + } + + return new SchemasHashKey(idVersions, created); + } +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Search/SearchResultType.cs b/backend/src/Squidex.Domain.Apps.Entities/Search/SearchResultType.cs index d6ce6f1c3..d48bccb5b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Search/SearchResultType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Search/SearchResultType.cs @@ -14,5 +14,5 @@ public enum SearchResultType Dashboard, Setting, Rule, - Schema + Schema, } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 01282f323..9353e4cd1 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -35,9 +35,9 @@ - - - + + + diff --git a/backend/src/Squidex.Domain.Users/DefaultKeyStore.cs b/backend/src/Squidex.Domain.Users/DefaultKeyStore.cs index caa25e021..de51430b3 100644 --- a/backend/src/Squidex.Domain.Users/DefaultKeyStore.cs +++ b/backend/src/Squidex.Domain.Users/DefaultKeyStore.cs @@ -50,7 +50,7 @@ public sealed class DefaultKeyStore(ISnapshotStore store) { securityKey = new RsaSecurityKey(RSA.Create(2048)) { - KeyId = CryptoRandom.CreateUniqueId(16) + KeyId = CryptoRandom.CreateUniqueId(16), }; state = new State { Key = securityKey.KeyId }; @@ -86,7 +86,7 @@ public sealed class DefaultKeyStore(ISnapshotStore store) securityKey = new RsaSecurityKey(state.Parameters) { - KeyId = state.Key + KeyId = state.Key, }; return securityKey; diff --git a/backend/src/Squidex.Domain.Users/DefaultUserResolver.cs b/backend/src/Squidex.Domain.Users/DefaultUserResolver.cs index ddbff830a..a7cb1d79d 100644 --- a/backend/src/Squidex.Domain.Users/DefaultUserResolver.cs +++ b/backend/src/Squidex.Domain.Users/DefaultUserResolver.cs @@ -30,7 +30,7 @@ public sealed class DefaultUserResolver(IServiceProvider serviceProvider) : IUse { var values = new UserValues { - Invited = invited + Invited = invited, }; var user = await userService.CreateAsync(email, values, ct: ct); @@ -62,8 +62,8 @@ public sealed class DefaultUserResolver(IServiceProvider serviceProvider) : IUse { CustomClaims = [ - new Claim(type, value) - ] + new Claim(type, value), + ], }; await userService.UpdateAsync(id, values, silent, ct); diff --git a/backend/src/Squidex.Domain.Users/DefaultUserService.cs b/backend/src/Squidex.Domain.Users/DefaultUserService.cs index d29366f5e..21bf5a34c 100644 --- a/backend/src/Squidex.Domain.Users/DefaultUserService.cs +++ b/backend/src/Squidex.Domain.Users/DefaultUserService.cs @@ -45,17 +45,16 @@ public sealed class DefaultUserService( Guard.NotNull(ids); ids = ids.Where(userFactory.IsId); - if (!ids.Any()) { return ResultList.Empty(); } - var users = userManager.Users.Where(x => ids.Contains(x.Id)).ToList(); - - var resolved = await ResolveAsync(users); + var userItems = userManager.Users.Where(x => ids.Contains(x.Id)).ToList(); + var userTotal = userItems.Count; + var resolved = await ResolveAsync(userItems); - return ResultList.Create(users.Count, resolved); + return ResultList.Create(userTotal, resolved); } public async Task> QueryAsync(string? query = null, int take = 10, int skip = 0, @@ -80,7 +79,6 @@ public sealed class DefaultUserService( var userItems = QueryUsers(query).Skip(skip).Take(take).ToList(); var userTotal = QueryUsers(query).LongCount(); - var resolved = await ResolveAsync(userItems); return ResultList.Create(userTotal, resolved); @@ -109,7 +107,7 @@ public sealed class DefaultUserService( var user = await userManager.FindByLoginAsync(provider, key); - return await ResolveOptionalAsync(user); + return user != null ? await ResolveAsync(user) : null; } public async Task FindByEmailAsync(string email, @@ -119,7 +117,7 @@ public sealed class DefaultUserService( var user = await userManager.FindByEmailAsync(email); - return await ResolveOptionalAsync(user); + return user != null ? await ResolveAsync(user) : null; } public async Task GetAsync(ClaimsPrincipal principal, @@ -129,7 +127,7 @@ public sealed class DefaultUserService( var user = await userManager.GetUserAsync(principal); - return await ResolveOptionalAsync(user); + return user != null ? await ResolveAsync(user) : null; } public async Task FindByIdAsync(string id, @@ -142,7 +140,7 @@ public sealed class DefaultUserService( var user = await userManager.FindByIdAsync(id); - return await ResolveOptionalAsync(user); + return user != null ? await ResolveAsync(user) : null; } public async Task CreateAsync(string email, UserValues? values = null, bool lockAutomatically = false, @@ -327,14 +325,17 @@ public sealed class DefaultUserService( var user = await GetUserAsync(id); - var resolved = await ResolveAsync(user); - - await userManager.DeleteAsync(user).Throw(log); - - foreach (var events in userEvents) + if (userEvents.Any()) { - await events.OnUserDeletedAsync(resolved); + var resolved = await ResolveAsync(user); + + foreach (var events in userEvents) + { + await events.OnUserDeletedAsync(resolved); + } } + + await userManager.DeleteAsync(user).Throw(log); } private async Task ForUserAsync(string id, Func action) @@ -358,12 +359,15 @@ public sealed class DefaultUserService( return user ?? throw new DomainObjectNotFoundException(id); } - private Task ResolveAsync(IEnumerable users) + private async Task> ResolveAsync(IEnumerable users) { - return Task.WhenAll(users.Select(async user => + var result = new List(); + foreach (var user in users) { - return await ResolveAsync(user); - })); + result.Add(await ResolveAsync(user)); + } + + return result; } private async Task ResolveAsync(IdentityUser user) @@ -378,16 +382,6 @@ public sealed class DefaultUserService( return new UserWithClaims(user, claims.ToList()); } - private async Task ResolveOptionalAsync(IdentityUser? user) - { - if (user == null) - { - return null; - } - - return await ResolveAsync(user); - } - private static bool HasConsentGiven(UserValues values, IUser? oldUser) { if (values.Consent == true && oldUser?.Claims.HasConsent() != true) diff --git a/backend/src/Squidex.Infrastructure/CollectionExtensions.cs b/backend/src/Squidex.Infrastructure/CollectionExtensions.cs index 0f1a00d36..3e153e8e7 100644 --- a/backend/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/backend/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -35,7 +35,7 @@ public static class CollectionExtensions { var clone = new Dictionary(source) { - [key] = value + [key] = value, }; result = clone; @@ -54,7 +54,7 @@ public static class CollectionExtensions { var clone = new Dictionary(source) { - [key] = value + [key] = value, }; result = clone; @@ -73,7 +73,7 @@ public static class CollectionExtensions { var clone = new Dictionary(source) { - [key] = value + [key] = value, }; result = clone; @@ -203,7 +203,7 @@ public static class CollectionExtensions public static IEnumerable OrEmpty(this IEnumerable? source) { - return source ?? Enumerable.Empty(); + return source ?? []; } public static IEnumerable NotNull(this IEnumerable source) where T : class diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs index e033bba41..043b0c3ae 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObject.cs @@ -103,7 +103,7 @@ public abstract partial class DomainObject : IAggregate where T : Entity, new var result = new T { - Version = EtagVersion.Empty + Version = EtagVersion.Empty, }; if (version == result.Version) @@ -214,7 +214,7 @@ public abstract partial class DomainObject : IAggregate where T : Entity, new snapshot = new T { - Version = EtagVersion.Empty + Version = EtagVersion.Empty, }; return new CommandResult(UniqueId, Version, previousVersion, result); @@ -365,7 +365,7 @@ public abstract partial class DomainObject : IAggregate where T : Entity, new previousSnapshot = new T { - Version = Version + Version = Version, }; } diff --git a/backend/src/Squidex.Infrastructure/Commands/DomainObjectState.cs b/backend/src/Squidex.Infrastructure/Commands/DomainObjectState.cs index 60abb2384..3f7dc1041 100644 --- a/backend/src/Squidex.Infrastructure/Commands/DomainObjectState.cs +++ b/backend/src/Squidex.Infrastructure/Commands/DomainObjectState.cs @@ -12,5 +12,5 @@ public enum DomainObjectState Undefined, Empty, Created, - Deleted + Deleted, } diff --git a/backend/src/Squidex.Infrastructure/Diagnostics/GCHealthCheck.cs b/backend/src/Squidex.Infrastructure/Diagnostics/GCHealthCheck.cs index f7359ae62..16ddbe590 100644 --- a/backend/src/Squidex.Infrastructure/Diagnostics/GCHealthCheck.cs +++ b/backend/src/Squidex.Infrastructure/Diagnostics/GCHealthCheck.cs @@ -30,7 +30,7 @@ public sealed class GCHealthCheck(IOptions options) : IHea { "HeapSizeBytes", heapSize }, { "HeapSizeString", heapSize.ToReadableSize() }, { "WorkingSetBytes", workingSet }, - { "WorkingSetString", workingSet.ToReadableSize() } + { "WorkingSetString", workingSet.ToReadableSize() }, }; var status = workingSet < threshold ? diff --git a/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs b/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs index 5932f3175..6ca9666f4 100644 --- a/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs +++ b/backend/src/Squidex.Infrastructure/Email/SmtpEmailSender.cs @@ -43,7 +43,7 @@ public sealed class SmtpEmailSender(IOptions options) : IEmailSende smtpMessage.Body = new TextPart(TextFormat.Html) { - Text = body + Text = body, }; smtpMessage.Subject = subject; diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/BatchSubscription.cs b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/BatchSubscription.cs index 7872f3284..34b75610e 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/BatchSubscription.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/BatchSubscription.cs @@ -30,14 +30,14 @@ internal sealed class BatchSubscription : IEventSubscriber, IEventS taskQueue = Channel.CreateBounded(new BoundedChannelOptions(2) { SingleReader = true, - SingleWriter = true + SingleWriter = true, }); batchQueue = Channel.CreateBounded(new BoundedChannelOptions(batchSize) { AllowSynchronousContinuations = true, SingleReader = true, - SingleWriter = true + SingleWriter = true, }); batchQueue.Batch(taskQueue, batchSize, batchDelay, completed.Token); diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerState.cs b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerState.cs index bc0ca04f4..87a1fc506 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerState.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/EventConsumerState.cs @@ -49,7 +49,7 @@ public sealed record EventConsumerState(string? Position, int Count, bool IsStop { return SimpleMapper.Map(this, new EventConsumerInfo { - Name = name + Name = name, }); } } diff --git a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/ParseSubscription.cs b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/ParseSubscription.cs index 856bd6fde..2a11eed53 100644 --- a/backend/src/Squidex.Infrastructure/EventSourcing/Consume/ParseSubscription.cs +++ b/backend/src/Squidex.Infrastructure/EventSourcing/Consume/ParseSubscription.cs @@ -27,7 +27,7 @@ internal sealed class ParseSubscription : IEventSubscriber, IEventS { AllowSynchronousContinuations = true, SingleReader = true, - SingleWriter = true + SingleWriter = true, }); #pragma warning disable MA0040 // Flow the cancellation token diff --git a/backend/src/Squidex.Infrastructure/FileExtensions.cs b/backend/src/Squidex.Infrastructure/FileExtensions.cs index f523f1628..30448f7b4 100644 --- a/backend/src/Squidex.Infrastructure/FileExtensions.cs +++ b/backend/src/Squidex.Infrastructure/FileExtensions.cs @@ -17,12 +17,12 @@ public static class FileExtensions "kB", "MB", "GB", - "TB" + "TB", ]; private static readonly Dictionary UnifiedExtensions = new Dictionary { - ["jpeg"] = "jpg" + ["jpeg"] = "jpg", }; public static string FileType(this string fileName) diff --git a/backend/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs b/backend/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs index 4c4e83c87..42b887916 100644 --- a/backend/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs +++ b/backend/src/Squidex.Infrastructure/Json/Objects/JsonValueType.cs @@ -14,5 +14,5 @@ public enum JsonValueType Boolean, Number, Object, - String + String, } diff --git a/backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs b/backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs index 4077fcb23..bf5482369 100644 --- a/backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs +++ b/backend/src/Squidex.Infrastructure/Json/System/ReadonlyDictionaryConverterFactory.cs @@ -49,7 +49,7 @@ public sealed class ReadonlyDictionaryConverterFactory : JsonConverterFactory [ typeToCreate.GetGenericArguments()[0], typeToCreate.GetGenericArguments()[1], - typeToConvert + typeToConvert, ]); var converter = (JsonConverter)Activator.CreateInstance(concreteType)!; diff --git a/backend/src/Squidex.Infrastructure/Json/System/SystemJsonSerializer.cs b/backend/src/Squidex.Infrastructure/Json/System/SystemJsonSerializer.cs index 43e3aedf3..96dde5e6e 100644 --- a/backend/src/Squidex.Infrastructure/Json/System/SystemJsonSerializer.cs +++ b/backend/src/Squidex.Infrastructure/Json/System/SystemJsonSerializer.cs @@ -19,12 +19,12 @@ public sealed class SystemJsonSerializer : IJsonSerializer { optionsNormal = new JsonSerializerOptions(options) { - WriteIndented = false + WriteIndented = false, }; optionsIndented = new JsonSerializerOptions(options) { - WriteIndented = true + WriteIndented = true, }; } diff --git a/backend/src/Squidex.Infrastructure/Log/BackgroundRequestLogStore.cs b/backend/src/Squidex.Infrastructure/Log/BackgroundRequestLogStore.cs index d15a4666b..c472ab44e 100644 --- a/backend/src/Squidex.Infrastructure/Log/BackgroundRequestLogStore.cs +++ b/backend/src/Squidex.Infrastructure/Log/BackgroundRequestLogStore.cs @@ -8,6 +8,7 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using NodaTime; using Squidex.Infrastructure.Timers; namespace Squidex.Infrastructure.Log; @@ -101,7 +102,7 @@ public sealed class BackgroundRequestLogStore : DisposableObjectBase, IRequestLo return logRepository.DeleteAsync(key, ct); } - public IAsyncEnumerable QueryAllAsync(string key, DateTime fromDate, DateTime toDate, + public IAsyncEnumerable QueryAllAsync(string key, Instant fromTime, Instant toTime, CancellationToken ct = default) { if (!IsEnabled) @@ -109,7 +110,7 @@ public sealed class BackgroundRequestLogStore : DisposableObjectBase, IRequestLo return AsyncEnumerable.Empty(); } - return logRepository.QueryAllAsync(key, fromDate, toDate, ct); + return logRepository.QueryAllAsync(key, fromTime, toTime, ct); } public Task LogAsync(Request request, diff --git a/backend/src/Squidex.Infrastructure/Log/IRequestLogRepository.cs b/backend/src/Squidex.Infrastructure/Log/IRequestLogRepository.cs index cd9652774..9d8609af8 100644 --- a/backend/src/Squidex.Infrastructure/Log/IRequestLogRepository.cs +++ b/backend/src/Squidex.Infrastructure/Log/IRequestLogRepository.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using NodaTime; + namespace Squidex.Infrastructure.Log; public interface IRequestLogRepository @@ -15,6 +17,6 @@ public interface IRequestLogRepository Task DeleteAsync(string key, CancellationToken ct = default); - IAsyncEnumerable QueryAllAsync(string key, DateTime fromDate, DateTime toDate, + IAsyncEnumerable QueryAllAsync(string key, Instant fromTime, Instant toTime, CancellationToken ct = default); } diff --git a/backend/src/Squidex.Infrastructure/Log/IRequestLogStore.cs b/backend/src/Squidex.Infrastructure/Log/IRequestLogStore.cs index 849536216..7db8191b2 100644 --- a/backend/src/Squidex.Infrastructure/Log/IRequestLogStore.cs +++ b/backend/src/Squidex.Infrastructure/Log/IRequestLogStore.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using NodaTime; + namespace Squidex.Infrastructure.Log; public interface IRequestLogStore @@ -17,6 +19,6 @@ public interface IRequestLogStore Task DeleteAsync(string key, CancellationToken ct = default); - IAsyncEnumerable QueryAllAsync(string key, DateTime fromDate, DateTime toDate, + IAsyncEnumerable QueryAllAsync(string key, Instant fromTime, Instant toTime, CancellationToken ct = default); } diff --git a/backend/src/Squidex.Infrastructure/Log/Request.cs b/backend/src/Squidex.Infrastructure/Log/Request.cs index f192f13c1..3aabd37b1 100644 --- a/backend/src/Squidex.Infrastructure/Log/Request.cs +++ b/backend/src/Squidex.Infrastructure/Log/Request.cs @@ -7,15 +7,13 @@ using NodaTime; -#pragma warning disable SA1401 // Fields should be private - namespace Squidex.Infrastructure.Log; public sealed class Request { - public Instant Timestamp; + public Instant Timestamp { get; set; } - public string Key; + public string Key { get; set; } - public Dictionary Properties = []; + public Dictionary Properties { get; set; } = []; } diff --git a/backend/src/Squidex.Infrastructure/Queries/ClrValue.cs b/backend/src/Squidex.Infrastructure/Queries/ClrValue.cs index 7f49e3def..7c288fc82 100644 --- a/backend/src/Squidex.Infrastructure/Queries/ClrValue.cs +++ b/backend/src/Squidex.Infrastructure/Queries/ClrValue.cs @@ -7,6 +7,7 @@ using System.Collections; using System.Globalization; +using Google.Protobuf.WellKnownTypes; using NodaTime; #pragma warning disable SA1313 // Parameter names should begin with lower-case letter @@ -59,6 +60,11 @@ public sealed record ClrValue(object? Value, ClrValueType ValueType, bool IsList return new ClrValue(value, ClrValueType.Int64, false); } + public static implicit operator ClrValue(DomainId value) + { + return new ClrValue(value.ToString(), ClrValueType.String, false); + } + public static implicit operator ClrValue(string? value) { return value != null ? new ClrValue(value, ClrValueType.String, false) : Null; @@ -109,6 +115,11 @@ public sealed record ClrValue(object? Value, ClrValueType ValueType, bool IsList return value != null ? new ClrValue(value, ClrValueType.String, true) : Null; } + public static implicit operator ClrValue(List value) + { + return value != null ? new ClrValue(value.Select(x => x.ToString()).ToList(), ClrValueType.String, true) : Null; + } + public static implicit operator ClrValue(List value) { return value != null ? new ClrValue(value, ClrValueType.Dynamic, true) : Null; diff --git a/backend/src/Squidex.Infrastructure/Queries/ClrValueType.cs b/backend/src/Squidex.Infrastructure/Queries/ClrValueType.cs index 502626c72..51250b900 100644 --- a/backend/src/Squidex.Infrastructure/Queries/ClrValueType.cs +++ b/backend/src/Squidex.Infrastructure/Queries/ClrValueType.cs @@ -19,5 +19,5 @@ public enum ClrValueType Single, Sphere, String, - Null + Null, } diff --git a/backend/src/Squidex.Infrastructure/Queries/CompareOperator.cs b/backend/src/Squidex.Infrastructure/Queries/CompareOperator.cs index bd1aa15c1..863c0b18a 100644 --- a/backend/src/Squidex.Infrastructure/Queries/CompareOperator.cs +++ b/backend/src/Squidex.Infrastructure/Queries/CompareOperator.cs @@ -24,5 +24,5 @@ public enum CompareOperator LessThanOrEqual, Matchs, NotEquals, - StartsWith + StartsWith, } diff --git a/backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs b/backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs index a1e83f227..813ca6b5f 100644 --- a/backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs +++ b/backend/src/Squidex.Infrastructure/Queries/FilterSchema.cs @@ -50,7 +50,7 @@ public sealed record FilterSchema(FilterSchemaType Type) var schema = field.Schema with { - Fields = null + Fields = null, }; result?.Add(field with { Path = path, Schema = schema }); @@ -78,7 +78,7 @@ public sealed record FilterSchema(FilterSchemaType Type) return this with { - Fields = conflictFree.ToReadonlyList() + Fields = conflictFree.ToReadonlyList(), }; } diff --git a/backend/src/Squidex.Infrastructure/Queries/FilterSchemaType.cs b/backend/src/Squidex.Infrastructure/Queries/FilterSchemaType.cs index 4f185b127..a309562b7 100644 --- a/backend/src/Squidex.Infrastructure/Queries/FilterSchemaType.cs +++ b/backend/src/Squidex.Infrastructure/Queries/FilterSchemaType.cs @@ -19,5 +19,5 @@ public enum FilterSchemaType Object, ObjectArray, String, - StringArray + StringArray, } diff --git a/backend/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs b/backend/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs index 43a6b2676..7f1f72641 100644 --- a/backend/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs +++ b/backend/src/Squidex.Infrastructure/Queries/Json/ValueConverter.cs @@ -19,7 +19,7 @@ public static class ValueConverter [ InstantPattern.General, InstantPattern.ExtendedIso, - InstantPattern.CreateWithInvariantCulture("yyyy-MM-dd") + InstantPattern.CreateWithInvariantCulture("yyyy-MM-dd"), ]; public static ClrValue? Convert(FilterField field, JsonValue value, PropertyPath path, List errors) diff --git a/backend/src/Squidex.Infrastructure/Queries/LogicalFilterType.cs b/backend/src/Squidex.Infrastructure/Queries/LogicalFilterType.cs index 1cf4249b4..1ed858122 100644 --- a/backend/src/Squidex.Infrastructure/Queries/LogicalFilterType.cs +++ b/backend/src/Squidex.Infrastructure/Queries/LogicalFilterType.cs @@ -10,5 +10,5 @@ namespace Squidex.Infrastructure.Queries; public enum LogicalFilterType { And, - Or + Or, } diff --git a/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs b/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs index 1b1a576d6..0daae1d6b 100644 --- a/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs +++ b/backend/src/Squidex.Infrastructure/Queries/QueryModel.cs @@ -17,7 +17,7 @@ public sealed class QueryModel CompareOperator.Equals, CompareOperator.Exists, CompareOperator.In, - CompareOperator.NotEquals + CompareOperator.NotEquals, ], [FilterSchemaType.DateTime] = [ @@ -33,12 +33,12 @@ public sealed class QueryModel CompareOperator.LessThanOrEqual, CompareOperator.Matchs, CompareOperator.NotEquals, - CompareOperator.StartsWith + CompareOperator.StartsWith, ], [FilterSchemaType.GeoObject] = [ CompareOperator.LessThan, - CompareOperator.Exists + CompareOperator.Exists, ], [FilterSchemaType.Guid] = [ @@ -54,7 +54,7 @@ public sealed class QueryModel CompareOperator.LessThanOrEqual, CompareOperator.Matchs, CompareOperator.NotEquals, - CompareOperator.StartsWith + CompareOperator.StartsWith, ], [FilterSchemaType.Object] = [], [FilterSchemaType.ObjectArray] = @@ -63,7 +63,7 @@ public sealed class QueryModel CompareOperator.Exists, CompareOperator.Equals, CompareOperator.In, - CompareOperator.NotEquals + CompareOperator.NotEquals, ], [FilterSchemaType.Number] = [ @@ -74,7 +74,7 @@ public sealed class QueryModel CompareOperator.GreaterThan, CompareOperator.GreaterThanOrEqual, CompareOperator.In, - CompareOperator.NotEquals + CompareOperator.NotEquals, ], [FilterSchemaType.String] = [ @@ -90,7 +90,7 @@ public sealed class QueryModel CompareOperator.LessThanOrEqual, CompareOperator.Matchs, CompareOperator.NotEquals, - CompareOperator.StartsWith + CompareOperator.StartsWith, ], [FilterSchemaType.StringArray] = [ @@ -106,8 +106,8 @@ public sealed class QueryModel CompareOperator.LessThanOrEqual, CompareOperator.Matchs, CompareOperator.NotEquals, - CompareOperator.StartsWith - ] + CompareOperator.StartsWith, + ], }; public FilterSchema Schema { get; init; } = FilterSchema.Any; diff --git a/backend/src/Squidex.Infrastructure/Queries/SortOrder.cs b/backend/src/Squidex.Infrastructure/Queries/SortOrder.cs index c366694ad..3da268672 100644 --- a/backend/src/Squidex.Infrastructure/Queries/SortOrder.cs +++ b/backend/src/Squidex.Infrastructure/Queries/SortOrder.cs @@ -10,5 +10,5 @@ namespace Squidex.Infrastructure.Queries; public enum SortOrder { Ascending, - Descending + Descending, } diff --git a/backend/src/Squidex.Infrastructure/RefTokenType.cs b/backend/src/Squidex.Infrastructure/RefTokenType.cs index f25351e2d..1868ead0a 100644 --- a/backend/src/Squidex.Infrastructure/RefTokenType.cs +++ b/backend/src/Squidex.Infrastructure/RefTokenType.cs @@ -10,5 +10,5 @@ namespace Squidex.Infrastructure; public enum RefTokenType { Subject, - Client + Client, } diff --git a/backend/src/Squidex.Infrastructure/Reflection/Internal/ReflectionExtensions.cs b/backend/src/Squidex.Infrastructure/Reflection/Internal/ReflectionExtensions.cs index 4d4229446..2a30b41d7 100644 --- a/backend/src/Squidex.Infrastructure/Reflection/Internal/ReflectionExtensions.cs +++ b/backend/src/Squidex.Infrastructure/Reflection/Internal/ReflectionExtensions.cs @@ -27,7 +27,7 @@ public static class ReflectionExtensions var considered = new List { - type + type, }; var queue = new Queue(); diff --git a/backend/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs b/backend/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs index e714e8a2f..7f25f2515 100644 --- a/backend/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs +++ b/backend/src/Squidex.Infrastructure/Reflection/SimpleMapper.cs @@ -350,7 +350,7 @@ public static class SimpleMapper var context = new MappingContext { Culture = culture, - NullableAsOptional = nullableAsOptional + NullableAsOptional = nullableAsOptional, }; return ClassMapper.MapClass(source, target, ref context); diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 94bfb3109..4de917b0f 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -24,13 +24,13 @@ - - - - - - - + + + + + + + diff --git a/backend/src/Squidex.Infrastructure/States/Persistence.cs b/backend/src/Squidex.Infrastructure/States/Persistence.cs index 996b5aa73..e562b9e53 100644 --- a/backend/src/Squidex.Infrastructure/States/Persistence.cs +++ b/backend/src/Squidex.Infrastructure/States/Persistence.cs @@ -176,7 +176,7 @@ internal sealed class Persistence( { var job = new SnapshotWriteJob(ownerKey, state, newVersion) { - OldVersion = oldVersion + OldVersion = oldVersion, }; await snapshotStore.WriteAsync(job, ct); diff --git a/backend/src/Squidex.Infrastructure/States/PersistenceMode.cs b/backend/src/Squidex.Infrastructure/States/PersistenceMode.cs index c63bd4143..dfac45dcf 100644 --- a/backend/src/Squidex.Infrastructure/States/PersistenceMode.cs +++ b/backend/src/Squidex.Infrastructure/States/PersistenceMode.cs @@ -12,5 +12,5 @@ public enum PersistenceMode { EventSourcing = 1, Snapshots = 2, - SnapshotsAndEventSourcing = 3 + SnapshotsAndEventSourcing = 3, } diff --git a/backend/src/Squidex.Infrastructure/StringExtensions.cs b/backend/src/Squidex.Infrastructure/StringExtensions.cs index e7a02a292..45f60d7cb 100644 --- a/backend/src/Squidex.Infrastructure/StringExtensions.cs +++ b/backend/src/Squidex.Infrastructure/StringExtensions.cs @@ -19,7 +19,7 @@ public static partial class StringExtensions private static readonly Regex RegexProperty = BuildPropertyRegex(); private static readonly JsonSerializerOptions JsonEscapeOptions = new JsonSerializerOptions { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, }; public static string JsonEscape(this string value) diff --git a/backend/src/Squidex.Infrastructure/Tasks/PartitionedScheduler.cs b/backend/src/Squidex.Infrastructure/Tasks/PartitionedScheduler.cs index 1730728b4..6a77fe975 100644 --- a/backend/src/Squidex.Infrastructure/Tasks/PartitionedScheduler.cs +++ b/backend/src/Squidex.Infrastructure/Tasks/PartitionedScheduler.cs @@ -25,7 +25,7 @@ public sealed class PartitionedScheduler : IAsyncDisposable channel = Channel.CreateBounded(new BoundedChannelOptions(bufferSize) { SingleReader = true, - SingleWriter = false + SingleWriter = false, }); worker = Task.Run(async () => diff --git a/backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs b/backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs index e512613fb..83d31588f 100644 --- a/backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs +++ b/backend/src/Squidex.Infrastructure/UsageTracking/ApiUsageTracker.cs @@ -50,7 +50,7 @@ public sealed class ApiUsageTracker(IUsageTracker usageTracker) : IApiUsageTrack { [CounterTotalCalls] = weight, [CounterTotalElapsedMs] = elapsedMs, - [CounterTotalBytes] = bytes + [CounterTotalBytes] = bytes, }; return usageTracker.TrackAsync(date, apiKey, category, counters, ct); diff --git a/backend/src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs b/backend/src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs index 2d4f2223a..17beba1fd 100644 --- a/backend/src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs +++ b/backend/src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs @@ -9,9 +9,6 @@ namespace Squidex.Infrastructure.UsageTracking; public interface IUsageRepository { - Task TrackUsagesAsync(UsageUpdate update, - CancellationToken ct = default); - Task TrackUsagesAsync(UsageUpdate[] updates, CancellationToken ct = default); diff --git a/backend/src/Squidex.Shared/Users/ClientUser.cs b/backend/src/Squidex.Shared/Users/ClientUser.cs index 7b5aca107..942913333 100644 --- a/backend/src/Squidex.Shared/Users/ClientUser.cs +++ b/backend/src/Squidex.Shared/Users/ClientUser.cs @@ -48,7 +48,7 @@ public sealed class ClientUser : IUser claims = [ new Claim(OpenIdClaims.ClientId, token.Identifier), - new Claim(SquidexClaimTypes.DisplayName, token.Identifier) + new Claim(SquidexClaimTypes.DisplayName, token.Identifier), ]; } } diff --git a/backend/src/Squidex.Web/ApiExceptionConverter.cs b/backend/src/Squidex.Web/ApiExceptionConverter.cs index b61116537..10b1ee7fb 100644 --- a/backend/src/Squidex.Web/ApiExceptionConverter.cs +++ b/backend/src/Squidex.Web/ApiExceptionConverter.cs @@ -31,7 +31,7 @@ public static class ApiExceptionConverter [412] = "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.10", [415] = "https://www.rfc-editor.org/rfc/rfc7231#section-6.5.13", [422] = "https://www.rfc-editor.org/rfc/rfc4918#section-11.2", - [500] = "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1" + [500] = "https://www.rfc-editor.org/rfc/rfc7231#section-6.6.1", }; public static (ErrorDto Error, Exception? Unhandled) ToErrorDto(int statusCode, HttpContext? httpContext) @@ -144,7 +144,7 @@ public static class ApiExceptionConverter { var error = new ErrorDto { - StatusCode = status + StatusCode = status, }; if (!string.IsNullOrWhiteSpace(message)) diff --git a/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs b/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs index 6db560fd7..4572d907b 100644 --- a/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs +++ b/backend/src/Squidex.Web/ApiExceptionFilterAttribute.cs @@ -49,7 +49,7 @@ public sealed class ApiExceptionFilterAttribute : ActionFilterAttribute, IExcept return new ObjectResult(error) { - StatusCode = error.StatusCode + StatusCode = error.StatusCode, }; } } diff --git a/backend/src/Squidex.Web/AssetRequestSizeLimitAttribute.cs b/backend/src/Squidex.Web/AssetRequestSizeLimitAttribute.cs index bec968c42..e83e0f8e9 100644 --- a/backend/src/Squidex.Web/AssetRequestSizeLimitAttribute.cs +++ b/backend/src/Squidex.Web/AssetRequestSizeLimitAttribute.cs @@ -41,7 +41,7 @@ public sealed class AssetRequestSizeLimitAttribute : Attribute, IAuthorizationFi { var options = new FormOptions { - MultipartBodyLengthLimit = assetOptions.Value.MaxSize + MultipartBodyLengthLimit = assetOptions.Value.MaxSize, }; context.HttpContext.Features.Set(new FormFeature(context.HttpContext.Request, options)); diff --git a/backend/src/Squidex.Web/Pipeline/CachingManager.cs b/backend/src/Squidex.Web/Pipeline/CachingManager.cs index 20e3b871b..41ab6c02c 100644 --- a/backend/src/Squidex.Web/Pipeline/CachingManager.cs +++ b/backend/src/Squidex.Web/Pipeline/CachingManager.cs @@ -198,7 +198,7 @@ public sealed class CachingManager : IRequestCache stringBuilderPool = new DefaultObjectPool(new StringBuilderPooledObjectPolicy { - MaximumRetainedCapacity = cachingOptions.Value.MaxSurrogateKeysSize + MaximumRetainedCapacity = cachingOptions.Value.MaxSurrogateKeysSize, }); } diff --git a/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs b/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs index fbd45abff..c099f8080 100644 --- a/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs +++ b/backend/src/Squidex.Web/Pipeline/RequestExceptionMiddleware.cs @@ -62,7 +62,7 @@ public sealed class RequestExceptionMiddleware(RequestDelegate next) await writer.ExecuteAsync(actionContext, new ObjectResult(error) { - StatusCode = error.StatusCode + StatusCode = error.StatusCode, }); } diff --git a/backend/src/Squidex.Web/Scripting/HttpRequestJintExtension.cs b/backend/src/Squidex.Web/Scripting/HttpRequestJintExtension.cs index 025021254..0efd8b664 100644 --- a/backend/src/Squidex.Web/Scripting/HttpRequestJintExtension.cs +++ b/backend/src/Squidex.Web/Scripting/HttpRequestJintExtension.cs @@ -41,7 +41,7 @@ public sealed class HttpRequestJintExtension(IHttpContextAccessor httpContextAcc Method = request.Method, Path = request.Path, Query = request.Query.ToDictionary(x => x.Key, x => x.Value.ToArray()), - QueryString = request.QueryString.Value + QueryString = request.QueryString.Value, }; return result; diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptAnyBodyAttribute.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptAnyBodyAttribute.cs index 0fb316492..d56fdac58 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptAnyBodyAttribute.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptAnyBodyAttribute.cs @@ -33,7 +33,7 @@ public sealed class AcceptAnyBodyAttribute : OpenApiOperationProcessorAttribute Schema = new JsonSchema { }, - Description = FieldDescriptions.GraphqlRequest + Description = FieldDescriptions.GraphqlRequest, }); return true; diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptHeader.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptHeader.cs index 3439c2d8f..040dcdc31 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptHeader.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/AcceptHeader.cs @@ -81,9 +81,9 @@ public class AcceptHeader Kind = OpenApiParameterKind.Header, Schema = new JsonSchema { - Type = schemaType + Type = schemaType, }, - Description = description + Description = description, }; context.OperationDescription.Operation.Parameters.Add(parameter); diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/CommonProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/CommonProcessor.cs index 312a5490f..4a4b6bb89 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/CommonProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/CommonProcessor.cs @@ -39,13 +39,13 @@ public sealed class CommonProcessor : IDocumentProcessor { url = logoUrl, backgroundStyle = string.Empty, - backgroundColor = logoColor - } + backgroundColor = logoColor, + }, }; context.Document.ExternalDocumentation = new OpenApiExternalDocumentation { - Url = "https://docs.squidex.io" + Url = "https://docs.squidex.io", }; } } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs index dd8a51326..5c52fe846 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs @@ -31,7 +31,7 @@ public sealed class DiscriminatorProcessor(TypeRegistry typeRegistry) : ISchemaP var discriminatorName = config.DiscriminatorProperty; var discriminatorObject = new OpenApiDiscriminator { - PropertyName = discriminatorName + PropertyName = discriminatorName, }; var schema = context.Schema; @@ -42,7 +42,7 @@ public sealed class DiscriminatorProcessor(TypeRegistry typeRegistry) : ISchemaP discriminatorObject.Mapping[typeName] = new JsonSchema { - Reference = derivedSchema + Reference = derivedSchema, }; } @@ -52,7 +52,7 @@ public sealed class DiscriminatorProcessor(TypeRegistry typeRegistry) : ISchemaP { schema.Properties[discriminatorName] = existingProperty = new JsonSchemaProperty { - Type = JsonObjectType.String + Type = JsonObjectType.String, }; } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/ErrorDtoProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/ErrorDtoProcessor.cs index 06a2e1920..925b67b33 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/ErrorDtoProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/ErrorDtoProcessor.cs @@ -32,7 +32,7 @@ public sealed class ErrorDtoProcessor : IOperationProcessor { operation.Responses.Add(code, new OpenApiResponse { - Description = description + Description = description, }); } } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs index 8956bc315..0b043d833 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/OpenApiServices.cs @@ -71,8 +71,8 @@ public static class OpenApiServices { SchemaSettings = new SystemTextJsonSchemaGeneratorSettings() { - SerializerOptions = c.GetRequiredService() - } + SerializerOptions = c.GetRequiredService(), + }, }; ConfigureSchemaSettings(settings.SchemaSettings, c.GetRequiredService(), true); @@ -134,7 +134,7 @@ public static class OpenApiServices schema.AdditionalPropertiesSchema = new JsonSchema { - Description = "Any" + Description = "Any", }; }); } @@ -147,7 +147,7 @@ public static class OpenApiServices schema.Item = new JsonSchema { - Type = itemType + Type = itemType, }; }); } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/QueryExtensions.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/QueryExtensions.cs index 54d34cb1a..060a2fe2a 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/QueryExtensions.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/QueryExtensions.cs @@ -17,12 +17,12 @@ public static class QueryExtensions { var stringSchema = new JsonSchema { - Type = JsonObjectType.String + Type = JsonObjectType.String, }; var numberSchema = new JsonSchema { - Type = JsonObjectType.Number + Type = JsonObjectType.Number, }; void AddParameterQuery(OpenApiParameter parameter) @@ -43,7 +43,7 @@ public static class QueryExtensions { Schema = stringSchema, Name = "$search", - Description = FieldDescriptions.QuerySkip + Description = FieldDescriptions.QuerySkip, }); } @@ -51,42 +51,42 @@ public static class QueryExtensions { Schema = numberSchema, Name = "$top", - Description = FieldDescriptions.QueryTop + Description = FieldDescriptions.QueryTop, }); AddParameterQuery(new OpenApiParameter { Schema = numberSchema, Name = "$skip", - Description = FieldDescriptions.QuerySkip + Description = FieldDescriptions.QuerySkip, }); AddParameterQuery(new OpenApiParameter { Schema = stringSchema, Name = "$orderby", - Description = FieldDescriptions.QueryOrderBy + Description = FieldDescriptions.QueryOrderBy, }); AddParameterQuery(new OpenApiParameter { Schema = stringSchema, Name = "$filter", - Description = FieldDescriptions.QueryFilter + Description = FieldDescriptions.QueryFilter, }); AddParameterQuery(new OpenApiParameter { Schema = stringSchema, Name = "q", - Description = FieldDescriptions.QueryQ + Description = FieldDescriptions.QueryQ, }); AddParameterQuery(new OpenApiParameter { Schema = stringSchema, Name = "ids", - Description = FieldDescriptions.QueryIds + Description = FieldDescriptions.QueryIds, }); operation.SetPositions(); diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/ScopesProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/ScopesProcessor.cs index ff79cf7cf..4618fc902 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/ScopesProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/ScopesProcessor.cs @@ -26,7 +26,7 @@ public sealed class ScopesProcessor : IOperationProcessor { context.OperationDescription.Operation.Security.Add(new OpenApiSecurityRequirement { - [Constants.SecurityDefinition] = permissionAttribute.PermissionIds + [Constants.SecurityDefinition] = permissionAttribute.PermissionIds, }); } else @@ -42,7 +42,7 @@ public sealed class ScopesProcessor : IOperationProcessor context.OperationDescription.Operation.Security.Add(new OpenApiSecurityRequirement { - [Constants.SecurityDefinition] = scopes + [Constants.SecurityDefinition] = scopes, }); } } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs index 36b54e188..6a8173db3 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/SecurityProcessor.cs @@ -29,7 +29,7 @@ public sealed class SecurityProcessor(IUrlGenerator urlGenerator) : SecurityDefi OpenIdConnectUrl = BuildUrl(".well-known/openid-configuration"), // Just described the token URL again. - Description = Properties.Resources.OpenApiSecurity.Replace("", BuildUrl($"connect/token"), StringComparison.Ordinal) + Description = Properties.Resources.OpenApiSecurity.Replace("", BuildUrl($"connect/token"), StringComparison.Ordinal), }; return security; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs index e7c8eb077..509509a14 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs @@ -65,7 +65,7 @@ public sealed class AppImageController( return new FileCallbackResult(App.Image.MimeType, callback) { - ErrorAs404 = true + ErrorAs404 = true, }; } @@ -83,7 +83,7 @@ public sealed class AppImageController( var resizeOptions = new ResizeOptions { TargetWidth = 50, - TargetHeight = 50 + TargetHeight = 50, }; using (Telemetry.Activities.StartActivity("Read")) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs index 46e9812df..f412f510a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguageDto.cs @@ -46,7 +46,7 @@ public sealed class AppLanguageDto : Resource IsMaster = languages.IsMaster(language), IsOptional = languages.IsOptional(language), Iso2Code = language.Iso2Code, - Fallback = config.Fallbacks.ToArray() + Fallback = config.Fallbacks.ToArray(), }; return result; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs index f1b90efaf..b60cb295b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppLanguagesDto.cs @@ -27,7 +27,7 @@ public sealed class AppLanguagesDto : Resource .Select(x => AppLanguageDto.FromDomain(x.Key, x.Value, config)) .Select(x => x.CreateLinks(resources, app)) .OrderByDescending(x => x.IsMaster).ThenBy(x => x.Iso2Code) - .ToArray() + .ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppSettingsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppSettingsDto.cs index 887152bc7..45aeadd76 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppSettingsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/AppSettingsDto.cs @@ -47,7 +47,7 @@ public sealed class AppSettingsDto : Resource HideDateTimeModeButton = settings.HideDateTimeModeButton, HideScheduler = settings.HideScheduler, Patterns = settings.Patterns.Select(PatternDto.FromPattern).ToArray(), - Version = app.Version + Version = app.Version, }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs index 810d05d23..e1911767f 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/ClientsDto.cs @@ -24,7 +24,7 @@ public sealed class ClientsDto : Resource Items = app.Clients .Select(x => ClientDto.FromClient(x.Key, x.Value)) .Select(x => x.CreateLinks(resources)) - .ToArray() + .ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/PatternDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/PatternDto.cs index f31aa6ab2..71710558e 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/PatternDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/PatternDto.cs @@ -38,7 +38,7 @@ public sealed class PatternDto { return new Pattern(Name, Regex) { - Message = Message + Message = Message, }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs index defbb9957..7de5ceab8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RoleDto.cs @@ -52,7 +52,7 @@ public sealed class RoleDto : Resource NumContributors = GetNumContributors(role, app), Permissions = role.Permissions.ToIds().ToArray(), Properties = role.Properties, - IsDefaultRole = role.IsDefault + IsDefaultRole = role.IsDefault, }; return result; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs index c368c7a34..68890b555 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/RolesDto.cs @@ -26,7 +26,7 @@ public sealed class RolesDto : Resource .Select(x => RoleDto.FromDomain(x, app)) .Select(x => x.CreateLinks(resources)) .OrderBy(x => x.Name) - .ToArray() + .ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs index c520419ab..629817a0a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs @@ -47,8 +47,8 @@ public sealed class UpdateAppSettingsDto Editors = Editors?.Select(x => x.ToEditor()).ToReadonlyList()!, HideScheduler = HideScheduler, HideDateTimeModeButton = HideDateTimeModeButton, - Patterns = Patterns?.Select(x => x.ToPattern()).ToReadonlyList()! - } + Patterns = Patterns?.Select(x => x.ToPattern()).ToReadonlyList()!, + }, }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowDto.cs index d06715d02..a6b99808b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowDto.cs @@ -47,7 +47,7 @@ public sealed class WorkflowDto : Resource Steps = workflow.Steps.ToDictionary( x => x.Key, x => WorkflowStepDto.FromDomain(x.Value)), - Id = id + Id = id, }); return result; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs index 7dd02fa3a..c7f1e5081 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs @@ -52,7 +52,7 @@ public sealed class WorkflowStepDto { Transitions = step.Transitions.ToDictionary( y => y.Key, - y => WorkflowTransitionDto.FromDomain(y.Value)) + y => WorkflowTransitionDto.FromDomain(y.Value)), }); if (step.NoUpdate != null) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs index 8fec9d53d..db6bb8546 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs @@ -31,7 +31,7 @@ public sealed class WorkflowsDto : Resource app.Workflows .Select(x => WorkflowDto.FromDomain(x.Key, x.Value)) .Select(x => x.CreateLinks(resources)) - .ToArray() + .ToArray(), }; var errors = await workflowsValidator.ValidateAsync(app.Id, app.Workflows); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs index c274eca45..39e6eb2a0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs @@ -179,7 +179,7 @@ public sealed class AssetContentController( FileDownloadName = asset.FileName, FileSize = contentLength, LastModified = asset.LastModified.ToDateTimeOffset(), - SendInline = request.Download != 1 + SendInline = request.Download != 1, }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs index cbce83097..15df659ff 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs @@ -160,7 +160,7 @@ public sealed class AssetDto : Resource { result.Meta = new AssetMeta { - IsDuplicate = "true" + IsDuplicate = "true", }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs index 32cdb176d..3056304f5 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFolderScope.cs @@ -11,5 +11,5 @@ public enum AssetFolderScope { PathAndItems, Path, - Items + Items, } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs index 1ab2c18f1..0dcdfa611 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetFoldersDto.cs @@ -34,7 +34,7 @@ public sealed class AssetFoldersDto : Resource { Total = assetFolders.Total, Items = assetFolders.Select(x => AssetFolderDto.FromDomain(x, resources)).ToArray(), - Path = path.Select(x => AssetFolderDto.FromDomain(x, resources)).ToArray() + Path = path.Select(x => AssetFolderDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs index 70806a94e..e3a99027d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs @@ -28,7 +28,7 @@ public sealed class AssetsDto : Resource var result = new AssetsDto { Total = assets.Total, - Items = assets.Select(x => AssetDto.FromDomain(x, resources)).ToArray() + Items = assets.Select(x => AssetDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs index 36816e4d0..144f82d0f 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Backups/BackupContentController.cs @@ -82,7 +82,7 @@ public class BackupContentController( { FileDownloadName = job.File.Name, FileSize = null, - ErrorAs404 = true + ErrorAs404 = true, }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs index ee611b904..4ad343dd1 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Backups/Models/BackupJobsDto.cs @@ -22,7 +22,7 @@ public sealed class BackupJobsDto : Resource { var result = new BackupJobsDto { - Items = jobs.Select(x => BackupJobDto.FromDomain(x, resources)).ToArray() + Items = jobs.Select(x => BackupJobDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentOpenApiController.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentOpenApiController.cs index 3903f10e8..5cea24cb0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentOpenApiController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentOpenApiController.cs @@ -28,7 +28,7 @@ public sealed class ContentOpenApiController( { var vm = new DocsVM { - Specification = $"~/api/content/{app}/swagger/v1/swagger.json" + Specification = $"~/api/content/{app}/swagger/v1/swagger.json", }; return View(nameof(Docs), vm); @@ -42,7 +42,7 @@ public sealed class ContentOpenApiController( { var vm = new DocsVM { - Specification = $"~/api/content/{app}/flat/swagger/v1/swagger.json" + Specification = $"~/api/content/{app}/flat/swagger/v1/swagger.json", }; return View(nameof(Docs), vm); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs index 6e6a33c97..f46499401 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/ContentsSharedController.cs @@ -32,7 +32,7 @@ public sealed class ContentsSharedController( { DefaultResponseContentType = new MediaTypeHeaderValue("application/json"), CsrfProtectionEnabled = false, - CsrfProtectionHeaders = [] + CsrfProtectionHeaders = [], }; /// diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/Builder.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/Builder.cs index 2ee6ffba1..1d846eb8d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/Builder.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/Builder.cs @@ -86,7 +86,7 @@ internal sealed class Builder Parent = this, SchemaDisplayName = "__Shared", SchemaName = "__Shared", - SchemaTypeName = "__Shared" + SchemaTypeName = "__Shared", }; builder.AddTag("API endpoints for operations across all schemas."); @@ -134,7 +134,7 @@ internal sealed class Builder Parent = this, SchemaDisplayName = schema.DisplayName(), SchemaName = schema.Name, - SchemaTypeName = typeName + SchemaTypeName = typeName, }; builder.AddTag("API endpoints for [schema] content items."); @@ -150,7 +150,7 @@ internal sealed class Builder return new JsonSchema { - Reference = reference + Reference = reference, }; } @@ -162,7 +162,7 @@ internal sealed class Builder { var reference = new JsonSchema { - Reference = definition + Reference = definition, }; return (reference, null); @@ -174,7 +174,7 @@ internal sealed class Builder return (new JsonSchema { - Reference = definition + Reference = definition, }, definition); } @@ -188,9 +188,9 @@ internal sealed class Builder ["total"] = JsonTypeBuilder.NumberProperty( FieldDescriptions.ContentsTotal, true), ["items"] = JsonTypeBuilder.ArrayProperty(contentSchema, - FieldDescriptions.ContentsItems, true) + FieldDescriptions.ContentsItems, true), }, - Type = JsonObjectType.Object + Type = JsonObjectType.Object, }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs index b00f38c78..8bbd053f7 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationBuilder.cs @@ -119,7 +119,7 @@ internal sealed class OperationBuilder(OperationsBuilder operations, OpenApiOper { var response = new OpenApiResponse { - Description = description + Description = description, }; if (schema != null && statusCode == 204) @@ -142,8 +142,8 @@ internal sealed class OperationBuilder(OperationsBuilder operations, OpenApiOper { new OpenApiSecurityRequirement { - [Constants.SecurityDefinition] = [fullId] - } + [Constants.SecurityDefinition] = [fullId], + }, }; return this; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationsBuilder.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationsBuilder.cs index 9d8ae741e..4a54f8b91 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationsBuilder.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/OperationsBuilder.cs @@ -49,8 +49,8 @@ internal sealed class OperationsBuilder { Tags = [ - tag - ] + tag, + ], }; var operations = Parent.OpenApiDocument.Paths.GetOrAddNew($"{Path}{path}"); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs index e06dcb02e..2636a488d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasOpenApiGenerator.cs @@ -223,15 +223,15 @@ public sealed class SchemasOpenApiGenerator( { Schemes = new List { - scheme + scheme, }, Consumes = new List { - "application/json" + "application/json", }, Produces = new List { - "application/json" + "application/json", }, Info = new OpenApiInfo { @@ -239,9 +239,9 @@ public sealed class SchemasOpenApiGenerator( Description = Resources.OpenApiContentDescription .Replace("[REDOC_LINK_NORMAL]", urlGenerator.BuildUrl($"api/content/{app.Name}/docs"), StringComparison.Ordinal) - .Replace("[REDOC_LINK_SIMPLE]", urlGenerator.BuildUrl($"api/content/{app.Name}/docs/flat"), StringComparison.Ordinal) + .Replace("[REDOC_LINK_SIMPLE]", urlGenerator.BuildUrl($"api/content/{app.Name}/docs/flat"), StringComparison.Ordinal), }, - SchemaType = NJsonSchema.SchemaType.OpenApi3 + SchemaType = NJsonSchema.SchemaType.OpenApi3, }; if (!string.IsNullOrWhiteSpace(context.Request.Host.Value)) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs index c47175b24..b386683d4 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs @@ -118,7 +118,7 @@ public sealed class ContentDto : Resource var response = SimpleMapper.Map(content, new ContentDto { SchemaId = content.SchemaId.Id, - SchemaName = content.SchemaId.Name + SchemaName = content.SchemaId.Name, }); if (resources.Context.Flatten()) @@ -139,7 +139,7 @@ public sealed class ContentDto : Resource { response.ScheduleJob = new ScheduleJobDto { - Color = content.ScheduledStatusColor! + Color = content.ScheduledStatusColor!, }; SimpleMapper.Map(content.ScheduleJob, response.ScheduleJob); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs index 6a41bff08..f68347947 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs @@ -35,7 +35,7 @@ public sealed class ContentsDto : Resource var result = new ContentsDto { Total = contents.Total, - Items = contents.Select(x => ContentDto.FromDomain(x, resources)).ToArray() + Items = contents.Select(x => ContentDto.FromDomain(x, resources)).ToArray(), }; if (schema != null) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs index 416af03b6..adaa9b8a0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Contents/Models/ImportContentsDto.cs @@ -47,7 +47,7 @@ public sealed class ImportContentsDto var job = new BulkUpdateJob { Type = BulkUpdateContentType.Create, - Data = x + Data = x, }; #pragma warning disable CS0618 // Type or member is obsolete diff --git a/backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs index 78cb970bf..5fee18d6d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/ContributorsDto.cs @@ -45,7 +45,7 @@ public sealed class ContributorsDto : Resource .Select(x => x.CreateUser(users)) .Select(x => x.CreateAppLinks(resources)) .OrderBy(x => x.ContributorName) - .ToArray() + .ToArray(), }; result.CreateInvited(invited); @@ -65,7 +65,7 @@ public sealed class ContributorsDto : Resource .Select(x => x.CreateUser(users)) .Select(x => x.CreateTeamLinks(resources)) .OrderBy(x => x.ContributorName) - .ToArray() + .ToArray(), }; result.CreateInvited(invited); @@ -84,7 +84,7 @@ public sealed class ContributorsDto : Resource { Metadata = new ContributorsMetadata { - IsInvited = "true" + IsInvited = "true", }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Docs/DocsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Docs/DocsController.cs index 55cdaaf04..f91ea0072 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Docs/DocsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Docs/DocsController.cs @@ -19,7 +19,7 @@ public sealed class DocsController(ICommandBus commandBus) : ApiController(comma { var vm = new DocsVM { - Specification = "~/api/swagger/v1/swagger.json" + Specification = "~/api/swagger/v1/swagger.json", }; return View(nameof(Docs), vm); diff --git a/backend/src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs b/backend/src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs index dd60d60dd..aa02d45c0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/EventConsumers/Models/EventConsumersDto.cs @@ -21,7 +21,7 @@ public sealed class EventConsumersDto : Resource { var result = new EventConsumersDto { - Items = items.Select(x => EventConsumerDto.FromDomain(x, resources)).ToArray() + Items = items.Select(x => EventConsumerDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Jobs/JobsContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Jobs/JobsContentController.cs index 15c4290a0..9db544967 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Jobs/JobsContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Jobs/JobsContentController.cs @@ -55,7 +55,7 @@ public class JobsContentController( { FileDownloadName = job.File.Name, FileSize = null, - ErrorAs404 = true + ErrorAs404 = true, }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Jobs/Models/JobsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Jobs/Models/JobsDto.cs index e0ec0d5c8..d963cd3a8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Jobs/Models/JobsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Jobs/Models/JobsDto.cs @@ -22,7 +22,7 @@ public sealed class JobsDto : Resource { var result = new JobsDto { - Items = jobs.Select(x => JobDto.FromDomain(x, resources)).ToArray() + Items = jobs.Select(x => JobDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/News/Service/FeaturesService.cs b/backend/src/Squidex/Areas/Api/Controllers/News/Service/FeaturesService.cs index 4cfd4aa49..8d8c9754c 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/News/Service/FeaturesService.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/News/Service/FeaturesService.cs @@ -33,7 +33,7 @@ public sealed class FeaturesService AppName = options.Value.AppName, ClientId = options.Value.ClientId, ClientSecret = options.Value.ClientSecret, - Url = "https://cloud.squidex.io" + Url = "https://cloud.squidex.io", }; var client = new SquidexClient(squidexOptions); @@ -47,7 +47,7 @@ public sealed class FeaturesService { var result = new FeaturesDto { - Version = version + Version = version, }; if (contents != null && version < FeatureVersion) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs index 78468a7a0..8890e8f1d 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansDto.cs @@ -49,7 +49,7 @@ public sealed class PlansDto CurrentPlanId = planId, Plans = plans.Select(PlanDto.FromDomain).ToArray(), PlanOwner = owner, - Referral = referral + Referral = referral, }; if (locked == PlansLockedReason.None) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansLockedReason.cs b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansLockedReason.cs index a685f87e5..28b061b50 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansLockedReason.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Plans/Models/PlansLockedReason.cs @@ -27,5 +27,5 @@ public enum PlansLockedReason /// /// The plan is managed by the team. /// - ManagedByTeam + ManagedByTeam, } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs index e7c68d86a..0cefa05c8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs @@ -28,7 +28,7 @@ public sealed class RuleEventsDto : Resource var result = new RuleEventsDto { Total = ruleEvents.Total, - Items = ruleEvents.Select(x => RuleEventDto.FromDomain(x, resources)).ToArray() + Items = ruleEvents.Select(x => RuleEventDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources, ruleId); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs index 10e876b44..a88039e49 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs @@ -31,7 +31,7 @@ public sealed class RulesDto : Resource var result = new RulesDto { - Items = items.Select(x => RuleDto.FromDomain(x, runningAvailable, ruleRunnerService, resources)).ToArray() + Items = items.Select(x => RuleDto.FromDomain(x, runningAvailable, ruleRunnerService, resources)).ToArray(), }; result.RunningRuleId = runningRuleId; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventDto.cs index 4597a94e9..444aba9ab 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventDto.cs @@ -68,7 +68,7 @@ public sealed record SimulatedRuleEventDto { var result = SimpleMapper.Map(ruleEvent, new SimulatedRuleEventDto { - SkipReasons = [] + SkipReasons = [], }); foreach (var reason in Enum.GetValues()) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventsDto.cs index dea34607b..088ae6b6a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/SimulatedRuleEventsDto.cs @@ -27,7 +27,7 @@ public sealed class SimulatedRuleEventsDto : Resource var result = new SimulatedRuleEventsDto { Total = events.Count, - Items = events.Select(SimulatedRuleEventDto.FromDomain).ToArray() + Items = events.Select(SimulatedRuleEventDto.FromDomain).ToArray(), }; return result; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureFieldRulesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureFieldRulesDto.cs index 63a652c68..fc49a7ea0 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureFieldRulesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureFieldRulesDto.cs @@ -22,7 +22,7 @@ public sealed class ConfigureFieldRulesDto { return new ConfigureFieldRules { - FieldRules = FieldRules?.Select(x => x.ToCommand()).ToArray() + FieldRules = FieldRules?.Select(x => x.ToCommand()).ToArray(), }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs index 8eea2125c..873538185 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs @@ -18,7 +18,7 @@ public sealed class ConfigurePreviewUrlsDto : Dictionary { return new ConfigurePreviewUrls { - PreviewUrls = new Dictionary(this).ToReadonlyDictionary() + PreviewUrls = new Dictionary(this).ToReadonlyDictionary(), }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs index 95bc1cf33..a1a1568c6 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs @@ -68,7 +68,7 @@ public sealed class FieldDto : Resource new NestedFieldDto { FieldId = field.Id, - Properties = properties + Properties = properties, }); return result; @@ -84,7 +84,7 @@ public sealed class FieldDto : Resource { FieldId = field.Id, Properties = properties, - Partitioning = field.Partitioning.Key + Partitioning = field.Partitioning.Key, }); if (field is IArrayField arrayField) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/IndexesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/IndexesDto.cs index 2a12c8f56..3e5d41fc7 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/IndexesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/IndexesDto.cs @@ -21,7 +21,7 @@ public sealed class IndexesDto : Resource { var result = new IndexesDto { - Items = indexes.Select(x => IndexDto.FromDomain(x, resources)).ToArray() + Items = indexes.Select(x => IndexDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs index 13d247109..e4b930cf5 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs @@ -21,7 +21,7 @@ public sealed class SchemasDto : Resource { var result = new SchemasDto { - Items = schemas.Select(x => SchemaDto.FromDomain(x, resources)).ToArray() + Items = schemas.Select(x => SchemaDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/CallsUsageDtoDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/CallsUsageDtoDto.cs index 3e0bd0bfd..67b48bcc3 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/CallsUsageDtoDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/CallsUsageDtoDto.cs @@ -69,7 +69,7 @@ public sealed class CallsUsageDtoDto TotalCalls = summary.TotalCalls, MonthBytes = summary.MonthBytes, MonthCalls = summary.MonthCalls, - Details = details.ToDictionary(x => x.Key, x => x.Value.Select(CallsUsagePerDateDto.FromDomain).ToArray()) + Details = details.ToDictionary(x => x.Key, x => x.Value.Select(CallsUsagePerDateDto.FromDomain).ToArray()), }; } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/StorageUsagePerDateDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/StorageUsagePerDateDto.cs index 127b41967..8d2b3c46f 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/StorageUsagePerDateDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Statistics/Models/StorageUsagePerDateDto.cs @@ -32,7 +32,7 @@ public sealed class StorageUsagePerDateDto { Date = stats.Date, TotalCount = stats.Counters.TotalAssets, - TotalSize = stats.Counters.TotalSize + TotalSize = stats.Counters.TotalSize, }; return result; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs index a9d52e3a0..4e5f5fe33 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Statistics/UsagesController.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; +using NodaTime; using Squidex.Areas.Api.Controllers.Statistics.Models; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Assets; @@ -72,10 +73,13 @@ public sealed class UsagesController( return new FileCallbackResult("text/csv", (body, range, ct) => { - return usageLog.ReadLogAsync(appId, fileDate.AddDays(-30), fileDate, body, ct); + var timeTo = Instant.FromDateTimeUtc(fileDate); + var timeFrom = timeTo.Minus(Duration.FromDays(30)); + + return usageLog.ReadLogAsync(appId, timeFrom, timeTo, body, ct); }) { - FileDownloadName = fileName + FileDownloadName = fileName, }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Teams/Models/AuthSchemeValueDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Teams/Models/AuthSchemeValueDto.cs index 9c88c86cb..1da4797b5 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Teams/Models/AuthSchemeValueDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Teams/Models/AuthSchemeValueDto.cs @@ -21,7 +21,7 @@ public class AuthSchemeValueDto { return new UpsertAuth { - Scheme = Scheme?.ToDomain() + Scheme = Scheme?.ToDomain(), }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplateDetailsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplateDetailsDto.cs index 0f17ad42c..310d2ebf3 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplateDetailsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplateDetailsDto.cs @@ -20,7 +20,7 @@ public class TemplateDetailsDto : Resource { var result = new TemplateDetailsDto { - Details = details + Details = details, }; return result.CreateLinks(name, resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplatesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplatesDto.cs index dcba501b7..296b217a4 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplatesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Templates/Models/TemplatesDto.cs @@ -21,7 +21,7 @@ public sealed class TemplatesDto : Resource { var result = new TemplatesDto { - Items = items.Select(x => TemplateDto.FromDomain(x, resources)).ToArray() + Items = items.Select(x => TemplateDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs index 2f1fa4d4f..96f11c192 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs @@ -39,7 +39,7 @@ public sealed class TranslationsController(ICommandBus commandBus, IAssetStore a await assetStore.DownloadAsync(path, body, range, ct); }) { - ErrorAs404 = true + ErrorAs404 = true, }; } @@ -79,7 +79,7 @@ public sealed class TranslationsController(ICommandBus commandBus, IAssetStore a { Configuration = request.Configuration, ConversationId = request.ConversationId, - Prompt = request.Prompt + Prompt = request.Prompt, }; var context = new AppChatContext diff --git a/backend/src/Squidex/Areas/Api/Controllers/Users/Models/UsersDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Users/Models/UsersDto.cs index 2159d3c33..3c898a82f 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Users/Models/UsersDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Users/Models/UsersDto.cs @@ -27,7 +27,7 @@ public sealed class UsersDto : Resource var result = new UsersDto { Total = total, - Items = items.Select(x => UserDto.FromDomain(x, resources)).ToArray() + Items = items.Select(x => UserDto.FromDomain(x, resources)).ToArray(), }; return result.CreateLinks(resources); diff --git a/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs b/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs index ec60bc590..ef853625e 100644 --- a/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs +++ b/backend/src/Squidex/Areas/Frontend/Middlewares/IndexExtensions.cs @@ -28,7 +28,7 @@ public static class IndexExtensions var scripts = new List { - $"var texts = {GetText(CultureInfo.CurrentUICulture.Name)};" + $"var texts = {GetText(CultureInfo.CurrentUICulture.Name)};", }; var uiOptions = httpContext.RequestServices.GetService>()?.Value; @@ -38,8 +38,8 @@ public static class IndexExtensions { More = new Dictionary(uiOptions.More) { - ["culture"] = CultureInfo.CurrentUICulture.Name - } + ["culture"] = CultureInfo.CurrentUICulture.Name, + }, }; var options = httpContext.Features.Get(); diff --git a/backend/src/Squidex/Areas/Frontend/Startup.cs b/backend/src/Squidex/Areas/Frontend/Startup.cs index 1fd1185a2..dd1264688 100644 --- a/backend/src/Squidex/Areas/Frontend/Startup.cs +++ b/backend/src/Squidex/Areas/Frontend/Startup.cs @@ -53,7 +53,7 @@ public static class Startup Transform = (html, context) => { return new ValueTask(html.AddOptions(context)); - } + }, }); }); @@ -109,7 +109,7 @@ public static class Startup response.Headers[HeaderNames.CacheControl] = "max-age=5184000"; } }, - FileProvider = fileProvider + FileProvider = fileProvider, }); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs b/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs index f33bdf0e1..4cdea2c1d 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/CreateAdminInitializer.cs @@ -60,7 +60,7 @@ public sealed class CreateAdminInitializer(IServiceProvider serviceProvider) : I var values = new UserValues { Password = adminPass, - Permissions = permissions + Permissions = permissions, }; await userService.UpdateAsync(user.Id, values, ct: ct); @@ -74,7 +74,7 @@ public sealed class CreateAdminInitializer(IServiceProvider serviceProvider) : I { Password = adminPass, Permissions = permissions, - DisplayName = adminEmail + DisplayName = adminEmail, }; await userService.CreateAsync(adminEmail, values, ct: ct); diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/Dynamic/DynamicSchemeProvider.cs b/backend/src/Squidex/Areas/IdentityServer/Config/Dynamic/DynamicSchemeProvider.cs index 398fc3329..2b2c58e5c 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/Dynamic/DynamicSchemeProvider.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/Dynamic/DynamicSchemeProvider.cs @@ -45,7 +45,7 @@ public sealed class DynamicSchemeProvider( var cacheOptions = new DistributedCacheEntryOptions { - AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) + AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10), }; await dynamicCache.SetAsync(CacheKey(id), serialized, cacheOptions, ct); @@ -192,7 +192,7 @@ public sealed class DynamicSchemeProvider( { Events = new OidcHandler(new MyIdentityOptions { - OidcOnSignoutRedirectUrl = config.SignoutRedirectUrl + OidcOnSignoutRedirectUrl = config.SignoutRedirectUrl, }), Authority = config.Authority, CallbackPath = new PathString($"/signin-{name}"), @@ -201,7 +201,7 @@ public sealed class DynamicSchemeProvider( RemoteSignOutPath = new PathString($"/signout-{name}"), RequireHttpsMetadata = false, ResponseType = "code", - SignedOutRedirectUri = new PathString($"/signout-callback-{name}") + SignedOutRedirectUri = new PathString($"/signout-callback-{name}"), }; configure.PostConfigure(name, oidcOptions); diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs b/backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs index bc9ead185..825b643b2 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/DynamicApplicationStore.cs @@ -99,8 +99,8 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Constants.ScopeApi, - Permissions.Prefixes.Scope + Constants.ScopePermissions - } + Permissions.Prefixes.Scope + Constants.ScopePermissions, + }, }.CopyClaims(user)); } @@ -120,8 +120,8 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Constants.ScopeApi, - Permissions.Prefixes.Scope + Constants.ScopePermissions - } + Permissions.Prefixes.Scope + Constants.ScopePermissions, + }, }); } @@ -139,11 +139,11 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor { new Uri(urlGenerator.BuildUrl("login;")), new Uri(urlGenerator.BuildUrl("client-callback-silent.html", false)), - new Uri(urlGenerator.BuildUrl("client-callback-popup.html", false)) + new Uri(urlGenerator.BuildUrl("client-callback-popup.html", false)), }, PostLogoutRedirectUris = { - new Uri(urlGenerator.BuildUrl("logout", false)) + new Uri(urlGenerator.BuildUrl("logout", false)), }, Permissions = { @@ -157,9 +157,9 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Constants.ScopeApi, - Permissions.Prefixes.Scope + Constants.ScopePermissions + Permissions.Prefixes.Scope + Constants.ScopePermissions, }, - ClientType = ClientTypes.Public + ClientType = ClientTypes.Public, }); var internalClientId = Constants.ClientInternalId; @@ -170,7 +170,7 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor ClientSecret = Constants.ClientInternalSecret, RedirectUris = { - new Uri(urlGenerator.BuildUrl("/signin-internal", false)) + new Uri(urlGenerator.BuildUrl("/signin-internal", false)), }, Permissions = { @@ -185,9 +185,9 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Constants.ScopeApi, - Permissions.Prefixes.Scope + Constants.ScopePermissions + Permissions.Prefixes.Scope + Constants.ScopePermissions, }, - ClientType = ClientTypes.Public + ClientType = ClientTypes.Public, }); var identityOptions = serviceProvider.GetRequiredService>().Value; @@ -212,8 +212,8 @@ public class DynamicApplicationStore(IServiceProvider serviceProvider) : InMemor Permissions.Scopes.Profile, Permissions.Scopes.Roles, Permissions.Prefixes.Scope + Constants.ScopeApi, - Permissions.Prefixes.Scope + Constants.ScopePermissions - } + Permissions.Prefixes.Scope + Constants.ScopePermissions, + }, }.SetAdmin()); } } diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerConfiguration.cs b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerConfiguration.cs index 12e2fdec8..e086a64df 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerConfiguration.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerConfiguration.cs @@ -28,8 +28,8 @@ public static class IdentityServerConfiguration Name = Constants.ScopeApi, Resources = { - Permissions.Prefixes.Scope + Constants.ScopeApi - } + Permissions.Prefixes.Scope + Constants.ScopeApi, + }, }); } } diff --git a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs index 00c8d636c..ac7515e26 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Config/IdentityServerServices.cs @@ -54,9 +54,6 @@ public static class IdentityServerServices services.AddSingletonAs() .As(); - services.AddSingletonAs() - .AsSelf(); - services.AddSingletonAs() .AsSelf(); @@ -78,6 +75,9 @@ public static class IdentityServerServices services.AddOpenIddict() .AddCore(builder => { + builder.SetDefaultScopeEntity(); + builder.SetDefaultApplicationEntity(); + builder.Services.AddSingletonAs() .As>(); diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs index e8f0f3844..3c2c0b78b 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/AccountController.cs @@ -99,7 +99,7 @@ public sealed class AccountController( var update = new UserValues { Consent = true, - ConsentForEmails = model.ConsentToAutomatedEmails + ConsentForEmails = model.ConsentToAutomatedEmails, }; await userService.UpdateAsync(user.Id, update, ct: HttpContext.RequestAborted); @@ -213,7 +213,7 @@ public sealed class AccountController( HasPasswordAuth = allowPasswordAuth, HasCustomAuth = allowCustomDomains, RequestType = requestType, - ReturnUrl = returnUrl + ReturnUrl = returnUrl, }; return View(nameof(Login), vm); @@ -261,7 +261,7 @@ public sealed class AccountController( { var values = new UserValues { - CustomClaims = login.Principal.Claims.GetSquidexClaims().ToList() + CustomClaims = login.Principal.Claims.GetSquidexClaims().ToList(), }; user = await userService.UpdateAsync(user.Id, values, false, HttpContext.RequestAborted); @@ -289,7 +289,7 @@ public sealed class AccountController( { var values = new UserValues { - CustomClaims = login.Principal.Claims.GetSquidexClaims().ToList() + CustomClaims = login.Principal.Claims.GetSquidexClaims().ToList(), }; var locked = identityOptions.LockAutomatically; diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/LoginVM.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/LoginVM.cs index 516f8b62d..51223e0c7 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/LoginVM.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Account/LoginVM.cs @@ -34,5 +34,5 @@ public enum RequestType { Get, Login, - LoginCustom + LoginCustom, } diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/AuthorizationController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/AuthorizationController.cs index 4c1587acb..470028aff 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/AuthorizationController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Connect/AuthorizationController.cs @@ -57,7 +57,7 @@ public class AuthorizationController( new AuthenticationProperties(new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid.", }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } @@ -68,7 +68,7 @@ public class AuthorizationController( new AuthenticationProperties(new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in.", }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } @@ -122,7 +122,7 @@ public class AuthorizationController( var properties = new AuthenticationProperties(new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in.", }); return Forbid(properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); @@ -138,7 +138,7 @@ public class AuthorizationController( return Challenge( new AuthenticationProperties { - RedirectUri = redirectUri + RedirectUri = redirectUri, }); } diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ChangeAboutModel.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ChangeAboutModel.cs index 74f29878b..e784b898d 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ChangeAboutModel.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ChangeAboutModel.cs @@ -26,7 +26,7 @@ public class ChangeAboutModel ["companyRole"] = CompanyRole, ["companySize"] = CompanySize, ["project"] = Project, - } + }, }; } } diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ProfileController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ProfileController.cs index 76b1a7dc8..d2296b15b 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ProfileController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Profile/ProfileController.cs @@ -155,7 +155,7 @@ public sealed class ProfileController( var update = new UserValues { - PictureUrl = SquidexClaimTypes.PictureUrlStore + PictureUrl = SquidexClaimTypes.PictureUrlStore, }; await userService.UpdateAsync(id, update, ct: ct); @@ -169,7 +169,7 @@ public sealed class ProfileController( var resizeOptions = new ResizeOptions { TargetWidth = 128, - TargetHeight = 128 + TargetHeight = 128, }; try @@ -256,7 +256,7 @@ public sealed class ProfileController( IsHidden = user.Claims.IsHidden(), Project = user.Claims.Answer("project"), ShowAbout = identityOptions.ShowAbout, - SuccessMessage = successMessage + SuccessMessage = successMessage, }; if (model != null) diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs index e002a2fc6..9b061b897 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/Setup/SetupController.cs @@ -65,7 +65,7 @@ public class SetupController( { var user = await userService.CreateAsync(model.Email, new UserValues { - Password = model.Password + Password = model.Password, }, ct: HttpContext.RequestAborted); await SignInManager.SignInAsync((IdentityUser)user.Identity, true); @@ -99,7 +99,7 @@ public class SetupController( IsAssetStoreFile = assetStore is FolderAssetStore, IsAssetStoreFtp = assetStore is FTPAssetStore, HasExternalLogin = externalProviders.Count != 0, - HasPasswordAuth = identityOptions.AllowPasswordAuth + HasPasswordAuth = identityOptions.AllowPasswordAuth, }; if (model != null) diff --git a/backend/src/Squidex/Areas/IdentityServer/Controllers/UserInfo/UserInfoController.cs b/backend/src/Squidex/Areas/IdentityServer/Controllers/UserInfo/UserInfoController.cs index df4dfb014..f52a0b3f4 100644 --- a/backend/src/Squidex/Areas/IdentityServer/Controllers/UserInfo/UserInfoController.cs +++ b/backend/src/Squidex/Areas/IdentityServer/Controllers/UserInfo/UserInfoController.cs @@ -32,14 +32,14 @@ public class UserInfoController(IUserService userService) : IdentityServerContro new AuthenticationProperties(new Dictionary { [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken, - [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The specified access token is bound to an account that no longer exists." + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The specified access token is bound to an account that no longer exists.", }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); } var claims = new Dictionary(StringComparer.Ordinal) { - [Claims.Subject] = user.Id + [Claims.Subject] = user.Id, }; if (User.HasScope(Scopes.Email)) diff --git a/backend/src/Squidex/Config/Domain/AppsServices.cs b/backend/src/Squidex/Config/Domain/AppsServices.cs index 423ed10e6..74e390fde 100644 --- a/backend/src/Squidex/Config/Domain/AppsServices.cs +++ b/backend/src/Squidex/Config/Domain/AppsServices.cs @@ -80,8 +80,8 @@ public static class AppsServices { Settings = new AppSettings { - Patterns = patterns.ToReadonlyList() - } + Patterns = patterns.ToReadonlyList(), + }, }; }); } diff --git a/backend/src/Squidex/Config/Domain/AssetServices.cs b/backend/src/Squidex/Config/Domain/AssetServices.cs index fe69550d2..1b586d267 100644 --- a/backend/src/Squidex/Config/Domain/AssetServices.cs +++ b/backend/src/Squidex/Config/Domain/AssetServices.cs @@ -128,7 +128,7 @@ public static class AssetServices ["MongoDb"] = () => { services.AddSquidexMongoAssetStore(config); - } + }, }); } } diff --git a/backend/src/Squidex/Config/Domain/BackupsServices.cs b/backend/src/Squidex/Config/Domain/BackupsServices.cs index 9347654d2..2c1d1f582 100644 --- a/backend/src/Squidex/Config/Domain/BackupsServices.cs +++ b/backend/src/Squidex/Config/Domain/BackupsServices.cs @@ -5,14 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Migrations.Migrations.Backup; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Schemas; -using Squidex.Infrastructure.Migrations; namespace Squidex.Config.Domain; @@ -51,8 +49,5 @@ public static class BackupsServices services.AddTransientAs() .AsSelf(); - - services.AddTransientAs() - .As(); } } diff --git a/backend/src/Squidex/Config/Domain/EventSourcingServices.cs b/backend/src/Squidex/Config/Domain/EventSourcingServices.cs index 09eb6f648..71481a25c 100644 --- a/backend/src/Squidex/Config/Domain/EventSourcingServices.cs +++ b/backend/src/Squidex/Config/Domain/EventSourcingServices.cs @@ -7,6 +7,7 @@ using EventStore.Client; using Squidex.Events.GetEventStore; +using Squidex.Hosting.Configuration; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing.Consume; @@ -24,6 +25,18 @@ public static class EventSourcingServices { services.AddSquidexMongoEventStore(config); }, + ["Sql"] = () => + { + if (!string.Equals(config.GetValue("store:type"), "Sql", StringComparison.OrdinalIgnoreCase)) + { + throw new ConfigurationException( + new ConfigurationError( + "Sql event store is only allowed, when 'store:type' is also set to 'Sql'.", + "messaging:type")); + } + + services.AddSquidexEntityFrameworkEventStore(config); + }, ["GetEventStore"] = () => { var configuration = config.GetRequiredValue("eventStore:getEventStore:configuration"); @@ -32,7 +45,7 @@ public static class EventSourcingServices .AsSelf(); services.AddGetEventStore(config); - } + }, }); services.AddTransientAs() diff --git a/backend/src/Squidex/Config/Domain/LoggingServices.cs b/backend/src/Squidex/Config/Domain/LoggingServices.cs index 457e2b8da..e2dd0b362 100644 --- a/backend/src/Squidex/Config/Domain/LoggingServices.cs +++ b/backend/src/Squidex/Config/Domain/LoggingServices.cs @@ -5,8 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -#define LOG_ALL_IDENTITY_SERVER_NONE - using Squidex.Infrastructure.Log; using Squidex.Log; using Squidex.Web.Pipeline; @@ -31,5 +29,8 @@ public static class LoggingServices builder.Services.AddSingletonAs() .As(); + + // Warnings are useful for improper queries but errors only log exceptions that we get anyway. + builder.AddFilter("Microsoft.EntityFrameworkCore", level => level is LogLevel.Warning or LogLevel.Critical); } } diff --git a/backend/src/Squidex/Config/Domain/ResizeServices.cs b/backend/src/Squidex/Config/Domain/ResizeServices.cs index 9dc60a822..e461b122c 100644 --- a/backend/src/Squidex/Config/Domain/ResizeServices.cs +++ b/backend/src/Squidex/Config/Domain/ResizeServices.cs @@ -26,7 +26,7 @@ public static class ResizeServices new CompositeThumbnailGenerator( [ c.GetRequiredService(), - c.GetRequiredService() + c.GetRequiredService(), ])) .AsSelf(); diff --git a/backend/src/Squidex/Config/Domain/StoreServices.cs b/backend/src/Squidex/Config/Domain/StoreServices.cs index 7d9e41e9b..866927726 100644 --- a/backend/src/Squidex/Config/Domain/StoreServices.cs +++ b/backend/src/Squidex/Config/Domain/StoreServices.cs @@ -5,9 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Migrations.Migrations.MongoDb; -using MongoDB.Driver; -using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.States; namespace Squidex.Config.Domain; @@ -20,43 +17,12 @@ public static class StoreServices { ["MongoDB"] = () => { - var mongoDatabaseName = config.GetRequiredValue("store:mongoDb:database")!; - var mongoContentDatabaseName = config.GetOptionalValue("store:mongoDb:contentDatabase", mongoDatabaseName)!; - - static IMongoDatabase GetDatabase(IServiceProvider serviceProvider, string name) - { - return serviceProvider.GetRequiredService().GetDatabase(name); - } - - services.AddTransientAs(c => new DeleteContentCollections(GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs(c => new RestructureContentCollection(GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs(c => new ConvertDocumentIds(GetDatabase(c, mongoDatabaseName), GetDatabase(c, mongoContentDatabaseName))) - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - - services.AddTransientAs() - .As(); - services.AddSquidexMongoStore(config); - } + }, + ["Sql"] = () => + { + services.AddSquidexEntityFramework(config); + }, }); services.AddSingleton(typeof(IStore<>), diff --git a/backend/src/Squidex/Config/Messaging/MessagingServices.cs b/backend/src/Squidex/Config/Messaging/MessagingServices.cs index c001c514d..a08a4713b 100644 --- a/backend/src/Squidex/Config/Messaging/MessagingServices.cs +++ b/backend/src/Squidex/Config/Messaging/MessagingServices.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.IO; using System.Text.Json; using Squidex.AI; using Squidex.Domain.Apps.Core.Subscriptions; @@ -18,6 +19,7 @@ using Squidex.Domain.Apps.Entities.Jobs; using Squidex.Domain.Apps.Entities.Rules; using Squidex.Domain.Apps.Entities.Rules.Runner; using Squidex.Domain.Apps.Entities.Rules.UsageTracking; +using Squidex.Hosting.Configuration; using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing.Consume; using Squidex.Messaging; @@ -99,7 +101,7 @@ public static class MessagingServices .As().As(); services.AddMessaging() - .AddTransport(config) + .AddSquidexTransport(config) .AddSubscriptions(!isWorker) .AddReplicatedCache(true, options => { @@ -134,4 +136,24 @@ public static class MessagingServices options.Scheduler = InlineScheduler.Instance; }); } + + public static MessagingBuilder AddSquidexTransport(this MessagingBuilder builder, IConfiguration config) + { + var type = config.GetValue("messaging:type"); + + if (string.Equals(type, "Sql", StringComparison.OrdinalIgnoreCase)) + { + if (!string.Equals(config.GetValue("store:type"), "Sql", StringComparison.OrdinalIgnoreCase)) + { + throw new ConfigurationException( + new ConfigurationError( + "Sql messaging transport is only allowed, when 'store:type' is also set to 'Sql'.", + "messaging:type")); + } + + return builder.AddSquidexEntityFrameworkTransport(config); + } + + return builder.AddTransport(config); + } } diff --git a/backend/src/Squidex/Config/Web/WebExtensions.cs b/backend/src/Squidex/Config/Web/WebExtensions.cs index 5a788f8c2..5e55962ff 100644 --- a/backend/src/Squidex/Config/Web/WebExtensions.cs +++ b/backend/src/Squidex/Config/Web/WebExtensions.cs @@ -82,11 +82,11 @@ public static class WebExtensions Data = value.Data.Count > 0 ? new Dictionary(value.Data) : null, value.Description, value.Duration, - value.Status + value.Status, }; }), report.Status, - report.TotalDuration + report.TotalDuration, }; var json = serializer.Serialize(response); @@ -99,19 +99,19 @@ public static class WebExtensions app.UseHealthChecks("/readiness", new HealthCheckOptions { Predicate = check => !check.Tags.Contains("background"), - ResponseWriter = writer + ResponseWriter = writer, }); app.UseHealthChecks("/healthz", new HealthCheckOptions { Predicate = check => check.Tags.Contains("node"), - ResponseWriter = writer + ResponseWriter = writer, }); app.UseHealthChecks("/background-healthz", new HealthCheckOptions { Predicate = check => check.Tags.Contains("background"), - ResponseWriter = writer + ResponseWriter = writer, }); return app; diff --git a/backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs b/backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs index 50f289c56..4ac75865d 100644 --- a/backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs +++ b/backend/src/Squidex/Pipeline/Plugins/PluginExtensions.cs @@ -26,7 +26,7 @@ public static class PluginExtensions typeof(SquidexEntities), typeof(SquidexEvents), typeof(SquidexInfrastructure), - typeof(SquidexWeb) + typeof(SquidexWeb), }.Select(x => x.Assembly.GetName()).ToArray(); public static IMvcBuilder AddSquidexPlugins(this IMvcBuilder mvcBuilder, IConfiguration config) diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 7f29af7d5..84a4463b9 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -18,6 +18,7 @@ + @@ -61,24 +62,24 @@ - - - - - - - + + + + + + + - - - - + + + + - - - - - + + + + + diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json index 536554b55..e0a8b0e40 100644 --- a/backend/src/Squidex/appsettings.json +++ b/backend/src/Squidex/appsettings.json @@ -540,7 +540,7 @@ "eventStore": { // Define the type of the event store. // - // SUPPORTED: MongoDb + // SUPPORTED: MongoDb, Sql "type": "MongoDb", "mongoDb": { @@ -551,15 +551,43 @@ // The name of the event store database. "database": "Squidex" + }, + + "sql": { + // Configured via "store:sql" } }, "store": { // Define the type of the read store. // - // SUPPORTED: MongoDb + // SUPPORTED: MongoDb, Sql "type": "MongoDb", + "sql": { + // The connection string to your database. + "connectionString": "", + + // For MySQL + // + // Ensure that + // - Version is set correctly. + // - Server is initialized with --log-bin-trust-function-creators=1 + // - Server is initialized with --local-infile=1 (for bulk inserts) + // - Connection String has: AllowLoadLocalInfile=true + + // The version of the MySQL server. + "version": "9.2.0-mysql", + + // The database provider. + // + // SUPPORTED: MySql, Postgres, SqlServer, + "provider": "Postgres", + + // Run the migration. + "runMigration": true + }, + "mongoDb": { // The connection string to your Mongo Server. // diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Apps/EFAppRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Apps/EFAppRepositoryTests.cs new file mode 100644 index 000000000..b74f42346 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Apps/EFAppRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Apps.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Apps; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFAppRepositoryTests(PostgresFixture fixture) : AppRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFAppRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositorySnapshotTests.cs new file mode 100644 index 000000000..028d63b54 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositorySnapshotTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.States; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFAssetFolderRepositorySnapshotTests(PostgresFixture fixture) : AssetFolderSnapshotStoreTests +{ + protected override Task> CreateSutAsync() + { + var sut = new EFAssetFolderRepository(fixture.DbContextFactory); + + return Task.FromResult>(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositoryTests.cs new file mode 100644 index 000000000..4e6868a82 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetFolderRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFAssetFolderRepositoryTests(PostgresFixture fixture) : AssetFolderRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFAssetFolderRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositorySnapshotTests.cs new file mode 100644 index 000000000..f9a170cc8 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositorySnapshotTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.States; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFAssetRepositorySnapshotTests(PostgresFixture fixture) : AssetSnapshotStoreTests +{ + protected override Task> CreateSutAsync() + { + var sut = new EFAssetRepository(fixture.DbContextFactory, fixture.Dialect); + + return Task.FromResult>(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositoryTests.cs new file mode 100644 index 000000000..37b1072e6 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Assets/EFAssetRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFAssetRepositoryTests(PostgresFixture fixture) : AssetRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFAssetRepository(fixture.DbContextFactory, fixture.Dialect); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositorySnapshotTests.cs new file mode 100644 index 000000000..867b0f6ba --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositorySnapshotTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.States; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFContentRepositorySnapshotTests(PostgresFixture fixture) : ContentSnapshotStoreTests +{ + protected override Task> CreateSutAsync() + { + var sut = new EFContentRepository(fixture.DbContextFactory, Context.AppProvider, fixture.Dialect); + + return Task.FromResult>(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositoryTests.cs new file mode 100644 index 000000000..621ad4ab5 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/EFContentRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFContentRepositoryTests(PostgresFixture fixture) : ContentRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFContentRepository(fixture.DbContextFactory, AppProvider, fixture.Dialect); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/Text/EFTextIndexerStateTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/Text/EFTextIndexerStateTests.cs new file mode 100644 index 000000000..d890f1ed3 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Contents/Text/EFTextIndexerStateTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.Domain.Apps.Entities.Contents.Text.State; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Contents.Text; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public sealed class EFTextIndexerStateTests(PostgresFixture fixture) : TextIndexerStateTests +{ + protected override Task CreateSutAsync(IContentRepository contentRepository) + { + var sut = new EFTextIndexerState(fixture.DbContextFactory, fixture.Dialect, contentRepository); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/History/EFHistoryEventRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/History/EFHistoryEventRepositoryTests.cs new file mode 100644 index 000000000..950315075 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/History/EFHistoryEventRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.History; +using Squidex.Domain.Apps.Entities.History.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.History; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFHistoryEventRepositoryTests(PostgresFixture fixture) : HistoryEventRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFHistoryEventRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleEventRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleEventRepositoryTests.cs new file mode 100644 index 000000000..fc6de89a8 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleEventRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Rules; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFRuleEventRepositoryTests(PostgresFixture fixture) : RuleEventRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFRuleEventRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleRepositoryTests.cs new file mode 100644 index 000000000..333180582 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Rules/EFRuleRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Rules; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFRuleRepositoryTests(PostgresFixture fixture) : RuleRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFRuleRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Schemas/EFSchemaRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Schemas/EFSchemaRepositoryTests.cs new file mode 100644 index 000000000..675878fef --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Schemas/EFSchemaRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Schemas; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFSchemaRepositoryTests(PostgresFixture fixture) : SchemaRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFSchemaRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Teams/EFTeamRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Teams/EFTeamRepositoryTests.cs new file mode 100644 index 000000000..016c35927 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Domain/Teams/EFTeamRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Teams; +using Squidex.Domain.Apps.Entities.Teams.Repositories; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Domain.Teams; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFTeamRepositoryTests(PostgresFixture fixture) : TeamRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFTeamRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Caching/EFDistributedCacheTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Caching/EFDistributedCacheTests.cs new file mode 100644 index 000000000..b3537fc51 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Caching/EFDistributedCacheTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Caching.Distributed; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Caching; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Infrastructure.Caching; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFDistributedCacheTests(PostgresFixture fixture) : DistributedCacheTests +{ + protected override Task CreateSutAsync(TimeProvider timeProvider) + { + var sut = new EFDistributedCache(fixture.DbContextFactory, timeProvider); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Log/EFRequestLogRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Log/EFRequestLogRepositoryTests.cs new file mode 100644 index 000000000..643efcfb7 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Log/EFRequestLogRepositoryTests.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Options; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Log; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Infrastructure.Log; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFRequestLogRepositoryTests(PostgresFixture fixture) : RequestLogRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = + new EFRequestLogRepository( + fixture.DbContextFactory, Options.Create(new RequestLogStoreOptions())); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/EFMigrationStatusTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/EFMigrationStatusTests.cs new file mode 100644 index 000000000..89af507d5 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/EFMigrationStatusTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Migrations; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Infrastructure.Migrations; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFMigrationStatusTests(PostgresFixture fixture) : MigrationStatusTests +{ + protected override async Task CreateSutAsync() + { + var sut = new EFMigrationStatus(fixture.DbContextFactory); + + await sut.InitializeAsync(default); + + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/MySqlQueryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/MySqlQueryTests.cs new file mode 100644 index 000000000..507d0596c --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/MySqlQueryTests.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Queries; +using Squidex.Providers.MySql; + +namespace Squidex.EntityFramework.Infrastructure.Queries; + +[Trait("Category", "TestContainer")] +[Collection("MySql")] +public class MySqlQueryTests(MySqlFixture fixture) : SqlQueryTests +{ + protected override async Task CreateDbContextAsync() + { + var context = await fixture.DbContextFactory.CreateDbContextAsync(); + + return context; + } + + protected override SqlDialect CreateDialect() + { + return MySqlDialect.Instance; + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/PostgresQueryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/PostgresQueryTests.cs new file mode 100644 index 000000000..c51f2a85e --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/PostgresQueryTests.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Queries; +using Squidex.Providers.Postgres; + +namespace Squidex.EntityFramework.Infrastructure.Queries; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class PostgresQueryTests(PostgresFixture fixture) : SqlQueryTests +{ + protected override async Task CreateDbContextAsync() + { + var context = await fixture.DbContextFactory.CreateDbContextAsync(); + + return context; + } + + protected override SqlDialect CreateDialect() + { + return PostgresDialect.Instance; + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlQueryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlQueryTests.cs new file mode 100644 index 000000000..0398609fb --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlQueryTests.cs @@ -0,0 +1,516 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Queries; + +namespace Squidex.EntityFramework.Infrastructure.Queries; + +public abstract class SqlQueryTests where TContext : DbContext +{ + protected abstract Task CreateDbContextAsync(); + + protected abstract SqlDialect CreateDialect(); + + private class TestSqlBuilder(SqlDialect dialect, string table) : SqlQueryBuilder(dialect, table) + { + public override bool IsJsonPath(PropertyPath path) + { + return path[0] == "Json"; + } + } + + private async Task CreateAndPrepareDbContextAsync() + { + var dbContext = await CreateDbContextAsync(); + + var set = dbContext.Set(); + if (await set.AnyAsync()) + { + return dbContext; + } + + for (var i = 1; i <= 20; i++) + { + object? mixed; + switch (i % 6) + { + case 0: + mixed = null; + break; + case 1: + mixed = $"Prefix{i}Suffix"; + break; + case 2: + mixed = i; + break; + case 3: + mixed = true; + break; + case 4: + mixed = new List { i }; + break; + default: + mixed = new Dictionary(); + break; + } + + set.Add(new TestEntity + { + Boolean = i > 10, + BooleanOrNull = i > 10 ? true : null, + Number = i, + NumberOrNull = i > 10 ? null : i, + Text = $"Prefix{i}Suffix", + Json = new TestJson + { + Boolean = i > 10, + BooleanOrNull = i > 10 ? true : null, + Mixed = mixed, + Number = i, + NumberOrNull = i > 10 ? null : i, + Array = [0, i], + Text = $"Prefix{i}Suffix", + }, + }); + } + + await dbContext.SaveChangesAsync(); + return dbContext; + } + + [Fact] + public async Task Should_query() + { + var actual = await QueryAsync(new ClrQuery()); + + Assert.Equal(Range(1, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_query_with_take() + { + var actual = await QueryAsync(new ClrQuery + { + Take = 5, + Skip = 0, + Sort = [new SortNode("Number", SortOrder.Ascending)], + }); + + Assert.Equal(Range(1, 5), actual); + } + + [Fact] + public async Task Should_query_with_skip() + { + var actual = await QueryAsync(new ClrQuery + { + Take = long.MaxValue, + Skip = 15, + Sort = [new SortNode("Number", SortOrder.Ascending)], + }); + + Assert.Equal(Range(16, 20), actual); + } + + [Fact] + public async Task Should_query_with_skip_and_take() + { + var actual = await QueryAsync(new ClrQuery + { + Take = 10, + Skip = 5, + Sort = [new SortNode("Number", SortOrder.Ascending)], + }); + + Assert.Equal(Range(6, 15), actual); + } + + [Fact] + public async Task Should_sort_by_text() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Text", SortOrder.Descending)], + }); + + Assert.Equal([.. Range(9, 3), 2, 20, 1, .. Range(19, 10)], actual); + } + + [Fact] + public async Task Should_sort_by_text_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Json.text", SortOrder.Descending)], + }); + + Assert.Equal([.. Range(9, 3), 2, 20, 1, .. Range(19, 10)], actual); + } + + [Fact] + public async Task Should_sort_by_number() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Number", SortOrder.Descending)], + }); + + Assert.Equal(Range(20, 1), actual); + } + + [Fact] + public async Task Should_sort_by_number_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Json.number", SortOrder.Descending)], + }); + + Assert.Equal(Range(20, 1), actual); + } + + [Fact] + public async Task Should_sort_by_boolean_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Boolean", SortOrder.Descending), new SortNode("Number", SortOrder.Ascending)], + }); + + Assert.Equal([.. Range(11, 20), .. Range(1, 10)], actual); + } + + [Fact] + public async Task Should_sort_by_boolean() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Json.boolean", SortOrder.Descending), new SortNode("Number", SortOrder.Ascending)], + }); + + Assert.Equal([.. Range(11, 20), .. Range(1, 10)], actual); + } + + [Fact] + public async Task Should_sort_by_mixed_json() + { + var actual = await QueryAsync(new ClrQuery + { + Sort = [new SortNode("Json.mixed", SortOrder.Descending)], + }); + + Assert.Equal([20, 14, 8, 2], actual.Take(4)); + } + + [Fact] + public async Task Should_filter_with_or() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Or(ClrFilter.Lt("Number", 3), ClrFilter.Gt("Number", 17)), + }); + + Assert.Equal([1, 2, 18, 19, 20], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_and() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.And(ClrFilter.Gt("Json.number", 5), ClrFilter.Lt("Json.number", 16)), + }); + + Assert.Equal(Range(6, 15), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_not() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Not(ClrFilter.Gt("Number", 10)), + }); + + Assert.Equal(Range(1, 10), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_number() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Gt("Number", 5), + }); + + Assert.Equal(Range(6, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_number_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Gt("Json.number", 5), + }); + + Assert.Equal(Range(6, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_number_in_mixed_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Gt("Json.mixed", 5), + }); + + Assert.Equal([8, 14, 20], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_null() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("NumberOrNull", ClrValue.Null), + }); + + Assert.Equal(Range(11, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_null_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Json.numberOrNull", ClrValue.Null), + }); + + Assert.Equal(Range(11, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_null_in_mixed_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Json.mixed", ClrValue.Null), + }); + + Assert.Equal([6, 12, 18], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_many() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.In("Number", new List { 3, 5, 7 }), + }); + + Assert.Equal([3, 5, 7], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_many_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.In("Json.number", new List { 3, 5, 7 }), + }); + + Assert.Equal([3, 5, 7], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_with_many_in_mixed_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.In("Json.mixed", new List { 2, 8, 14 }), + }); + + Assert.Equal([2, 8, 14], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_in_json_array() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.And(ClrFilter.Gt("Json.array.1", 5), ClrFilter.Lt("Json.array.1", 16)), + }); + + Assert.Equal(Range(6, 15), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_in_mixed_json_array() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.And(ClrFilter.Gt("Json.mixed.0", 5), ClrFilter.Lt("Json.mixed.0", 16)), + }); + + Assert.Contains(10, actual); + } + + [Fact] + public async Task Should_filter_by_boolean() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Boolean", true), + }); + + Assert.Equal(Range(11, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_boolean_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Json.boolean", true), + }); + + Assert.Equal(Range(11, 20), actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_boolean_in_mixed_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Eq("Json.mixed", true), + }); + + Assert.Equal([3, 9, 15], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_contains() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Contains("Text", "7"), + }); + + Assert.Equal([7, 17], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_contains_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.Contains("Json.text", "7"), + }); + + Assert.Equal([7, 17], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_startsWith() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.StartsWith("Text", "Prefix5"), + }); + + Assert.Equal([5], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_startsWith_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.StartsWith("Json.text", "Prefix5"), + }); + + Assert.Equal([5], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_endWith() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.EndsWith("Text", "5Suffix"), + }); + + Assert.Equal([5, 15], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_filter_by_string_endWith_in_json() + { + var actual = await QueryAsync(new ClrQuery + { + Filter = ClrFilter.EndsWith("Json.text", "5Suffix"), + }); + + Assert.Equal([5, 15], actual.Order().ToArray()); + } + + [Fact] + public async Task Should_query_count() + { + var builder = + new TestSqlBuilder(CreateDialect(), "TestEntity") + .Count(); + + var (sql, parameters) = builder.Compile(); + + var dbContext = await CreateAndPrepareDbContextAsync(); + var dbResult = await dbContext.Database.SqlQueryRaw(sql, parameters).FirstOrDefaultAsync(); + + Assert.Equal(20, dbResult); + } + + private static long[] Range(int from, int to) + { + var result = new List(); + if (to >= from) + { + for (var i = from; i <= to; i++) + { + result.Add(i); + } + } + else + { + for (var i = from; i >= to; i--) + { + result.Add(i); + } + } + + return result.ToArray(); + } + + private async Task> QueryAsync(ClrQuery query) + { + var builder = + new TestSqlBuilder(CreateDialect(), "TestEntity") + .Limit(query) + .Offset(query) + .Order(query) + .Where(query); + + var (sql, parameters) = builder.Compile(); + + var dbContext = await CreateAndPrepareDbContextAsync(); + var dbResult = await dbContext.Set().FromSqlRaw(sql, parameters).ToListAsync(); + + return dbResult.Select(x => x.Number).ToList(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlServerQueryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlServerQueryTests.cs new file mode 100644 index 000000000..ce5b5fcf7 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Queries/SqlServerQueryTests.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.Queries; +using Squidex.Providers.SqlServer; + +namespace Squidex.EntityFramework.Infrastructure.Queries; + +[Trait("Category", "TestContainer")] +[Collection("SqlServer")] +public class SqlServerQueryTests(SqlServerFixture fixture) : SqlQueryTests +{ + protected override async Task CreateDbContextAsync() + { + var context = await fixture.DbContextFactory.CreateDbContextAsync(); + + return context; + } + + protected override SqlDialect CreateDialect() + { + return SqlServerDialect.Instance; + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/States/EFSnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/States/EFSnapshotStoreTests.cs new file mode 100644 index 000000000..96e6d824c --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/States/EFSnapshotStoreTests.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.States; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Infrastructure.States; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFSnapshotStoreTests(PostgresFixture fixture) : SnapshotStoreTests +{ + protected override Task> CreateSutAsync() + { + var sut = new EFSnapshotStore>(fixture.DbContextFactory); + + return Task.FromResult>(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/UsageTracking/EFUsageRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/UsageTracking/EFUsageRepositoryTests.cs new file mode 100644 index 000000000..ded266110 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/UsageTracking/EFUsageRepositoryTests.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.EntityFramework.TestHelpers; +using Squidex.Infrastructure.UsageTracking; +using Squidex.Shared; + +namespace Squidex.EntityFramework.Infrastructure.UsageTracking; + +[Trait("Category", "TestContainer")] +[Collection("Postgres")] +public class EFUsageRepositoryTests(PostgresFixture fixture) : UsageRepositoryTests +{ + protected override Task CreateSutAsync() + { + var sut = new EFUsageRepository(fixture.DbContextFactory); + + return Task.FromResult(sut); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs new file mode 100644 index 000000000..78863e7a2 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs @@ -0,0 +1,61 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure.Migrations; +using Squidex.Providers.MySql; +using Squidex.Providers.Postgres; +using Testcontainers.MySql; + +namespace Squidex.EntityFramework.Migrations; + +[Trait("Category", "TestContainer")] +public class MySqlMigrationTests : IAsyncLifetime +{ + private readonly MySqlContainer mysql = new MySqlBuilder().Build(); + + public async Task InitializeAsync() + { + await mysql.StartAsync(); + } + + public async Task DisposeAsync() + { + await mysql.DisposeAsync(); + } + + [Fact] + public async Task Should_migrate() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + var connectionString = mysql.GetConnectionString(); + + b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), mysql => + { + mysql.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + await databaseMigrator.InitializeAsync(default); + + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + + var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); + Assert.NotEmpty(migrations); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs new file mode 100644 index 000000000..5af740111 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure.Migrations; +using Squidex.Providers.Postgres; +using Testcontainers.PostgreSql; + +namespace Squidex.EntityFramework.Migrations; + +[Trait("Category", "TestContainer")] +public class PostgresMigrationTests : IAsyncLifetime +{ + private readonly PostgreSqlContainer postgreSql = new PostgreSqlBuilder().Build(); + + public async Task InitializeAsync() + { + await postgreSql.StartAsync(); + } + + public async Task DisposeAsync() + { + await postgreSql.DisposeAsync(); + } + + [Fact] + public async Task Should_migrate() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseNpgsql(postgreSql.GetConnectionString()); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + await databaseMigrator.InitializeAsync(default); + + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + + var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); + Assert.NotEmpty(migrations); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs new file mode 100644 index 000000000..06505d551 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs @@ -0,0 +1,56 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Infrastructure.Migrations; +using Squidex.Providers.Postgres; +using Squidex.Providers.SqlServer; +using Testcontainers.MsSql; + +namespace Squidex.EntityFramework.Migrations; + +[Trait("Category", "TestContainer")] +public class SqlServerMigrationTests : IAsyncLifetime +{ + private readonly MsSqlContainer sqlServer = new MsSqlBuilder().Build(); + + public async Task InitializeAsync() + { + await sqlServer.StartAsync(); + } + + public async Task DisposeAsync() + { + await sqlServer.DisposeAsync(); + } + + [Fact] + public async Task Should_migrate() + { + var services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseSqlServer(sqlServer.GetConnectionString()); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); + + var databaseMigrator = services.GetRequiredService>(); + var databaseFactory = services.GetRequiredService>(); + + await databaseMigrator.InitializeAsync(default); + + await using var dbContext = await databaseFactory.CreateDbContextAsync(); + + var migrations = await dbContext.Database.GetAppliedMigrationsAsync(); + Assert.NotEmpty(migrations); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs new file mode 100644 index 000000000..62a035b3e --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs @@ -0,0 +1,76 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Hosting; +using Testcontainers.MySql; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.EntityFramework.TestHelpers; + +[CollectionDefinition("MySql")] +public sealed class MySqlFixtureCollection : ICollectionFixture +{ +} + +public sealed class MySqlFixture : IAsyncLifetime +{ + private readonly MySqlContainer mysql = + new MySqlBuilder() + .WithReuse(true) + .WithLabel("reuse-id", "squidex-mysql") + .Build(); + + private IServiceProvider services; + + public IDbContextFactory DbContextFactory => services.GetRequiredService>(); + + public async Task InitializeAsync() + { + await mysql.StartAsync(); + + services = + new ServiceCollection() + .AddDbContextFactory(b => + { + var connectionString = mysql.GetConnectionString(); + + b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), mysql => + { + mysql.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .BuildServiceProvider(); + + var factory = services.GetRequiredService>(); + var context = await factory.CreateDbContextAsync(); + var creator = (RelationalDatabaseCreator)context.Database.GetService(); + + await creator.EnsureCreatedAsync(); + + foreach (var service in services.GetRequiredService>()) + { + await service.InitializeAsync(default); + } + } + + public async Task DisposeAsync() + { + foreach (var service in services.GetRequiredService>()) + { + await service.ReleaseAsync(default); + } + + await mysql.StopAsync(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs new file mode 100644 index 000000000..6c7a0ec87 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs @@ -0,0 +1,76 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Hosting; +using Squidex.Infrastructure.Queries; +using Squidex.Providers.Postgres; +using Testcontainers.PostgreSql; + +#pragma warning disable CA1822 // Mark members as static +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.EntityFramework.TestHelpers; + +[CollectionDefinition("Postgres")] +public sealed class PostgresFixtureCollection : ICollectionFixture +{ +} + +public sealed class PostgresFixture : IAsyncLifetime +{ + private readonly PostgreSqlContainer postgreSql = + new PostgreSqlBuilder() + .WithReuse(true) + .WithLabel("reuse-id", "squidex-postgres") + .Build(); + + private IServiceProvider services; + + public IDbContextFactory DbContextFactory => services.GetRequiredService>(); + + public SqlDialect Dialect => PostgresDialect.Instance; + + public async Task InitializeAsync() + { + await postgreSql.StartAsync(); + + services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseNpgsql(postgreSql.GetConnectionString()); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .BuildServiceProvider(); + + var factory = services.GetRequiredService>(); + var context = await factory.CreateDbContextAsync(); + var creator = (RelationalDatabaseCreator)context.Database.GetService(); + + await creator.EnsureCreatedAsync(); + + foreach (var service in services.GetRequiredService>()) + { + await service.InitializeAsync(default); + } + } + + public async Task DisposeAsync() + { + foreach (var service in services.GetRequiredService>()) + { + await service.ReleaseAsync(default); + } + + await postgreSql.StopAsync(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs new file mode 100644 index 000000000..594bdc538 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs @@ -0,0 +1,71 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.DependencyInjection; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Hosting; +using Testcontainers.MsSql; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.EntityFramework.TestHelpers; + +[CollectionDefinition("SqlServer")] +public sealed class SqlServerFixtureCollection : ICollectionFixture +{ +} + +public sealed class SqlServerFixture : IAsyncLifetime +{ + private readonly MsSqlContainer sqlServer = + new MsSqlBuilder() + .WithReuse(true) + .WithLabel("reuse-id", "squidex-mssql") + .Build(); + + private IServiceProvider services; + + public IDbContextFactory DbContextFactory => services.GetRequiredService>(); + + public async Task InitializeAsync() + { + await sqlServer.StartAsync(); + + services = + new ServiceCollection() + .AddDbContextFactory(b => + { + b.UseSqlServer(sqlServer.GetConnectionString()); + }) + .AddSingleton(TestUtils.DefaultSerializer) + .BuildServiceProvider(); + + var factory = services.GetRequiredService>(); + var context = await factory.CreateDbContextAsync(); + var creator = (RelationalDatabaseCreator)context.Database.GetService(); + + await creator.EnsureCreatedAsync(); + + foreach (var service in services.GetRequiredService>()) + { + await service.InitializeAsync(default); + } + } + + public async Task DisposeAsync() + { + foreach (var service in services.GetRequiredService>()) + { + await service.ReleaseAsync(default); + } + + await sqlServer.StopAsync(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestDbContext.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestDbContext.cs new file mode 100644 index 000000000..d8164a8e1 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestDbContext.cs @@ -0,0 +1,56 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.EntityFrameworkCore; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json; +using Squidex.Infrastructure.States; +using Squidex.Shared; + +#pragma warning disable CS9107 // Parameter is captured into the state of the enclosing type and its value is also passed to the base constructor. The value might be captured by the base class as well. +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.EntityFramework.TestHelpers; + +public class TestDbContextMySql(DbContextOptions options, IJsonSerializer jsonSerializer) + : TestDbContext(options, jsonSerializer) +{ + protected override string? JsonColumnType() + { + return "json"; + } +} + +public class TestDbContextPostgres(DbContextOptions options, IJsonSerializer jsonSerializer) + : TestDbContext(options, jsonSerializer) +{ + protected override string? JsonColumnType() + { + return "jsonb"; + } +} + +public class TestDbContexSqlServer(DbContextOptions options, IJsonSerializer jsonSerializer) + : TestDbContext(options, jsonSerializer) +{ +} + +public class TestDbContext(DbContextOptions options, IJsonSerializer jsonSerializer) + : AppDbContext(options, jsonSerializer) +{ + protected override void OnModelCreating(ModelBuilder builder) + { + builder.UseSnapshot>(jsonSerializer, JsonColumnType()); + + builder.Entity(b => + { + b.Property(x => x.Json).AsJsonString(jsonSerializer, JsonColumnType()); + }); + + base.OnModelCreating(builder); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestEntity.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestEntity.cs new file mode 100644 index 000000000..d0236798c --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/TestEntity.cs @@ -0,0 +1,47 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.EntityFramework.TestHelpers; + +public class TestEntity +{ + [Key] + public Guid Id { get; set; } = Guid.NewGuid(); + + public long Number { get; set; } + + public long? NumberOrNull { get; set; } + + public string Text { get; set; } + + public bool Boolean { get; set; } + + public bool? BooleanOrNull { get; set; } + + public TestJson Json { get; set; } +} + +public class TestJson +{ + public long Number { get; set; } + + public long? NumberOrNull { get; set; } + + public string Text { get; set; } + + public bool Boolean { get; set; } + + public bool? BooleanOrNull { get; set; } + + public object? Mixed { get; set; } + + public long[] Array { get; set; } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Apps/MongoAppRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Apps/MongoAppRepositoryTests.cs new file mode 100644 index 000000000..42698a421 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Apps/MongoAppRepositoryTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Apps.Repositories; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Apps; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoAppRepositoryTests(MongoFixture fixture) : AppRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoAppRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetMappingTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetMappingTests.cs index a64176827..967617737 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetMappingTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetMappingTests.cs @@ -22,9 +22,7 @@ public class AssetMappingTests : GivenContext var snapshotJob = new SnapshotWriteJob(source.UniqueId, source, source.Version); var snapshot = MongoAssetEntity.Create(snapshotJob); - var mapped = snapshot.ToState(); - - mapped.Should().BeEquivalentTo(source); + snapshot.Should().BeEquivalentTo(source); } [Fact] @@ -35,7 +33,7 @@ public class AssetMappingTests : GivenContext var snapshotJob = new SnapshotWriteJob(source.UniqueId, source, source.Version); var snapshot = MongoAssetFolderEntity.Create(snapshotJob); - var mapped = snapshot.ToState(); + var mapped = snapshot; mapped.Should().BeEquivalentTo(source); } diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetQueryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetQueryTests.cs index d27abdc08..1c63d5f19 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetQueryTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetQueryTests.cs @@ -13,7 +13,7 @@ using Squidex.Domain.Apps.Entities.Assets.Visitors; using Squidex.Infrastructure; using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Validation; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter; namespace Squidex.MongoDb.Domain.Assets; @@ -25,7 +25,6 @@ public class AssetQueryTests static AssetQueryTests() { MongoAssetEntity.RegisterClassMap(); - MongoTestUtils.SetupBson(); } diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryFixture.cs deleted file mode 100644 index b0010ab81..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryFixture.cs +++ /dev/null @@ -1,135 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Globalization; -using Microsoft.Extensions.DependencyInjection; -using MongoDB.Driver; -using Squidex.Domain.Apps.Core.Assets; -using Squidex.Domain.Apps.Core.TestHelpers; -using Squidex.Domain.Apps.Entities.MongoDb.Assets; -using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.States; - -namespace Squidex.MongoDb.Domain.Assets; - -public sealed class AssetsQueryFixture : GivenContext, IAsyncLifetime -{ - private readonly int numValues = 250; - - private readonly NamedId[] appIds = - [ - NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), - NamedId.Of(DomainId.Create("4b3672c1-97c6-4e0b-a067-71e9e9a29db9"), "my-app1") - ]; - - public IMongoDatabase Database { get; } - - public MongoAssetRepository AssetRepository { get; } - - public AssetsQueryFixture() - { - BsonJsonConvention.Register(TestUtils.DefaultOptions()); - - var mongoClient = MongoClientFactory.Create(TestConfig.Configuration["mongoDb:configuration"]); - var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]); - - Database = mongoDatabase; - - var services = - new ServiceCollection() - .AddSingleton() - .AddSingleton(mongoClient) - .AddSingleton(mongoDatabase) - .AddLogging() - .BuildServiceProvider(); - - AssetRepository = services.GetRequiredService(); - } - - public async Task InitializeAsync() - { - await AssetRepository.InitializeAsync(default); - - await CreateDataAsync(default); - } - - public Task DisposeAsync() - { - return Task.CompletedTask; - } - - private async Task CreateDataAsync( - CancellationToken ct) - { - if (await AssetRepository.StreamAll(appIds[0].Id, ct).AnyAsync(ct)) - { - return; - } - - var batch = new List>(); - - async Task ExecuteBatchAsync(Asset? entity) - { - if (entity != null) - { - batch.Add(new SnapshotWriteJob(entity.UniqueId, entity, 0)); - } - - if ((entity == null || batch.Count >= 1000) && batch.Count > 0) - { - var store = (ISnapshotStore)AssetRepository; - - await store.WriteManyAsync(batch, ct); - batch.Clear(); - } - } - - foreach (var appId in appIds) - { - for (var i = 0; i < numValues; i++) - { - var fileName = i.ToString(CultureInfo.InvariantCulture); - - for (var j = 0; j < numValues; j++) - { - var tag = j.ToString(CultureInfo.InvariantCulture); - - var asset = CreateAsset() with - { - FileHash = fileName, - FileName = fileName, - Metadata = new AssetMetadata - { - ["value"] = JsonValue.Create(tag) - }, - Tags = - [ - tag - ], - Slug = fileName - }; - - await ExecuteBatchAsync(asset); - } - } - } - - await ExecuteBatchAsync(null); - } - - public DomainId RandomAppId() - { - return appIds[Random.Shared.Next(appIds.Length)].Id; - } - - public string RandomValue() - { - return Random.Shared.Next(numValues).ToString(CultureInfo.InvariantCulture); - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryIntegrationTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryIntegrationTests.cs deleted file mode 100644 index 19634351f..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/AssetsQueryIntegrationTests.cs +++ /dev/null @@ -1,210 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Domain.Apps.Core.Assets; -using Squidex.Domain.Apps.Entities; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Queries; -using F = Squidex.Infrastructure.Queries.ClrFilter; - -#pragma warning disable MA0040 // Forward the CancellationToken parameter to methods that take one -#pragma warning disable SA1300 // Element should begin with upper-case letter -#pragma warning disable xUnit1044 // Avoid using TheoryData type arguments that are not serializable - -namespace Squidex.MongoDb.Domain.Assets; - -[Trait("Category", "Dependencies")] -public class AssetsQueryIntegrationTests : IClassFixture, IAsyncLifetime -{ - private readonly ProfilerCollection profiler; - - public static readonly TheoryData ParentIds = new TheoryData - { - { null }, - { DomainId.Empty } - }; - - public AssetsQueryFixture _ { get; } - - public AssetsQueryIntegrationTests(AssetsQueryFixture fixture) - { - _ = fixture; - - profiler = new ProfilerCollection(_.Database); - } - - public Task InitializeAsync() - { - return profiler.ClearAsync(); - } - - public async Task DisposeAsync() - { - var queries = await profiler.GetQueriesAsync(_.AssetRepository.GetInternalCollection().CollectionNamespace.CollectionName); - - Assert.All(queries, query => - { - Assert.Equal(query.NumDocuments, query.DocsExamined); - }); - } - - [Fact] - public async Task Should_find_asset_by_slug() - { - var random = _.RandomValue(); - - var asset = await _.AssetRepository.FindAssetBySlugAsync(_.RandomAppId(), random, false); - - // The Slug is random here, as it does not really matter. - Assert.NotNull(asset); - } - - [Fact] - public async Task Should_query_asset_by_hash() - { - var random = _.RandomValue(); - - var assets = await _.AssetRepository.FindAssetByHashAsync(_.RandomAppId(), random, random, 1024); - - // The Hash is random here, as it does not really matter. - Assert.NotNull(assets); - } - - [Fact] - public async Task Should_query_ids() - { - var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet(); - - var assets = await _.AssetRepository.QueryIdsAsync(_.RandomAppId(), ids); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(assets); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets(DomainId? parentId) - { - var query = new ClrQuery(); - - var assets = await QueryAsync(parentId, query); - - // Default page size is 1000. - Assert.Equal(1000, assets.Count); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets_with_random_count(DomainId? parentId) - { - var query = new ClrQuery - { - Random = 40 - }; - - var assets = await QueryAsync(parentId, query); - - // Default page size is 1000, so we expect less elements. - Assert.Equal(40, assets.Count); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets_by_tags(DomainId? parentId) - { - var random = _.RandomValue(); - - var query = new ClrQuery - { - Filter = F.Eq("Tags", random) - }; - - var assets = await QueryAsync(parentId, query); - - // The tag is random here, as it does not really matter. - Assert.NotNull(assets); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets_by_tags_and_fileName(DomainId? parentId) - { - var random = _.RandomValue(); - - var query = new ClrQuery - { - Filter = F.And(F.Eq("Tags", random), F.Contains("FileName", random)) - }; - - var assets = await QueryAsync(parentId, query); - - // The filter is a random value from the expected result set. - Assert.NotEmpty(assets); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets_by_fileName(DomainId? parentId) - { - var random = _.RandomValue(); - - var query = new ClrQuery - { - Filter = F.Contains("FileName", random) - }; - - var assets = await QueryAsync(parentId, query); - - // The filter is a random value from the expected result set. - Assert.NotEmpty(assets); - } - - [Theory] - [MemberData(nameof(ParentIds))] - public async Task Should_query_assets_by_fileName_and_tags(DomainId? parentId) - { - var random = _.RandomValue(); - - var query = new ClrQuery - { - Filter = F.And(F.Contains("FileName", random), F.Eq("Tags", random)) - }; - - var assets = await QueryAsync(parentId, query); - - // The filter is a random value from the expected result set. - Assert.NotEmpty(assets); - } - - private async Task> QueryAsync(DomainId? parentId, ClrQuery clrQuery, - int top = 1000, - int skip = 100) - { - clrQuery.Top = top; - clrQuery.Skip = skip; - clrQuery.Sort ??= []; - - if (clrQuery.Sort.Count == 0) - { - clrQuery.Sort.Add(new SortNode("lastModified", SortOrder.Descending)); - } - - if (!clrQuery.Sort.Exists(x => x.Path.Equals("id"))) - { - clrQuery.Sort.Add(new SortNode("id", SortOrder.Ascending)); - } - - var q = - Q.Empty - .WithoutTotal() - .WithQuery(clrQuery); - - var assets = await _.AssetRepository.QueryAsync(_.RandomAppId(), parentId, q); - - return assets; - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetFolderRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetFolderRepositorySnapshotTests.cs new file mode 100644 index 000000000..fac5b7c80 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetFolderRepositorySnapshotTests.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.MongoDb.Assets; +using Squidex.Infrastructure.States; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoAssetFolderRepositorySnapshotTests(MongoFixture fixture) : AssetFolderSnapshotStoreTests +{ + protected override async Task> CreateSutAsync() + { + var sut = new MongoAssetFolderRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositorySnapshotTests.cs new file mode 100644 index 000000000..20d9e4bf1 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositorySnapshotTests.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.MongoDb.Assets; +using Squidex.Infrastructure.States; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoAssetRepositorySnapshotTests(MongoFixture fixture) : AssetSnapshotStoreTests +{ + protected override async Task> CreateSutAsync() + { + var sut = new MongoAssetRepository(fixture.Database, A.Fake>(), string.Empty); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositoryTests.cs new file mode 100644 index 000000000..ab7e6f1cd --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Assets/MongoAssetRepositoryTests.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Domain.Apps.Entities.MongoDb.Assets; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Assets; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoAssetRepositoryTests(MongoFixture fixture) : AssetRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoAssetRepository(fixture.Database, A.Fake>(), string.Empty); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentQueryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentQueryTests.cs index e5fc46fe8..7202f2014 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentQueryTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentQueryTests.cs @@ -15,7 +15,7 @@ using Squidex.Domain.Apps.Entities.Contents.Operations; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Queries; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter; namespace Squidex.MongoDb.Domain.Contents; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryFixture.cs deleted file mode 100644 index 6b3f9e996..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryFixture.cs +++ /dev/null @@ -1,209 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Globalization; -using LoremNET; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using MongoDB.Driver; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.TestHelpers; -using Squidex.Domain.Apps.Entities; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.MongoDb.Contents; -using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.States; - -#pragma warning disable MA0048 // File name must match type name - -namespace Squidex.MongoDb.Domain.Contents; - -public sealed class ContentsQueryFixture_Default : ContentsQueryFixture -{ - public ContentsQueryFixture_Default() - : base(false) - { - } -} - -public sealed class ContentsQueryFixture_Dedicated : ContentsQueryFixture -{ - public ContentsQueryFixture_Dedicated() - : base(true) - { - } -} - -public abstract class ContentsQueryFixture : GivenContext, IAsyncLifetime -{ - private readonly int numValues = 10000; - - public IMongoDatabase Database { get; } - - public MongoContentRepository ContentRepository { get; } - - public NamedId[] AppIds { get; } = - [ - NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), - NamedId.Of(DomainId.Create("4b3672c1-97c6-4e0b-a067-71e9e9a29db9"), "my-app1") - ]; - - public NamedId[] SchemaIds { get; } = - [ - NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-schema1"), - NamedId.Of(DomainId.Create("4b3672c1-97c6-4e0b-a067-71e9e9a29db9"), "my-schema2"), - NamedId.Of(DomainId.Create("76357c9b-0514-4377-9fcc-a632e7ef960d"), "my-schema3"), - NamedId.Of(DomainId.Create("164c451e-e5a8-41f8-8aaf-e4b56603d7e7"), "my-schema4"), - NamedId.Of(DomainId.Create("741e902c-fdfa-41ad-8e5a-b7cb9d6e3d94"), "my-schema5") - ]; - - protected ContentsQueryFixture(bool selfHosting) - { - BsonJsonConvention.Register(TestUtils.DefaultOptions()); - - var mongoClient = MongoClientFactory.Create(TestConfig.Configuration["mongoDb:configuration"]); - var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]); - - Database = mongoDatabase; - - var services = - new ServiceCollection() - .AddSingleton(Options.Create(new ContentsOptions { OptimizeForSelfHosting = selfHosting })) - .AddSingleton(CreateAppProvider()) - .AddSingleton(mongoClient) - .AddSingleton(mongoDatabase) - .AddSingleton() - .AddLogging() - .BuildServiceProvider(); - - ContentRepository = services.GetRequiredService(); - } - - public async Task InitializeAsync() - { - await ContentRepository.InitializeAsync(default); - - await CreateDataAsync(default); - } - - public Task DisposeAsync() - { - return Task.CompletedTask; - } - - private async Task CreateDataAsync( - CancellationToken ct) - { - if (await ContentRepository.StreamAll(AppIds[0].Id, null, SearchScope.All, ct).AnyAsync(ct)) - { - return; - } - - var batch = new List>(); - - async Task ExecuteBatchAsync(WriteContent? state) - { - if (state != null) - { - batch.Add(new SnapshotWriteJob(state.UniqueId, state, 0)); - } - - if ((state == null || batch.Count >= 1000) && batch.Count > 0) - { - var store = (ISnapshotStore)ContentRepository; - - await store.WriteManyAsync(batch, ct); - - batch.Clear(); - } - } - - foreach (var appId in AppIds) - { - foreach (var schemaId in SchemaIds) - { - for (var i = 0; i < numValues; i++) - { - var content = CreateWriteContent() with - { - AppId = appId, - CurrentVersion = new ContentVersion( - Status.Published, - new ContentData() - .AddField("field1", - new ContentFieldData() - .AddInvariant(JsonValue.Create(i))) - .AddField("field2", - new ContentFieldData() - .AddInvariant(JsonValue.Create(Lorem.Paragraph(200, 20))))), - SchemaId = schemaId - }; - - await ExecuteBatchAsync(content); - } - } - } - - await ExecuteBatchAsync(null); - } - - private IAppProvider CreateAppProvider() - { - var appProvider = A.Fake(); - - A.CallTo(() => appProvider.GetAppWithSchemaAsync(A._, A._, false, A._)) - .ReturnsLazily(x => - { - var appId = x.GetArgument(0)!; - - return Task.FromResult<(App?, Schema?)>(( - CreateApp(appId), - CreateSchema(appId, x.GetArgument(1)!))); - }); - - return appProvider; - } - - public DomainId RandomAppId() - { - return AppIds[Random.Shared.Next(AppIds.Length)].Id; - } - - public DomainId RandomSchemaId() - { - return SchemaIds[Random.Shared.Next(SchemaIds.Length)].Id; - } - - public App RandomApp() - { - return CreateApp(RandomAppId()); - } - - public Schema RandomSchema() - { - return CreateSchema(RandomAppId(), RandomSchemaId()); - } - - public string RandomValue() - { - return Random.Shared.Next(numValues).ToString(CultureInfo.InvariantCulture); - } - - private App CreateApp(DomainId appId) - { - return App with { Id = appId }; - } - - private Schema CreateSchema(DomainId appId, DomainId schemaId) - { - return Schema with { Id = schemaId, AppId = NamedId.Of(appId, "my-app") }; - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryTestsBase.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryTestsBase.cs deleted file mode 100644 index f1068c0f8..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/ContentsQueryTestsBase.cs +++ /dev/null @@ -1,219 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using NodaTime; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Entities; -using Squidex.Domain.Apps.Entities.Contents; -using Squidex.Domain.Apps.Entities.Contents.Repositories; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Queries; -using F = Squidex.Infrastructure.Queries.ClrFilter; - -#pragma warning disable SA1300 // Element should begin with upper-case letter - -namespace Squidex.MongoDb.Domain.Contents; - -public abstract class ContentsQueryTestsBase(ContentsQueryFixture fixture) -{ - public ContentsQueryFixture _ { get; } = fixture; - - [Fact] - public async Task Should_verify_ids() - { - var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet(); - - var contents = await _.ContentRepository.QueryIdsAsync(_.RandomApp(), ids, SearchScope.Published); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_by_ids() - { - var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet(); - - var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), [_.RandomSchema()], Q.Empty.WithIds(ids), SearchScope.All); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_by_ids_and_schema() - { - var ids = Enumerable.Repeat(0, 50).Select(_ => DomainId.NewGuid()).ToHashSet(); - - var contents = await _.ContentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), Q.Empty.WithIds(ids), SearchScope.All); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_ids_by_filter() - { - var filter = F.Eq("data.field1.iv", 12); - - var contents = await _.ContentRepository.QueryIdsAsync(_.RandomApp(), _.RandomSchema(), filter, SearchScope.All); - - // We have a concrete query, so we expect an actual. - Assert.NotEmpty(contents); - } - - [Fact] - public async Task Should_query_contents_by_filter() - { - var query = new ClrQuery - { - Filter = F.Eq("data.field1.iv", 12) - }; - - var contents = await QueryAsync(_.ContentRepository, query, 1000, 0); - - // We have a concrete query, so we expect an actual. - Assert.NotEmpty(contents); - } - - [Fact] - public async Task Should_query_contents_scheduled() - { - var time = SystemClock.Instance.GetCurrentInstant(); - - var contents = await _.ContentRepository.StreamScheduledWithoutDataAsync(time, SearchScope.All).ToListAsync(); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_with_default_query() - { - var query = new ClrQuery(); - - var contents = await QueryAsync(_.ContentRepository, query); - - // We have a concrete query, so we expect an actual result. - Assert.NotEmpty(contents); - } - - [Fact] - public async Task Should_query_contents_with_default_query_and_id() - { - var query = new ClrQuery(); - - var contents = await QueryAsync(_.ContentRepository, query, reference: DomainId.NewGuid()); - - // The IDs are random here, as it does not really matter. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_with_large_skip() - { - var query = new ClrQuery - { - Sort = - [ - new SortNode("data.value.iv", SortOrder.Ascending) - ] - }; - - var contents = await QueryAsync(_.ContentRepository, query, 1000, 9000); - - // We have a concrete query, so we expect an actual result. - Assert.NotEmpty(contents); - } - - [Fact] - public async Task Should_query_contents_with_query_fulltext() - { - var query = new ClrQuery - { - FullText = "hello" - }; - - var contents = await QueryAsync(_.ContentRepository, query); - - // The full text is resolved by another system, so we cannot verify the actual result. - Assert.NotNull(contents); - } - - [Fact] - public async Task Should_query_contents_with_query_filter() - { - var query = new ClrQuery - { - Filter = F.Eq("data.field1.iv", 200) - }; - - var contents = await QueryAsync(_.ContentRepository, query, 1000, 0); - - // We have a concrete query, so we expect an actual result. - Assert.NotEmpty(contents); - } - - [Fact] - public async Task Should_query_contents_with_query_filter_and_id() - { - var query = new ClrQuery - { - Filter = F.Eq("data.value.iv", 12) - }; - - var contents = await QueryAsync(_.ContentRepository, query, 1000, 0, reference: DomainId.NewGuid()); - - // We do not insert test entities with references, so we cannot verify the actual result. - Assert.Empty(contents); - } - - [Fact] - public async Task Should_query_contents_with_random_count() - { - var query = new ClrQuery - { - Random = 40 - }; - - var contents = await QueryAsync(_.ContentRepository, query); - - // We do not insert test entities with references, so we cannot verify the actual. - Assert.Equal(40, contents.Count); - } - - private async Task> QueryAsync(IContentRepository contentRepository, - ClrQuery clrQuery, - int top = 1000, - int skip = 100, - DomainId reference = default) - { - clrQuery.Take = top; - clrQuery.Skip = skip; - clrQuery.Sort ??= []; - - if (clrQuery.Sort.Count == 0) - { - clrQuery.Sort.Add(new SortNode("lastModified", SortOrder.Descending)); - } - - if (!clrQuery.Sort.Exists(x => x.Path.Equals("id"))) - { - clrQuery.Sort.Add(new SortNode("id", SortOrder.Ascending)); - } - - var q = - Q.Empty - .WithoutTotal() - .WithQuery(clrQuery) - .WithReference(reference); - - var contents = await contentRepository.QueryAsync(_.RandomApp(), _.RandomSchema(), q, SearchScope.All); - - return contents; - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/IndexParserTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/IndexParserTests.cs index 9d8674a27..2f6ef63b8 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/IndexParserTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/IndexParserTests.cs @@ -23,7 +23,7 @@ public class IndexParserTests ["mt"] = 1, ["mb"] = -1, ["do.field1"] = 1, - } + }, }; static IndexParserTests() diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryDedicatedTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryDedicatedTests.cs new file mode 100644 index 000000000..b987157d5 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryDedicatedTests.cs @@ -0,0 +1,35 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.MongoDb.Contents; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoContentRepositoryDedicatedTests(MongoFixture fixture) : ContentRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = + new MongoContentRepository( + fixture.Database, + AppProvider, + string.Empty, + Options.Create(new ContentsOptions { OptimizeForSelfHosting = true }), + A.Fake>()); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotDedicatedTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotDedicatedTests.cs new file mode 100644 index 000000000..2d656b2a7 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotDedicatedTests.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.MongoDb.Contents; +using Squidex.Infrastructure.States; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoContentRepositorySnapshotDedicatedTests(MongoFixture fixture) : ContentSnapshotStoreTests +{ + protected override bool CheckConsistencyOnWrite => false; + + protected override async Task> CreateSutAsync() + { + var sut = + new MongoContentRepository( + fixture.Database, + Context.AppProvider, + string.Empty, + Options.Create(new ContentsOptions { OptimizeForSelfHosting = true }), + A.Fake>()); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotTests.cs new file mode 100644 index 000000000..05f26bab8 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositorySnapshotTests.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.MongoDb.Contents; +using Squidex.Infrastructure.States; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoContentRepositorySnapshotTests(MongoFixture fixture) : ContentSnapshotStoreTests +{ + protected override bool CheckConsistencyOnWrite => false; + + protected override async Task> CreateSutAsync() + { + var sut = + new MongoContentRepository( + fixture.Database, + Context.AppProvider, + string.Empty, + Options.Create(new ContentsOptions()), + A.Fake>()); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryTests.cs new file mode 100644 index 000000000..e5f03de85 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/MongoContentRepositoryTests.cs @@ -0,0 +1,35 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.MongoDb.Contents; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Contents; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoContentRepositoryTests(MongoFixture fixture) : ContentRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = + new MongoContentRepository( + fixture.Database, + AppProvider, + string.Empty, + Options.Create(new ContentsOptions()), + A.Fake>()); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/StatusSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/StatusSerializerTests.cs index a1011ff1a..e6e9b3eb7 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/StatusSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/StatusSerializerTests.cs @@ -6,11 +6,11 @@ // ========================================================================== using Squidex.Domain.Apps.Core.Contents; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Domain.Contents; -public sealed class StatusSerializerTests +public class StatusSerializerTests { [Fact] public void Should_serialize_and_deserialize_status() diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasParsingTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasParsingTests.cs index eec0a402f..1f5e82b86 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasParsingTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasParsingTests.cs @@ -11,7 +11,7 @@ using Lucene.Net.Analysis.Standard; using Lucene.Net.Analysis.Util; using Lucene.Net.Util; using MongoDB.Bson; -using Squidex.Domain.Apps.Entities.Text; +using Squidex.Domain.Apps.Entities.Contents.Text; using LuceneQueryAnalyzer = Lucene.Net.QueryParsers.Classic.QueryParser; namespace Squidex.MongoDb.Domain.Contents.Text; @@ -24,7 +24,7 @@ public class AtlasParsingTests new StandardAnalyzer(LuceneVersion.LUCENE_48, CharArraySet.EMPTY_SET)); private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions { - WriteIndented = true + WriteIndented = true, }; [Fact] @@ -38,10 +38,10 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "hello" - } + query = "hello", + }, }); Assert.Equal(expected, actual); @@ -58,10 +58,10 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = new[] { "hello", "dolly" } - } + query = new[] { "hello", "dolly" }, + }, }); Assert.Equal(expected, actual); @@ -87,20 +87,20 @@ public class AtlasParsingTests { "the", "right", - "way" - } - } + "way", + }, + }, }, new { text = new { path = "text", - query = "go" - } - } - } - } + query = "go", + }, + }, + }, + }, }); Assert.Equal(expected, actual); @@ -126,9 +126,9 @@ public class AtlasParsingTests { "do", "it", - "right" - } - } + "right", + }, + }, }, new { @@ -136,13 +136,13 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "right" - } - } - } - } + query = "right", + }, + }, + }, + }, }); Assert.Equal(expected, actual); @@ -159,10 +159,10 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "te?t" - } + query = "te?t", + }, }); Assert.Equal(expected, actual); @@ -179,10 +179,10 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "test*" - } + query = "test*", + }, }); Assert.Equal(expected, actual); @@ -199,14 +199,14 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, query = "roam", fuzzy = new { - maxEdits = 2 - } - } + maxEdits = 2, + }, + }, }); Assert.Equal(expected, actual); @@ -223,14 +223,14 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, query = "roam", fuzzy = new { - maxEdits = 1 - } - } + maxEdits = 1, + }, + }, }); Assert.Equal(expected, actual); @@ -247,15 +247,15 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, query = new[] { "jakarta", - "apache" + "apache", }, - slop = 10 - } + slop = 10, + }, }); Assert.Equal(expected, actual); @@ -284,10 +284,10 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "jakarta" - } + query = "jakarta", + }, }, new { @@ -295,13 +295,13 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "apache" - } - } - } - } + query = "apache", + }, + }, + }, + }, }, new { @@ -309,13 +309,13 @@ public class AtlasParsingTests { path = new { - wildcard = "*" + wildcard = "*", }, - query = "website" - } - } - } - } + query = "website", + }, + }, + }, + }, }); Assert.Equal(expected, actual); @@ -337,8 +337,8 @@ public class AtlasParsingTests text = new { path = "title", - query = "return" - } + query = "return", + }, }, new { @@ -348,12 +348,12 @@ public class AtlasParsingTests query = new[] { "pink", - "panther" - } - } - } - } - } + "panther", + }, + }, + }, + }, + }, }); Assert.Equal(expected, actual); @@ -370,8 +370,8 @@ public class AtlasParsingTests { path = "mod_date", gte = 20020101, - lte = 20030101 - } + lte = 20030101, + }, }); Assert.Equal(expected, actual); @@ -388,8 +388,8 @@ public class AtlasParsingTests { path = "mod_date", gt = 20020101, - lt = 20030101 - } + lt = 20030101, + }, }); Assert.Equal(expected, actual); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasTextIndexFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasTextIndexFixture.cs index 87b4e04bf..d208a8902 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasTextIndexFixture.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/AtlasTextIndexFixture.cs @@ -9,10 +9,10 @@ using System.Net; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Domain.Apps.Entities.Text; using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Domain.Contents.Text; @@ -42,7 +42,7 @@ public sealed class AtlasTextIndexFixture : IAsyncLifetime { return new HttpClientHandler { - Credentials = new NetworkCredential(options.PublicKey, options.PrivateKey, "cloud.mongodb.com") + Credentials = new NetworkCredential(options.PublicKey, options.PrivateKey, "cloud.mongodb.com"), }; }).Services .BuildServiceProvider(); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/BsonUniqueContentIdSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/BsonUniqueContentIdSerializerTests.cs index ed80d9f69..e8c4fd916 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/BsonUniqueContentIdSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/BsonUniqueContentIdSerializerTests.cs @@ -8,7 +8,7 @@ using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Domain.Apps.Entities.MongoDb; using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Domain.Contents.Text; @@ -19,13 +19,13 @@ public class BsonUniqueContentIdSerializerTests BsonUniqueContentIdSerializer.Register(); } - public static readonly TheoryData CustomIds = new TheoryData - { + public static readonly TheoryData CustomIds = + [ "id", "id-short", "id-and-guid-size", - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." - }; + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + ]; [Fact] public void Should_serialize_and_deserialize_guid_guid() @@ -109,24 +109,4 @@ public class BsonUniqueContentIdSerializerTests Assert.Contains("App ID cannot be longer than 253 bytes.", exception.Message, StringComparison.Ordinal); } - - [Fact] - public void Should_calculate_next_custom_id() - { - var appId = DomainId.Create("x"); - - var actual = BsonUniqueContentIdSerializer.NextAppId(appId); - - Assert.Equal(new UniqueContentId(DomainId.Create("y"), DomainId.Empty), actual); - } - - [Fact] - public void Should_calculate_next_guid_id() - { - var appId = DomainId.Create("70fb3772-2ab5-4854-b421-054d2479a0f7"); - - var actual = BsonUniqueContentIdSerializer.NextAppId(appId); - - Assert.Equal(new UniqueContentId(DomainId.Create("70fb3773-2ab5-4854-b421-054d2479a0f7"), DomainId.Empty), actual); - } } diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexFixture.cs deleted file mode 100644 index 7762acaaf..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexFixture.cs +++ /dev/null @@ -1,47 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Domain.Apps.Entities.Text; -using Squidex.Infrastructure; -using Squidex.TestHelpers; -using Testcontainers.MongoDb; - -namespace Squidex.MongoDb.Domain.Contents.Text; - -public sealed class MongoTextIndexFixture : IAsyncLifetime -{ - private readonly MongoDbContainer mongoDb = - new MongoDbBuilder() - .WithReuse(true) - .WithLabel("reuse-id", "mongo-text-index") - .Build(); - - public MongoTextIndex Index { get; private set; } - - public MongoTextIndexFixture() - { - MongoTestUtils.SetupBson(); - } - - public async Task InitializeAsync() - { - await mongoDb.StartAsync(); - - var mongoClient = MongoClientFactory.Create(TestConfig.Configuration["mongoDb:configuration"]!); - var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]!); - - Index = new MongoTextIndex(mongoDatabase, string.Empty); - - await Index.InitializeAsync(default); - } - - public async Task DisposeAsync() - { - await mongoDb.StopAsync(); - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexTests.cs index 1e362463a..2a97ef22e 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexTests.cs @@ -8,21 +8,24 @@ #pragma warning disable SA1300 // Element should begin with upper-case letter using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Domain.Contents.Text; [Trait("Category", "Dependencies")] -public class MongoTextIndexTests(MongoTextIndexFixture fixture) : TextIndexerTestsBase, IClassFixture +[Collection("Mongo")] +public class MongoTextIndexTests(MongoFixture fixture) : TextIndexerTestsBase { public override bool SupportsQuerySyntax => false; public override bool SupportsGeo => true; - public MongoTextIndexFixture _ { get; } = fixture; - - public override Task CreateSutAsync() + public override async Task CreateSutAsync() { - return Task.FromResult(_.Index); + var sut = new MongoTextIndex(fixture.Database, string.Empty); + + await sut.InitializeAsync(default); + return sut; } [Fact] diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateFixture.cs deleted file mode 100644 index 5a783b982..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateFixture.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Domain.Apps.Entities.Contents.Repositories; -using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Domain.Apps.Entities.Text; -using Squidex.Infrastructure; -using Squidex.TestHelpers; -using Testcontainers.MongoDb; - -namespace Squidex.MongoDb.Domain.Contents.Text; - -public sealed class MongoTextIndexerStateFixture : IAsyncLifetime -{ - private readonly MongoDbContainer mongoDb = - new MongoDbBuilder() - .WithReuse(true) - .WithLabel("reuse-id", "mongo-text-indexer") - .Build(); - - public IContentRepository ContentRepository { get; } = A.Fake(); - - public MongoTextIndexerState State { get; private set; } - - public MongoTextIndexerStateFixture() - { - MongoTestUtils.SetupBson(); - } - - public async Task InitializeAsync() - { - await mongoDb.StartAsync(); - - var mongoClient = MongoClientFactory.Create(TestConfig.Configuration["mongoDb:configuration"]!); - var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]!); - - State = new MongoTextIndexerState(mongoDatabase, ContentRepository); - - await State.InitializeAsync(default); - } - - public async Task DisposeAsync() - { - await mongoDb.DisposeAsync(); - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateTests.cs index 78d2c577f..92bb9416d 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/Text/MongoTextIndexerStateTests.cs @@ -7,123 +7,23 @@ #pragma warning disable SA1300 // Element should begin with upper-case letter -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Entities; -using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.Contents.Text; using Squidex.Domain.Apps.Entities.Contents.Text.State; -using Squidex.Infrastructure; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; namespace Squidex.MongoDb.Domain.Contents.Text; -[Trait("Category", "Dependencies")] -public class MongoTextIndexerStateTests(MongoTextIndexerStateFixture fixture) : IClassFixture +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoTextIndexerStateTests(MongoFixture fixture) : TextIndexerStateTests { - public MongoTextIndexerStateFixture _ { get; set; } = fixture; - - [Fact] - public async Task Should_add_state() - { - var appId = DomainId.NewGuid(); - var id1 = new UniqueContentId(appId, DomainId.NewGuid()); - var id2 = new UniqueContentId(appId, DomainId.NewGuid()); - var id3 = new UniqueContentId(appId, DomainId.NewGuid()); - - await _.State.SetAsync( - [ - new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, - new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, - new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None } - ]); - - var actual = await _.State.GetAsync(HashSet.Of(id1, id2)); - - actual.Should().BeEquivalentTo(new Dictionary - { - [id1] = new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, - [id2] = new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft } - }); - } - - [Fact] - public async Task Should_remove_state() - { - var id = new UniqueContentId(DomainId.NewGuid(), DomainId.NewGuid()); - - await _.State.SetAsync( - [ - new TextContentState { UniqueContentId = id, State = TextState.Stage0_Draft__Stage1_None } - ]); - - await _.State.SetAsync( - [ - new TextContentState { UniqueContentId = id, State = TextState.Deleted } - ]); - - var actual = await _.State.GetAsync(HashSet.Of(id)); - - Assert.Empty(actual); - } - - [Fact] - public async Task Should_remove_by_app() + protected override async Task CreateSutAsync(IContentRepository contentRepository) { - var appId1 = DomainId.NewGuid(); - var appId2 = DomainId.NewGuid(); - var app2 = new App { Id = appId2 }; - - var id1 = new UniqueContentId(appId1, DomainId.NewGuid()); - var id2 = new UniqueContentId(appId1, DomainId.NewGuid()); - var id3 = new UniqueContentId(appId2, DomainId.NewGuid()); - - await _.State.SetAsync( - [ - new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, - new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, - new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None } - ]); - - await ((IDeleter)_.State).DeleteAppAsync(app2, default); - - var actual = await _.State.GetAsync(HashSet.Of(id1, id2, id3)); - - actual.Should().BeEquivalentTo(new Dictionary - { - [id3] = new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None } - }); - } - - [Fact] - public async Task Should_remove_by_schema() - { - var appId = DomainId.NewGuid(); - var app = new App { Id = appId }; - - var schemaId = DomainId.NewGuid(); - var schema = new Schema { Id = schemaId }; - - var id1 = new UniqueContentId(appId, DomainId.NewGuid()); - var id2 = new UniqueContentId(appId, DomainId.NewGuid()); - var id3 = new UniqueContentId(appId, DomainId.NewGuid()); - - await _.State.SetAsync( - [ - new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, - new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, - new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None } - ]); - - A.CallTo(() => _.ContentRepository.StreamIds(appId, schemaId, SearchScope.All, default)) - .Returns(new[] { id1.ContentId, id2.ContentId }.ToAsyncEnumerable()); - - await ((IDeleter)_.State).DeleteSchemaAsync(app, schema, default); - - var actual = await _.State.GetAsync(HashSet.Of(id1, id2, id3)); + var sut = new MongoTextIndexerState(fixture.Database, contentRepository); - actual.Should().BeEquivalentTo(new Dictionary - { - [id3] = new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None } - }); + await sut.InitializeAsync(default); + return sut; } } diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/TokenizerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/TokenizerTests.cs index e9d40f20d..d31ab590a 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/TokenizerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Contents/TokenizerTests.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Entities.Text; +using Squidex.Domain.Apps.Entities.Contents.Text; namespace Squidex.MongoDb.Domain.Contents; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/History/MongoHistoryEventRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/History/MongoHistoryEventRepositoryTests.cs new file mode 100644 index 000000000..932a57706 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/History/MongoHistoryEventRepositoryTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.History; +using Squidex.Domain.Apps.Entities.History.Repositories; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.History; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoHistoryEventRepositoryTests(MongoFixture fixture) : HistoryEventRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoHistoryEventRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Rules/MongoRuleRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Rules/MongoRuleRepositoryTests.cs new file mode 100644 index 000000000..743e8faa0 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Rules/MongoRuleRepositoryTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Rules; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoRuleRepositoryTests(MongoFixture fixture) : RuleRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoRuleRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemaRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemaRepositoryTests.cs new file mode 100644 index 000000000..f4a07cb26 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemaRepositoryTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Repositories; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Schemas; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoSchemaRepositoryTests(MongoFixture fixture) : SchemaRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoSchemaRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemasHashTests.cs similarity index 52% rename from backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashTests.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemasHashTests.cs index 2883ddaaf..6efb0cea7 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/MongoSchemasHashTests.cs @@ -9,37 +9,29 @@ using NodaTime; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; - -#pragma warning disable SA1300 // Element should begin with upper-case letter +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Domain.Schemas; -[Trait("Category", "Dependencies")] -public class SchemasHashTests : GivenContext, IClassFixture +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoSchemasHashTests(MongoFixture fixture) : GivenContext, IAsyncLifetime { - public SchemasHashFixture _ { get; } + private readonly MongoSchemasHash sut = new MongoSchemasHash(fixture.Database); - public SchemasHashTests(SchemasHashFixture fixture) + public async Task InitializeAsync() { - _ = fixture; + await sut.InitializeAsync(default); } - [Fact] - public async Task Should_compute_cache_independent_from_order() + public Task DisposeAsync() { - var schema1 = Schema.WithId(DomainId.NewGuid(), "my-schema"); - var schema2 = Schema.WithId(DomainId.NewGuid(), "my-schema") with { Version = 3 }; - - var hash1 = await _.SchemasHash.ComputeHashAsync(App, [schema1, schema2], CancellationToken); - var hash2 = await _.SchemasHash.ComputeHashAsync(App, [schema2, schema1], CancellationToken); - - Assert.NotNull(hash1); - Assert.NotNull(hash2); - Assert.Equal(hash1, hash2); + return Task.CompletedTask; } [Fact] @@ -50,26 +42,24 @@ public class SchemasHashTests : GivenContext, IClassFixture var timestamp = SystemClock.Instance.GetCurrentInstant().WithoutMs(); - var computedHash = await _.SchemasHash.ComputeHashAsync(App, [schema1, schema2], CancellationToken); - - await _.SchemasHash.On( + await sut.On( [ Envelope.Create(new SchemaCreated { AppId = AppId, - SchemaId = schema1.NamedId() + SchemaId = schema1.NamedId(), }).SetEventStreamNumber(schema1.Version).SetTimestamp(timestamp), Envelope.Create(new SchemaCreated { AppId = AppId, - SchemaId = schema2.NamedId() - }).SetEventStreamNumber(schema2.Version).SetTimestamp(timestamp) + SchemaId = schema2.NamedId(), + }).SetEventStreamNumber(schema2.Version).SetTimestamp(timestamp), ]); - var (dbTime, dbHash) = await _.SchemasHash.GetCurrentHashAsync(App, CancellationToken); + var hashKey = await sut.GetCurrentHashAsync(App, CancellationToken); - Assert.Equal(dbHash, computedHash); - Assert.Equal(dbTime, timestamp); + Assert.NotEmpty(hashKey); + Assert.Equal(hashKey.Timestamp, timestamp); } } diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashFixture.cs deleted file mode 100644 index 5d2fdb376..000000000 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Schemas/SchemasHashFixture.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.Domain.Apps.Entities.Schemas; -using Squidex.Domain.Apps.Entities.TestHelpers; -using Squidex.Infrastructure; -using Squidex.TestHelpers; - -namespace Squidex.MongoDb.Domain.Schemas; - -public sealed class SchemasHashFixture -{ - public MongoSchemasHash SchemasHash { get; } - - public SchemasHashFixture() - { - MongoTestUtils.SetupBson(); - - var mongoClient = MongoClientFactory.Create(TestConfig.Configuration["mongoDb:configuration"]); - var mongoDatabase = mongoClient.GetDatabase(TestConfig.Configuration["mongodb:database"]); - - var schemasHash = new MongoSchemasHash(mongoDatabase); - - Task.Run(async () => - { - await schemasHash.InitializeAsync(default); - }).Wait(); - - SchemasHash = schemasHash; - } -} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Teams/MongoTeamRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Teams/MongoTeamRepositoryTests.cs new file mode 100644 index 000000000..a6d27a3f6 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Domain/Teams/MongoTeamRepositoryTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Teams; +using Squidex.Domain.Apps.Entities.Teams.Repositories; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Domain.Teams; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoTeamRepositoryTests(MongoFixture fixture) : TeamRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoTeamRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/BsonJsonSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/BsonJsonSerializerTests.cs index 01a4acce6..1e294af5a 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/BsonJsonSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/BsonJsonSerializerTests.cs @@ -8,7 +8,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Infrastructure; @@ -104,7 +104,7 @@ public class BsonJsonSerializerTests UInt64 = 164, UInt32 = 132, UInt16 = 116, - Uri = new Uri("http://squidex.io") + Uri = new Uri("http://squidex.io"), }; if (nested) @@ -122,7 +122,7 @@ public class BsonJsonSerializerTests { var source = new TestWrapper { - Value = TestObject.CreateWithValues() + Value = TestObject.CreateWithValues(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -135,7 +135,7 @@ public class BsonJsonSerializerTests { var source = new TestWrapperDocument { - Value = TestObject.CreateWithValues() + Value = TestObject.CreateWithValues(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -148,7 +148,7 @@ public class BsonJsonSerializerTests { var source = new TestWrapperString { - Value = TestObject.CreateWithValues() + Value = TestObject.CreateWithValues(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -161,7 +161,7 @@ public class BsonJsonSerializerTests { var source = new TestWrapperBinary { - Value = TestObject.CreateWithValues() + Value = TestObject.CreateWithValues(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -176,8 +176,8 @@ public class BsonJsonSerializerTests { Value = new Dictionary { - ["$key"] = 12 - } + ["$key"] = 12, + }, }; var deserialized = source.SerializeAndDeserializeBson(); @@ -192,8 +192,8 @@ public class BsonJsonSerializerTests { Value = new Dictionary { - ["type.of.value"] = 12 - } + ["type.of.value"] = 12, + }, }; var deserialized = source.SerializeAndDeserializeBson(); @@ -208,8 +208,8 @@ public class BsonJsonSerializerTests { Value = new Dictionary { - [string.Empty] = 12 - } + [string.Empty] = 12, + }, }; var deserialized = source.SerializeAndDeserializeBson(); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Caching/MongoDistributedCacheTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Caching/MongoDistributedCacheTests.cs new file mode 100644 index 000000000..0f6c0248c --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Caching/MongoDistributedCacheTests.cs @@ -0,0 +1,26 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Caching.Distributed; +using Squidex.Infrastructure.Caching; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Infrastructure.Caching; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoDistributedCacheTests(MongoFixture fixture) : DistributedCacheTests +{ + protected override async Task CreateSutAsync(TimeProvider timeProvider) + { + var sut = new MongoDistributedCache(fixture.Database, timeProvider); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/DomainIdSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/DomainIdSerializerTests.cs index 93330372d..1e11f6e69 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/DomainIdSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/DomainIdSerializerTests.cs @@ -8,7 +8,7 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Infrastructure; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventConsumerProcessorIntegrationTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventConsumerProcessorIntegrationTests.cs index 87cd85244..b0dfbbf5e 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventConsumerProcessorIntegrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventConsumerProcessorIntegrationTests.cs @@ -126,7 +126,7 @@ public abstract class EventConsumerProcessorIntegrationTests { await persistence.WriteEventsAsync( [ - Envelope.Create(new MyEvent()) + Envelope.Create(new MyEvent()), ]); } }); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventConsumerProcessorIntegrationTests_Direct.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventConsumerProcessorIntegrationTests_Direct.cs similarity index 93% rename from backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventConsumerProcessorIntegrationTests_Direct.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventConsumerProcessorIntegrationTests_Direct.cs index e6d8f46b2..6f9b9e984 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventConsumerProcessorIntegrationTests_Direct.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventConsumerProcessorIntegrationTests_Direct.cs @@ -9,7 +9,7 @@ using Squidex.Events; -namespace Squidex.MongoDb.Infrastructure; +namespace Squidex.MongoDb.Infrastructure.EventSourcing; [Trait("Category", "Dependencies")] public class MongoEventConsumerProcessorIntegrationTests_Direct(MongoEventStoreFixture_Direct fixture) : EventConsumerProcessorIntegrationTests, IClassFixture diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreFixture.cs similarity index 97% rename from backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreFixture.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreFixture.cs index 50514ffc1..30051733b 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreFixture.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreFixture.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure.TestHelpers; #pragma warning disable MA0048 // File name must match type name -namespace Squidex.MongoDb.Infrastructure; +namespace Squidex.MongoDb.Infrastructure.EventSourcing; public sealed class MongoEventStoreFixture_Direct : MongoEventStoreFixture { diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreParallelInsertTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreParallelInsertTests.cs similarity index 97% rename from backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreParallelInsertTests.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreParallelInsertTests.cs index b683cd334..fb6d9a46d 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoEventStoreParallelInsertTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/EventSourcing/MongoEventStoreParallelInsertTests.cs @@ -15,10 +15,10 @@ using Squidex.Infrastructure.TestHelpers; #pragma warning disable SA1300 // Element should begin with upper-case letter -namespace Squidex.MongoDb.Infrastructure; +namespace Squidex.MongoDb.Infrastructure.EventSourcing; [Trait("Category", "Dependencies")] -public sealed class MongoEventStoreParallelInsertTests : IClassFixture +public class MongoEventStoreParallelInsertTests : IClassFixture { private readonly TestState state = new TestState(DomainId.Empty); private readonly DefaultEventFormatter eventFormatter; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/InstantSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/InstantSerializerTests.cs index 04930a482..5f7ee726a 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/InstantSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/InstantSerializerTests.cs @@ -7,7 +7,7 @@ using NodaTime; using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Infrastructure; @@ -18,7 +18,7 @@ public class InstantSerializerTests { var source = new BsonAsDefaultEntity { - Value = GetTime() + Value = GetTime(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -31,7 +31,7 @@ public class InstantSerializerTests { var source = new BsonAsStringEntity { - Value = GetTime() + Value = GetTime(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -44,7 +44,7 @@ public class InstantSerializerTests { var source = new BsonAsInt64Entity { - Value = GetTime() + Value = GetTime(), }; var deserialized = source.SerializeAndDeserializeBson(); @@ -57,7 +57,7 @@ public class InstantSerializerTests { var source = new BsonAsDateTimeEntity { - Value = GetTime() + Value = GetTime(), }; var deserialized = source.SerializeAndDeserializeBson(); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/JsonValueSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/JsonValueSerializerTests.cs index 043ca311e..b538a1f1b 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/JsonValueSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/JsonValueSerializerTests.cs @@ -6,7 +6,7 @@ // ========================================================================== using Squidex.Infrastructure.Json.Objects; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Infrastructure; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Log/MongoRequestLogRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Log/MongoRequestLogRepositoryTests.cs new file mode 100644 index 000000000..01a42e317 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Log/MongoRequestLogRepositoryTests.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Options; +using Squidex.Infrastructure.Log; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Infrastructure.Log; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoRequestLogRepositoryTests(MongoFixture fixture) : RequestLogRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = + new MongoRequestLogRepository(fixture.Database, + Options.Create(new RequestLogStoreOptions())); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Migrations/MongoMigrationStatusTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Migrations/MongoMigrationStatusTests.cs new file mode 100644 index 000000000..f164e787c --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Migrations/MongoMigrationStatusTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Migrations; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Infrastructure.Migrations; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoMigrationStatusTests(MongoFixture fixture) : MigrationStatusTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoMigrationStatus(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/NamedIdTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/NamedIdTests.cs index 574d5cf09..489ed4a56 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/NamedIdTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/NamedIdTests.cs @@ -6,7 +6,7 @@ // ========================================================================== using Squidex.Infrastructure; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; namespace Squidex.MongoDb.Infrastructure; diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoQueryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Queries/MongoQueryTests.cs similarity index 99% rename from backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoQueryTests.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Queries/MongoQueryTests.cs index 0dd5c25c8..46547516a 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/MongoQueryTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/Queries/MongoQueryTests.cs @@ -11,10 +11,10 @@ using NodaTime; using NodaTime.Text; using Squidex.Infrastructure; using Squidex.Infrastructure.Queries; -using Squidex.TestHelpers; +using Squidex.MongoDb.TestHelpers; using ClrFilter = Squidex.Infrastructure.Queries.ClrFilter; -namespace Squidex.MongoDb.Infrastructure; +namespace Squidex.MongoDb.Infrastructure.Queries; public class MongoQueryTests { diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/States/MongoSnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/States/MongoSnapshotStoreTests.cs new file mode 100644 index 000000000..f10b2d320 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/States/MongoSnapshotStoreTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.States; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Infrastructure.States; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoSnapshotStoreTests(MongoFixture fixture) : SnapshotStoreTests +{ + protected override async Task> CreateSutAsync() + { + var sut = new MongoSnapshotStore(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/TypeConverterStringSerializerTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/TypeConverterStringSerializerTests.cs index 777c27289..1cc4d84b8 100644 --- a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/TypeConverterStringSerializerTests.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/TypeConverterStringSerializerTests.cs @@ -29,7 +29,7 @@ public class TypeConverterStringSerializerTests { var source = new ValueHolder { - Value = TimeSpan.Zero + Value = TimeSpan.Zero, }; var deserialized = SerializeAndDeserializeBson(source); @@ -42,7 +42,7 @@ public class TypeConverterStringSerializerTests { var source = new ValueHolder { - Value = TimeSpan.Zero + Value = TimeSpan.Zero, }; var deserialized = SerializeAndDeserializeBson(source); @@ -55,7 +55,7 @@ public class TypeConverterStringSerializerTests { var source = new ValueHolder { - Value = null + Value = null, }; var deserialized = SerializeAndDeserializeBson(source); @@ -68,7 +68,7 @@ public class TypeConverterStringSerializerTests { var source = new ValueHolder { - Value = RefToken.Client("client") + Value = RefToken.Client("client"), }; var deserialized = SerializeAndDeserializeBson(source); @@ -81,7 +81,7 @@ public class TypeConverterStringSerializerTests { var source = new ValueHolder { - Value = null + Value = null, }; var deserialized = SerializeAndDeserializeBson(source); diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/UsageTracking/MongoUsageRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/UsageTracking/MongoUsageRepositoryTests.cs new file mode 100644 index 000000000..30d93f679 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/Infrastructure/UsageTracking/MongoUsageRepositoryTests.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.UsageTracking; +using Squidex.MongoDb.TestHelpers; +using Squidex.Shared; + +namespace Squidex.MongoDb.Infrastructure.UsageTracking; + +[Trait("Category", "TestContainer")] +[Collection("Mongo")] +public class MongoUsageRepositoryTests(MongoFixture fixture) : UsageRepositoryTests +{ + protected override async Task CreateSutAsync() + { + var sut = new MongoUsageRepository(fixture.Database); + + await sut.InitializeAsync(default); + return sut; + } +} diff --git a/backend/tests/Squidex.Data.Tests/TestHelpers/Entities.cs b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/Entities.cs similarity index 96% rename from backend/tests/Squidex.Data.Tests/TestHelpers/Entities.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/Entities.cs index 9cf60f57d..ad35c7eee 100644 --- a/backend/tests/Squidex.Data.Tests/TestHelpers/Entities.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/Entities.cs @@ -10,7 +10,7 @@ using MongoDB.Bson.Serialization.Attributes; #pragma warning disable MA0048 // File name must match type name -namespace Squidex.TestHelpers; +namespace Squidex.MongoDb.TestHelpers; public sealed class BsonAsDateTimeEntity { diff --git a/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoFixture.cs b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoFixture.cs new file mode 100644 index 000000000..f86ac5c69 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoFixture.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using MongoDB.Driver; +using Testcontainers.MongoDb; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.MongoDb.TestHelpers; + +[CollectionDefinition("Mongo")] +public sealed class MongoFixtureCollection : ICollectionFixture +{ +} + +public class MongoFixture : IAsyncLifetime +{ + private readonly MongoDbContainer mongoDb = + new MongoDbBuilder() + .WithReuse(false) + .WithLabel("reuse-id", "squidex-mongodb") + .Build(); + + public IMongoClient Client { get; private set; } + + public IMongoDatabase Database => Client.GetDatabase("Test"); + + public MongoFixture() + { + MongoTestUtils.SetupBson(); + } + + public async Task InitializeAsync() + { + await mongoDb.StartAsync(); + + Client = new MongoClient(mongoDb.GetConnectionString()); + } + + public async Task DisposeAsync() + { + await mongoDb.StopAsync(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/TestHelpers/MongoTestUtils.cs b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoTestUtils.cs similarity index 92% rename from backend/tests/Squidex.Data.Tests/TestHelpers/MongoTestUtils.cs rename to backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoTestUtils.cs index 2656495b5..e879893ce 100644 --- a/backend/tests/Squidex.Data.Tests/TestHelpers/MongoTestUtils.cs +++ b/backend/tests/Squidex.Data.Tests/MongoDb/TestHelpers/MongoTestUtils.cs @@ -8,11 +8,11 @@ using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; +using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; -using Squidex.Infrastructure.TestHelpers; -namespace Squidex.TestHelpers; +namespace Squidex.MongoDb.TestHelpers; public static class MongoTestUtils { @@ -43,7 +43,7 @@ public static class MongoTestUtils public static T SerializeAndDeserializeBson(this T value) { - return SerializeAndDeserializeBson(value); + return value.SerializeAndDeserializeBson(); } public static TOut SerializeAndDeserializeBson(this TIn value) diff --git a/backend/tests/Squidex.Data.Tests/Shared/AppRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/AppRepositoryTests.cs new file mode 100644 index 000000000..91c7c1235 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/AppRepositoryTests.cs @@ -0,0 +1,178 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities.Apps.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Shared; + +public abstract class AppRepositoryTests +{ + private static readonly DomainId KnownId = DomainId.Create("3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"); + + protected abstract Task CreateSutAsync(); + + protected virtual async Task PrepareAsync(IAppRepository sut, App[] apps) + { + if (sut is not ISnapshotStore store) + { + return; + } + + var writes = apps.Select(x => new SnapshotWriteJob(x.Id, x, 0)); + + await store.WriteManyAsync(writes); + } + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if (await sut.FindAsync(KnownId) != null) + { + return sut; + } + + var created = SystemClock.Instance.GetCurrentInstant(); + var createdBy = RefToken.Client("client1"); + + var defaultNotDeleted = new App + { + Id = KnownId, + Name = "default", + Description = "default-not-deleted", + IsDeleted = false, + Created = created, + CreatedBy = createdBy, + }; + + var defaultDeleted = new App + { + Id = DomainId.NewGuid(), + Name = "default", + Description = "default-deleted", + IsDeleted = true, + Created = created, + CreatedBy = createdBy, + }; + + var duplicateName1 = new App + { + Id = DomainId.NewGuid(), + Name = "with-duplicate", + Description = "duplicate-old", + Created = created.Minus(Duration.FromHours(1)), + CreatedBy = createdBy, + }; + + var duplicateName2 = new App + { + Id = DomainId.NewGuid(), + Name = "with-duplicate", + Description = "duplicate-new", + Created = created, + CreatedBy = createdBy, + }; + + var byTeam = new App + { + Id = DomainId.NewGuid(), + Name = "by-team", + Description = "by-team", + TeamId = DomainId.Create(Guid.Empty), + Created = created, + CreatedBy = createdBy, + }; + + var byContributors = new App + { + Id = DomainId.NewGuid(), + Name = "by-contributor", + Description = "by-contributor", + Contributors = Contributors.Empty.Assign("1", Role.Owner).Assign("2", Role.Owner), + Created = created, + CreatedBy = createdBy, + }; + + await PrepareAsync(sut, [ + defaultDeleted, + defaultNotDeleted, + duplicateName1, + duplicateName2, + byTeam, + byContributors, + ]); + + return sut; + } + + [Fact] + public async Task Should_query_by_team() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryAllAsync(DomainId.Create(Guid.Empty)); + + Assert.Equal("by-team", result.Single().Description); + } + + [Theory] + [InlineData("1")] + [InlineData("2")] + public async Task Should_query_by_contributor(string id) + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryAllAsync(id, []); + + Assert.Equal("by-contributor", result.Single().Description); + } + + [Fact] + public async Task Should_query_by_duplicate_name() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryAllAsync(string.Empty, ["with-duplicate"]); + + Assert.Equal("duplicate-new", result.Single().Description); + } + + [Fact] + public async Task Should_find_by_duplicate_name() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.FindAsync("with-duplicate"); + + Assert.Equal("duplicate-new", result?.Description); + } + + [Fact] + public async Task Should_find_by_name() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.FindAsync("default"); + + Assert.Equal("default-not-deleted", result?.Description); + } + + [Fact] + public async Task Should_find_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.FindAsync(KnownId); + + Assert.Equal("default-not-deleted", result?.Description); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/AssetFolderRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/AssetFolderRepositoryTests.cs new file mode 100644 index 000000000..c9661569b --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/AssetFolderRepositoryTests.cs @@ -0,0 +1,138 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable xUnit1044 // Avoid using TheoryData type arguments that are not serializable +#pragma warning disable MA0040 // Forward the CancellationToken parameter to methods that take one + +namespace Squidex.Shared; + +public abstract class AssetFolderRepositoryTests : GivenContext +{ + private const int NumValues = 250; + private static readonly DomainId ParentId = DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d451"); + private static readonly NamedId[] AppIds = + [ + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d453"), "my-app1"), + ]; + + private readonly DomainId appId; + + protected AssetFolderRepositoryTests() + { + appId = AppIds[Random.Shared.Next(AppIds.Length)].Id; + } + + protected abstract Task CreateSutAsync(); + + protected async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if ((await sut.QueryAsync(AppIds[0].Id, null)).Count > 0) + { + return sut; + } + + if (sut is not ISnapshotStore store) + { + return sut; + } + + var batch = new List>(); + + async Task ExecuteBatchAsync(AssetFolder? entity) + { + if (entity != null) + { + batch.Add(new SnapshotWriteJob(entity.UniqueId, entity, 0)); + } + + if ((entity == null || batch.Count >= 1000) && batch.Count > 0) + { + await store.WriteManyAsync(batch); + batch.Clear(); + } + } + + foreach (var forAppId in AppIds) + { + for (var i = 0; i < NumValues; i++) + { + var fileName = i.ToString(CultureInfo.InvariantCulture); + + var assetFolder = CreateAssetFolder() with + { + AppId = forAppId, + FolderName = fileName, + }; + + await ExecuteBatchAsync(assetFolder); + } + + var byParent = CreateAssetFolder() with + { + AppId = forAppId, + ParentId = ParentId, + FolderName = "0", + }; + + await ExecuteBatchAsync(byParent); + } + + await ExecuteBatchAsync(null); + return sut; + } + + public static readonly TheoryData ParentIds = new TheoryData + { + { null }, + { DomainId.Empty }, + }; + + [Fact] + public async Task Should_find_asset_folder_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var assetFolder1 = (await sut.QueryAsync(appId, null))[0]; + var assetFolder2 = await sut.FindAssetFolderAsync(appId, assetFolder1.Id); + + // The Slug is random here, as it does not really matter. + Assert.NotNull(assetFolder2); + } + + [Fact] + public async Task Should_query_child_ids() + { + var sut = await CreateAndPrepareSutAsync(); + + var assets = await sut.QueryChildIdsAsync(appId, ParentId); + + // No pagination is going on here. + Assert.Single(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets(DomainId? parentId) + { + var sut = await CreateAndPrepareSutAsync(); + + var assets = await sut.QueryAsync(appId, parentId); + + // Default page size is unlimited. + Assert.True(assets.Count >= NumValues); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/AssetFolderSnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/Shared/AssetFolderSnapshotStoreTests.cs new file mode 100644 index 000000000..a260764be --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/AssetFolderSnapshotStoreTests.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Shared; + +public abstract class AssetFolderSnapshotStoreTests : SnapshotStoreTests +{ + protected override AssetFolder CreateEntity(DomainId id, int version) + { + var context = new GivenContext(); + + return Cleanup(context.CreateAssetFolder() with { Id = id, Version = version }); + } + + protected override AssetFolder Cleanup(AssetFolder expected) + { + return SimpleMapper.Map(expected, new AssetFolder()); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/AssetRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/AssetRepositoryTests.cs new file mode 100644 index 000000000..1320fb8d7 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/AssetRepositoryTests.cs @@ -0,0 +1,368 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Globalization; +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; +using Squidex.Infrastructure.Queries; +using Squidex.Infrastructure.States; + +#pragma warning disable xUnit1044 // Avoid using TheoryData type arguments that are not serializable +#pragma warning disable MA0040 // Forward the CancellationToken parameter to methods that take one + +namespace Squidex.Shared; + +public abstract class AssetRepositoryTests : GivenContext +{ + private const int NumValues = 250; + private static readonly DomainId ParentId = DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d451"); + private static readonly NamedId[] AppIds = + [ + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d453"), "my-app2"), + ]; + + private readonly string randomValue = Random.Shared.Next(NumValues).ToString(CultureInfo.InvariantCulture); + private readonly DomainId appId; + + protected AssetRepositoryTests() + { + appId = AppIds[Random.Shared.Next(AppIds.Length)].Id; + } + + protected abstract Task CreateSutAsync(); + + protected async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + if (sut is not ISnapshotStore store) + { + return sut; + } + + if (await sut.StreamAll(AppIds[0].Id).AnyAsync()) + { + return sut; + } + + var batch = new List>(); + + async Task ExecuteBatchAsync(Asset? entity) + { + if (entity != null) + { + batch.Add(new SnapshotWriteJob(entity.UniqueId, entity, 0)); + } + + if ((entity == null || batch.Count >= 1000) && batch.Count > 0) + { + await store.WriteManyAsync(batch); + batch.Clear(); + } + } + + foreach (var forAppId in AppIds) + { + for (var i = 0; i < NumValues; i++) + { + var fileName = i.ToString(CultureInfo.InvariantCulture); + + for (var j = 0; j < NumValues; j++) + { + var asset = CreateAsset() with + { + AppId = forAppId, + FileHash = fileName, + FileName = fileName, + Metadata = new AssetMetadata + { + ["value"] = JsonValue.Create($"value_{j}"), + }, + Tags = + [ + $"tag_{j}", + ], + Slug = fileName, + }; + + await ExecuteBatchAsync(asset); + } + } + + var byParent = CreateAsset() with + { + AppId = forAppId, + ParentId = ParentId, + FileHash = "0", + FileName = "0", + Metadata = new AssetMetadata + { + ["value"] = JsonValue.Create("0"), + }, + Tags = + [ + "tag_0", + ], + Slug = "0", + }; + + await ExecuteBatchAsync(byParent); + } + + await ExecuteBatchAsync(null); + return sut; + } + + public static readonly TheoryData ParentIds = new TheoryData + { + { null }, + { DomainId.Empty }, + }; + + [Fact] + public async Task Should_find_asset_by_id_only() + { + var sut = await CreateAndPrepareSutAsync(); + + var asset1 = await sut.StreamAll(appId).FirstAsync(); + var asset2 = await sut.FindAssetAsync(asset1.Id); + + // The Slug is random here, as it does not really matter. + Assert.NotNull(asset2); + } + + [Fact] + public async Task Should_find_asset_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var asset1 = await sut.StreamAll(appId).FirstAsync(); + var asset2 = await sut.FindAssetAsync(appId, asset1.Id, false); + + // The ID is random here, as it does not really matter. + Assert.NotNull(asset2); + } + + [Fact] + public async Task Should_find_asset_by_hash() + { + var sut = await CreateAndPrepareSutAsync(); + + var asset = await sut.FindAssetByHashAsync(appId, randomValue, randomValue, 1024); + + // The Hash is random here, as it does not really matter. + Assert.NotNull(asset); + } + + [Fact] + public async Task Should_find_asset_by_slug() + { + var sut = await CreateAndPrepareSutAsync(); + + var asset = await sut.FindAssetBySlugAsync(appId, randomValue, false); + + // The Slug is random here, as it does not really matter. + Assert.NotNull(asset); + } + + [Fact] + public async Task Should_query_ids() + { + var sut = await CreateAndPrepareSutAsync(); + + var assetIds = await sut.StreamAll(appId).Take(100).Select(x => x.Id).ToHashSetAsync(); + var assets = await sut.QueryIdsAsync(appId, assetIds); + + // The IDs are valid. + Assert.Equal(assetIds.Count, assets.Count); + } + + [Fact] + public async Task Should_query_child_ids() + { + var sut = await CreateAndPrepareSutAsync(); + + var assets = await sut.QueryChildIdsAsync(appId, ParentId); + + // No pagination is going on here. + Assert.Single(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets(DomainId? parentId) + { + var query = new ClrQuery(); + + var assets = await QueryAsync(parentId, query); + + // Default page size is 1000. + Assert.Equal(1000, assets.Count); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_with_random_count(DomainId? parentId) + { + var query = new ClrQuery + { + Random = 40, + }; + + var assets = await QueryAsync(parentId, query); + + // Default page size is 1000, so we expect less elements. + Assert.Equal(40, assets.Count); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_tags(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = ClrFilter.Eq("tags", $"tag_{randomValue}"), + }; + + var assets = await QueryAsync(parentId, query); + + // The tag is random here, as it does not really matter. + Assert.NotNull(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_tags_in_query(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = ClrFilter.In("tags", new List { randomValue, "other" }), + }; + + var assets = await QueryAsync(parentId, query); + + // The tag is random here, as it does not really matter. + Assert.NotNull(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_tags_and_fileName(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = + ClrFilter.And( + ClrFilter.Eq("tags", $"tag_{randomValue}"), + ClrFilter.Contains("fileName", randomValue)), + }; + + var assets = await QueryAsync(parentId, query); + + // The filter is a random value from the expected result set. + Assert.NotEmpty(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_fileName(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = ClrFilter.Contains("fileName", randomValue), + }; + + var assets = await QueryAsync(parentId, query); + + // The filter is a random value from the expected result set. + Assert.NotEmpty(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_metadata(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = ClrFilter.Contains("metadata.value", $"value_{randomValue}"), + }; + + var assets = await QueryAsync(parentId, query); + + // The filter is a random value from the expected result set. + Assert.NotEmpty(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_fileName_and_tags(DomainId? parentId) + { + var query = new ClrQuery + { + Filter = + ClrFilter.And( + ClrFilter.Contains("fileName", randomValue), + ClrFilter.Eq("tags", $"tag_{randomValue}")), + }; + + var assets = await QueryAsync(parentId, query); + + // The filter is a random value from the expected result set. + Assert.NotEmpty(assets); + } + + [Theory] + [MemberData(nameof(ParentIds))] + public async Task Should_query_assets_by_ids(DomainId? parentId) + { + var sut = await CreateAndPrepareSutAsync(); + + var q = + Q.Empty + .WithIds(await sut.StreamAll(appId).Take(100).Select(x => x.Id).ToHashSetAsync()); + + var assets = await sut.QueryAsync(appId, parentId, q); + + // The filter is a random value from the expected result set. + Assert.NotEmpty(assets); + } + + private async Task> QueryAsync(DomainId? parentId, + ClrQuery clrQuery, + int top = 1000, + int skip = 0) + { + var sut = await CreateAndPrepareSutAsync(); + + clrQuery.Top = top; + clrQuery.Skip = skip; + clrQuery.Sort ??= []; + + if (clrQuery.Sort.Count == 0) + { + clrQuery.Sort.Add(new SortNode("lastModified", SortOrder.Descending)); + } + + if (!clrQuery.Sort.Exists(x => x.Path.Equals("id"))) + { + clrQuery.Sort.Add(new SortNode("id", SortOrder.Ascending)); + } + + var q = + Q.Empty + .WithoutTotal() + .WithQuery(clrQuery); + + var assets = await sut.QueryAsync(appId, parentId, q); + + return assets; + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/AssetSnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/Shared/AssetSnapshotStoreTests.cs new file mode 100644 index 000000000..96cd62d22 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/AssetSnapshotStoreTests.cs @@ -0,0 +1,28 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Assets; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Shared; + +public abstract class AssetSnapshotStoreTests : SnapshotStoreTests +{ + public GivenContext Context { get; } = new GivenContext(); + + protected override Asset CreateEntity(DomainId id, int version) + { + return Cleanup(Context.CreateAsset() with { Id = id, Version = version }); + } + + protected override Asset Cleanup(Asset expected) + { + return SimpleMapper.Map(expected, new Asset()); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/ContentRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/ContentRepositoryTests.cs new file mode 100644 index 000000000..a18b19dec --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/ContentRepositoryTests.cs @@ -0,0 +1,422 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using LoremNET; +using NodaTime; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; +using Squidex.Infrastructure.Queries; +using Squidex.Infrastructure.States; + +#pragma warning disable xUnit1044 // Avoid using TheoryData type arguments that are not serializable +#pragma warning disable MA0040 // Forward the CancellationToken parameter to methods that take one + +namespace Squidex.Shared; + +public abstract class ContentRepositoryTests : GivenContext +{ + private const int NumValues = 50; + private static readonly NamedId[] AppIds = + [ + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d453"), "my-app2"), + ]; + + private static readonly NamedId[] SchemaIds = + [ + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d454"), "my-schema1"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d455"), "my-schema2"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d456"), "my-schema3"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d457"), "my-schema4"), + ]; + + private readonly Instant now = SystemClock.Instance.GetCurrentInstant(); + private readonly App app; + private readonly Schema schema; + + protected ContentRepositoryTests() + { + var appId = AppIds[Random.Shared.Next(AppIds.Length)]; + var appName = appId.Name; + + app = CreateApp(appId.Id, appName); + + var schemaId = SchemaIds[Random.Shared.Next(SchemaIds.Length)]; + var schemaName = schemaId.Name; + + schema = CreateSchema(app, schemaId.Id, schemaName); + + A.CallTo(() => AppProvider.GetSchemaAsync(A._, A._, A._, A._)) + .ReturnsLazily(x => + { + var appId = x.GetArgument(0); + var appFound = CreateApp(appId, "my-app"); + + var schemaId = x.GetArgument(1); + var schemaFound = CreateSchema(appFound, schemaId, "my-schema"); + + return Task.FromResult(schemaFound); + }); + + A.CallTo(() => AppProvider.GetAppWithSchemaAsync(A._, A._, A._, A._)) + .ReturnsLazily(x => + { + var appId = x.GetArgument(0); + var appFound = CreateApp(appId, "my-app"); + + var schemaId = x.GetArgument(1); + var schemaFound = CreateSchema(appFound, schemaId, "my-schema"); + + return Task.FromResult<(App?, Schema?)>((appFound, schemaFound)); + }); + } + + protected abstract Task CreateSutAsync(); + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + if (sut is not ISnapshotStore store) + { + return sut; + } + + if (await sut.StreamAll(AppIds[0].Id, [schema.Id], SearchScope.All).AnyAsync()) + { + return sut; + } + + var batch = new List>(); + + async Task ExecuteBatchAsync(WriteContent? state) + { + if (state != null) + { + batch.Add(new SnapshotWriteJob(state.UniqueId, state, 0)); + } + + if ((state == null || batch.Count >= 1000) && batch.Count > 0) + { + await store.WriteManyAsync(batch, default); + batch.Clear(); + } + } + + foreach (var forAppId in AppIds) + { + foreach (var forSchemaId in SchemaIds) + { + var previousIds = new List(); + + for (var i = 0; i < NumValues; i++) + { + var contentId = DomainId.NewGuid(); + + if (i == 0) + { + previousIds = [contentId]; + } + + var content = CreateWriteContent() with + { + Id = contentId, + CurrentVersion = new ContentVersion( + Status.Published, + new ContentData() + .AddField("field1", + new ContentFieldData() + .AddInvariant(JsonValue.Create(i))) + .AddField("field2", + new ContentFieldData() + .AddInvariant(JsonValue.Create(Lorem.Paragraph(200, 20)))) + .AddField("references", + new ContentFieldData() + .AddInvariant( + JsonValue.Array(previousIds.ToArray())))), + SchemaId = forSchemaId, + ScheduleJob = + i > NumValues / 2 + ? new ScheduleJob(DomainId.NewGuid(), Status.Archived, User, now.Plus(Duration.FromDays(i))) + : null, + AppId = forAppId, + }; + + await ExecuteBatchAsync(content); + } + } + } + + await ExecuteBatchAsync(null); + + return sut; + } + + [Fact] + public async Task Should_stream_all_with_schema() + { + var sut = await CreateAndPrepareSutAsync(); + + var count = await sut.StreamAll(AppIds[0].Id, [schema.Id], SearchScope.All).CountAsync(); + + Assert.Equal(NumValues, count); + } + + [Fact] + public async Task Should_stream_all_without_schema() + { + var sut = await CreateAndPrepareSutAsync(); + + var count = await sut.StreamAll(AppIds[0].Id, null, SearchScope.All).CountAsync(); + + Assert.Equal(NumValues * SchemaIds.Length, count); + } + + [Fact] + public async Task Should_stream_all_with_empty_schemas() + { + var sut = await CreateAndPrepareSutAsync(); + + var count = await sut.StreamAll(AppIds[0].Id, [], SearchScope.All).CountAsync(); + + Assert.Equal(0, count); + } + + [Fact] + public async Task Should_verify_ids() + { + var sut = await CreateAndPrepareSutAsync(); + + var contentIds = await sut.StreamAll(app.Id, [schema.Id], default).Select(x => x.Id).ToHashSetAsync(); + var contents = await sut.QueryIdsAsync(app, contentIds, SearchScope.Published); + + // The IDs are valid. + Assert.Equal(contents.Count, contentIds.Count); + } + + [Fact] + public async Task Should_query_by_ids() + { + var sut = await CreateAndPrepareSutAsync(); + + var contentIds = await sut.StreamAll(app.Id, [schema.Id], default).Select(x => x.Id).ToHashSetAsync(); + var contents = await sut.QueryAsync(app, [schema], Q.Empty.WithIds(contentIds), SearchScope.All); + + // The IDs are valid. + Assert.Equal(contents.Count, contentIds.Count); + } + + [Fact] + public async Task Should_query_by_ids_and_schema() + { + var sut = await CreateAndPrepareSutAsync(); + + var contentIds = await sut.StreamAll(app.Id, [schema.Id], default).Select(x => x.Id).ToHashSetAsync(); + var contents = await sut.QueryAsync(app, schema, Q.Empty.WithIds(contentIds), SearchScope.All); + + // The IDs are valid. + Assert.Equal(contents.Count, contentIds.Count); + } + + [Fact] + public async Task Should_query_ids_by_filter() + { + var sut = await CreateAndPrepareSutAsync(); + + var filter = ClrFilter.Eq("data.field1.iv", 12); + + var contents = await sut.QueryIdsAsync(app, schema, filter, SearchScope.All); + + // We have a concrete query, so we expect an actual. + Assert.Single(contents); + } + + [Fact] + public async Task Should_query_with_limit_and_total() + { + var contents = await QueryAsync(new ClrQuery(), 20, 0, withTotal: true); + + // We have a concrete query, so we expect an actual. + Assert.Equal(20, contents.Count); + Assert.Equal(50, contents.Total); + } + + [Fact] + public async Task Should_query_by_filter() + { + var query = new ClrQuery + { + Filter = ClrFilter.Eq("data.field1.iv", 12), + }; + + var contents = await QueryAsync(query, 1000, 0); + + // We have a concrete query, so we expect an actual. + Assert.Single(contents); + } + + [Fact] + public async Task Should_query_scheduled() + { + var sut = await CreateAndPrepareSutAsync(); + + var contents = await sut.StreamScheduledWithoutDataAsync(now.Plus(Duration.FromDays(30)), default).ToListAsync(); + + // The IDs are random here, as it does not really matter. + Assert.NotEmpty(contents); + } + + [Fact] + public async Task Should_query_with_default_query() + { + var query = new ClrQuery(); + + var contents = await QueryAsync(query); + + // We have a concrete query, so we expect an actual result. + Assert.Equal(NumValues, contents.Count); + } + + [Fact] + public async Task Should_query_with_large_skip() + { + var query = new ClrQuery + { + Sort = + [ + new SortNode("data.field1.iv", SortOrder.Ascending), + ], + }; + + var contents = await QueryAsync(query, 1000, 9000); + + // We have a concrete query, so we expect an actual result. + Assert.Empty(contents); + } + + [Fact] + public async Task Should_query_with_query_fulltext() + { + var query = new ClrQuery + { + FullText = "hello", + }; + + var contents = await QueryAsync(query); + + // The full text is resolved by another system, so we cannot verify the actual result. + Assert.NotNull(contents); + } + + [Fact] + public async Task Should_query_with_query_filter() + { + var query = new ClrQuery + { + Filter = ClrFilter.Eq("data.field1.iv", NumValues / 4), + }; + + var contents = await QueryAsync(query, 1000, 0); + + // We have a concrete query, so we expect an actual result. + Assert.NotEmpty(contents); + } + + [Fact] + public async Task Should_query_with_reference() + { + var baseQuery = new ClrQuery + { + Filter = ClrFilter.Eq("data.field1.iv", 0), + }; + + var content = await QueryAsync(baseQuery, 1); + var contents = await QueryAsync(new ClrQuery(), reference: content[0].Id); + + // We do not insert test entities with references, so we cannot verify the actual result. + Assert.Equal(NumValues, contents.Count); + } + + [Fact] + public async Task Should_query_with_referencing() + { + var content = await QueryAsync(new ClrQuery(), 1, NumValues / 2); + var contents = await QueryAsync(new ClrQuery(), 1000, 0, referencing: content[0].Id); + + // We do not insert test entities with references, so we cannot verify the actual result. + Assert.Single(contents); + } + + [Fact] + public async Task Should_query_with_random_count() + { + var query = new ClrQuery + { + Random = 40, + }; + + var contents = await QueryAsync(query); + + // We do not insert test entities with references, so we cannot verify the actual. + Assert.Equal(40, contents.Count); + } + + private async Task> QueryAsync( + ClrQuery clrQuery, + int top = 1000, + int skip = 0, + DomainId reference = default, + DomainId referencing = default, + bool withTotal = false) + { + clrQuery.Take = top; + clrQuery.Skip = skip; + clrQuery.Sort ??= []; + + if (clrQuery.Sort.Count == 0) + { + clrQuery.Sort.Add(new SortNode("lastModified", SortOrder.Descending)); + } + + if (!clrQuery.Sort.Exists(x => x.Path.Equals("id"))) + { + clrQuery.Sort.Add(new SortNode("id", SortOrder.Ascending)); + } + + var q = + Q.Empty + .WithoutTotal(!withTotal) + .WithQuery(clrQuery) + .WithReference(reference) + .WithReferencing(referencing); + + var sut = await CreateAndPrepareSutAsync(); + + return await sut.QueryAsync(app, schema, q, SearchScope.All); + } + + private App CreateApp(DomainId id, string name) + { + var newApp = App with { Id = id, Name = name }; + + return newApp; + } + + private Schema CreateSchema(App app, DomainId id, string name) + { + var newSchema = Schema with { AppId = app.NamedId(), Id = id, Name = name }; + + return newSchema.AddReferences(0, "references", Partitioning.Invariant); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/ContentSnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/Shared/ContentSnapshotStoreTests.cs new file mode 100644 index 000000000..48cb96d04 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/ContentSnapshotStoreTests.cs @@ -0,0 +1,52 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Squidex.Infrastructure.Json.Objects; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Shared; + +public abstract class ContentSnapshotStoreTests : SnapshotStoreTests +{ + public GivenContext Context { get; } = new GivenContext(); + + protected ContentSnapshotStoreTests() + { + Context.Schema = Context.Schema.AddReferences(0, "myReferences", Partitioning.Invariant); + } + + protected override WriteContent CreateEntity(DomainId id, int version) + { + return Cleanup(Context.CreateWriteContent() with + { + Id = id, + CurrentVersion = new ContentVersion(Status.Published, + new ContentData() + .AddField("myString", + new ContentFieldData() + .AddInvariant("Hello Squidex")) + .AddField("myReferences", + new ContentFieldData() + .AddInvariant( + JsonValue.Array( + DomainId.NewGuid(), + DomainId.NewGuid(), + DomainId.NewGuid())))), + Version = version, + }); + } + + protected override WriteContent Cleanup(WriteContent expected) + { + return SimpleMapper.Map(expected, new WriteContent()); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/DistributedCacheTests.cs b/backend/tests/Squidex.Data.Tests/Shared/DistributedCacheTests.cs new file mode 100644 index 000000000..1692d3980 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/DistributedCacheTests.cs @@ -0,0 +1,91 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Extensions.Caching.Distributed; + +namespace Squidex.Shared; + +public abstract class DistributedCacheTests +{ + private readonly TimeProvider timeProvider = A.Fake(); + private DateTimeOffset now = DateTimeOffset.UtcNow; + + protected DistributedCacheTests() + { + A.CallTo(() => timeProvider.GetUtcNow()) + .ReturnsLazily(() => now); + } + + protected abstract Task CreateSutAsync(TimeProvider timeProvider); + + [Fact] + public async Task Should_add_and_get_entry_without_expiration() + { + var sut = await CreateSutAsync(timeProvider); + + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue = cacheKey; + + var options = new DistributedCacheEntryOptions(); + + await sut.SetStringAsync(cacheKey, cacheValue, options); + + var result = await sut.GetStringAsync(cacheKey); + Assert.Equal(cacheKey, result); + } + + [Fact] + public async Task Should_add_and_get_entry() + { + var sut = await CreateSutAsync(timeProvider); + + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue = cacheKey; + + var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; + + await sut.SetStringAsync(cacheKey, cacheValue, options); + + var result = await sut.GetStringAsync(cacheKey); + Assert.Equal(cacheKey, result); + } + + [Fact] + public async Task Should_not_return_result_if_expired() + { + var sut = await CreateSutAsync(timeProvider); + + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue = cacheKey; + + var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; + + await sut.SetStringAsync(cacheKey, cacheValue, options); + + now = now.AddDays(1); + + var result = await sut.GetStringAsync(cacheKey); + Assert.Null(result); + } + + [Fact] + public async Task Should_not_return_result_if_removed() + { + var sut = await CreateSutAsync(timeProvider); + + var cacheKey = Guid.NewGuid().ToString(); + var cacheValue = cacheKey; + + var options = new DistributedCacheEntryOptions(); + + await sut.SetStringAsync(cacheKey, cacheValue, options); + await sut.RemoveAsync(cacheKey); + + var result = await sut.GetStringAsync(cacheKey); + Assert.Null(result); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/HistoryEventRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/HistoryEventRepositoryTests.cs new file mode 100644 index 000000000..bf813a81a --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/HistoryEventRepositoryTests.cs @@ -0,0 +1,164 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.History; +using Squidex.Domain.Apps.Entities.History.Repositories; +using Squidex.Infrastructure; + +namespace Squidex.Shared; + +public abstract class HistoryEventRepositoryTests +{ + private static readonly RefToken Actor = RefToken.Client("client"); + private static readonly DomainId KnownId = DomainId.Create("3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"); + + protected abstract Task CreateSutAsync(); + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if ((await sut.QueryByChannelAsync(KnownId, string.Empty, 10)).Any()) + { + return sut; + } + + var created = SystemClock.Instance.GetCurrentInstant(); + + await sut.InsertManyAsync([ + new HistoryEvent + { + Id = DomainId.NewGuid(), + Actor = Actor, + Channel = "parent1.child1", + Created = created.Plus(Duration.FromHours(10)), + EventType = "type", + OwnerId = KnownId, + }, + new HistoryEvent + { + Id = DomainId.NewGuid(), + Actor = Actor, + Channel = "parent1.child2", + Created = created.Plus(Duration.FromHours(9)), + EventType = "type", + OwnerId = KnownId, + }, + new HistoryEvent + { + Id = DomainId.NewGuid(), + Actor = Actor, + Channel = "channel", + Created = created.Plus(Duration.FromHours(6)), + EventType = "type", + OwnerId = DomainId.NewGuid(), + }, + ]); + + return sut; + } + + [Fact] + public async Task Query_all_async() + { + var result = await QueryAsync(KnownId, string.Empty, 100); + + Assert.Equal(["parent1.child1", "parent1.child2"], result); + } + + [Fact] + public async Task Query_with_limited_count() + { + var result = await QueryAsync(KnownId, string.Empty, 1); + + Assert.Equal(["parent1.child1"], result); + } + + [Fact] + public async Task Query_with_channel() + { + var result = await QueryAsync(KnownId, "parent1.child1", 100); + + Assert.Equal(["parent1.child1"], result); + } + + [Fact] + public async Task Should_replace_on_insert() + { + var sut = await CreateSutAsync(); + + var appId = DomainId.NewGuid(); + + await sut.InsertManyAsync([ + new HistoryEvent + { + Id = appId, + Actor = Actor, + Channel = "channel1", + Created = default, + EventType = "type", + OwnerId = appId, + }, + ]); + + await sut.InsertManyAsync([ + new HistoryEvent + { + Id = appId, + Actor = Actor, + Channel = "channel2", + Created = default, + EventType = "type", + OwnerId = appId, + }, + ]); + + var result = await QueryAsync(appId, string.Empty, 100); + Assert.Equal(["channel2"], result); + } + + [Fact] + public async Task Should_delete_by_app() + { + var sut = await CreateSutAsync(); + if (sut is not IDeleter deleter) + { + return; + } + + var appId = DomainId.NewGuid(); + + await sut.InsertManyAsync([ + new HistoryEvent + { + Id = appId, + Actor = Actor, + Channel = "channel1", + Created = default, + EventType = "type", + OwnerId = appId, + }, + ]); + + await deleter.DeleteAppAsync(new App { Id = appId }, default); + + var result = await QueryAsync(appId, string.Empty, 100); + Assert.Empty(result); + } + + private async Task QueryAsync(DomainId knownId, string prefix, int count) + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryByChannelAsync(knownId, prefix, count); + + return result.Select(x => x.Channel).ToArray(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/MigrationStatusTests.cs b/backend/tests/Squidex.Data.Tests/Shared/MigrationStatusTests.cs new file mode 100644 index 000000000..d0218f9c4 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/MigrationStatusTests.cs @@ -0,0 +1,71 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.Migrations; + +namespace Squidex.Shared; + +public abstract class MigrationStatusTests +{ + protected abstract Task CreateSutAsync(); + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + await sut.UnlockAsync(); + return sut; + } + + [Fact] + public async Task Should_update_version() + { + var sut = await CreateAndPrepareSutAsync(); + + var version_0 = await sut.GetVersionAsync(); + await sut.CompleteAsync(version_0 + 1); + + var version_1 = await sut.GetVersionAsync(); + + Assert.Equal(version_0 + 1, version_1); + } + + [Fact] + public async Task Should_acquire_lock() + { + var sut = await CreateAndPrepareSutAsync(); + + var lockTaken = 0; + await Parallel.ForEachAsync(Enumerable.Range(0, 50), async (_, ct) => + { + if (await sut.TryLockAsync(ct)) + { + Interlocked.Increment(ref lockTaken); + } + }); + + Assert.Equal(1, lockTaken); + } + + [Fact] + public async Task Should_unlock_after_lock_taken() + { + var sut = await CreateAndPrepareSutAsync(); + + var taken_0 = await sut.TryLockAsync(); + var taken_1 = await sut.TryLockAsync(); + + await sut.UnlockAsync(); + + var taken_2 = await sut.TryLockAsync(); + + Assert.True(taken_0); + Assert.True(taken_2); + + Assert.False(taken_1); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/RequestLogRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/RequestLogRepositoryTests.cs new file mode 100644 index 000000000..77cace2b6 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/RequestLogRepositoryTests.cs @@ -0,0 +1,97 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Infrastructure.Log; + +namespace Squidex.Shared; + +public abstract class RequestLogRepositoryTests +{ + protected abstract Task CreateSutAsync(); + + [Fact] + public async Task Should_delete_by_key() + { + var sut = await CreateSutAsync(); + + var key = Guid.NewGuid().ToString(); + + var now = SystemClock.Instance.GetCurrentInstant(); + var timeMin = now.Minus(Duration.FromDays(300)); + var timeMax = now.Plus(Duration.FromDays(300)); + + await sut.InsertManyAsync([ + new Request { Key = key }, + new Request { Key = key }, + ]); + + await sut.DeleteAsync(key); + + var found = await sut.QueryAllAsync(key, timeMin, timeMax).ToListAsync(); + + Assert.DoesNotContain(found, x => x.Key == key); + } + + [Fact] + public async Task Should_query_by_several_factory() + { + var sut = await CreateSutAsync(); + + var key1 = Guid.NewGuid().ToString(); + var key2 = Guid.NewGuid().ToString(); + + var now = SystemClock.Instance.GetCurrentInstant(); + var timeMin = now.Minus(Duration.FromDays(300)); + var timeMax = now.Plus(Duration.FromDays(300)); + + await sut.InsertManyAsync([ + new Request + { + Key = key1, + Timestamp = now, + }, + new Request + { + Key = key1, + Timestamp = now.Plus(Duration.FromHours(1)), + }, + new Request + { + Key = key1, + Timestamp = now.Plus(Duration.FromHours(2)), + }, + new Request + { + Key = key1, + Timestamp = now.Plus(Duration.FromHours(3)), + }, + new Request + { + Key = key2, + Timestamp = now, + }, + new Request + { + Key = key2, + Timestamp = now.Plus(Duration.FromHours(1)), + }, + ]); + + var byKey1 = await sut.QueryAllAsync(key1, timeMin, timeMax).ToListAsync(); + Assert.Equal(4, byKey1.Count); + + var byKey2 = await sut.QueryAllAsync(key2, timeMin, timeMax).ToListAsync(); + Assert.Equal(2, byKey2.Count); + + var byDate = await sut.QueryAllAsync(key1, now, now.Plus(Duration.FromDays(2))).ToListAsync(); + Assert.Equal(4, byDate.Count); + + var outOfDate = await sut.QueryAllAsync(key1, timeMin, now.Minus(Duration.FromDays(2))).ToListAsync(); + Assert.Empty(outOfDate); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/RuleEventRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/RuleEventRepositoryTests.cs new file mode 100644 index 000000000..dd5df10d1 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/RuleEventRepositoryTests.cs @@ -0,0 +1,267 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities.Rules; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Infrastructure; + +namespace Squidex.Shared; + +public abstract class RuleEventRepositoryTests +{ + private const int NumValues = 250; + private static readonly NamedId[] AppIds = + [ + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d452"), "my-app1"), + NamedId.Of(DomainId.Create("3b5ba909-e5a5-4858-9d0d-df4ff922d453"), "my-app1"), + ]; + + private readonly DomainId appId; + + protected RuleEventRepositoryTests() + { + appId = AppIds[Random.Shared.Next(AppIds.Length)].Id; + } + + protected abstract Task CreateSutAsync(); + + protected async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if ((await sut.QueryByAppAsync(AppIds[0].Id, null)).Count > 0) + { + return sut; + } + + var batch = new List(); + + async Task ExecuteBatchAsync(RuleEventWrite? entity) + { + if (entity != null) + { + batch.Add(entity.Value); + } + + if ((entity == null || batch.Count >= 1000) && batch.Count > 0) + { + await sut.EnqueueAsync(batch); + batch.Clear(); + } + } + + var created = SystemClock.Instance.GetCurrentInstant(); + + foreach (var forAppId in AppIds) + { + foreach (var ruleId in AppIds) + { + for (var i = 0; i < NumValues; i++) + { + await ExecuteBatchAsync(new RuleEventWrite + { + Job = new RuleJob + { + Id = DomainId.NewGuid(), + ActionData = $"Data{i}", + ActionName = $"Action{i}", + AppId = forAppId.Id, + Created = created, + Description = $"Description{i}", + EventName = $"Event{i}", + ExecutionPartition = 0, + Expires = created, + RuleId = ruleId.Id, + }, + NextAttempt = created.Plus(Duration.FromDays(i)), + }); + } + } + } + + await ExecuteBatchAsync(null); + return sut; + } + + [Fact] + public async Task Should_find_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var event1 = (await sut.QueryByAppAsync(appId))[0]; + var event2 = await sut.FindAsync(event1.Id); + + Assert.NotNull(event2); + } + + [Fact] + public async Task Should_query_by_app() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryByAppAsync(appId); + + // The default page size is 20. + Assert.Equal(20, result.Count); + } + + [Fact] + public async Task Should_query_by_app_and_rule() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryByAppAsync(appId, appId); + + // The default page size is 20. + Assert.Equal(20, result.Count); + } + + [Fact] + public async Task Should_query_by_app_and_rule_with_count() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryByAppAsync(appId, appId, 0, int.MaxValue); + + // Unlimited count. + Assert.Equal(NumValues, result.Count); + } + + [Fact] + public async Task Should_query_by_app_and_rule_with_offset() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryByAppAsync(appId, appId, NumValues - 5); + + // Only take the last 5 events. + Assert.Equal(5, result.Count); + } + + [Fact] + public async Task Should_query_pending() + { + var sut = await CreateAndPrepareSutAsync(); + + // Do not return all events. + var now = SystemClock.Instance.GetCurrentInstant().Plus(Duration.FromDays(20)); + + var result = await sut.QueryPendingAsync(now).ToListAsync(); + + // Result does not return all items. + Assert.InRange(result.Count, 50, 100); + } + + [Fact] + public async Task Should_enqueue_item() + { + var sut = await CreateSutAsync(); + + var id = DomainId.NewGuid(); + var randomRuleId = DomainId.NewGuid(); + var randomAppId = DomainId.NewGuid(); + + await sut.EnqueueAsync([ + new RuleEventWrite + { + Job = new RuleJob + { + Id = id, + ActionData = $"Data", + ActionName = "Action", + AppId = randomAppId, + Created = SystemClock.Instance.GetCurrentInstant(), + Description = $"Description", + EventName = "Event", + ExecutionPartition = 0, + RuleId = randomRuleId, + }, + }, + ]); + + var nextAttempt = SystemClock.Instance.GetCurrentInstant(); + await sut.EnqueueAsync(id, nextAttempt); + + var found = await sut.FindAsync(id); + found!.NextAttempt!.Value.Should().BeCloseTo(nextAttempt, Duration.FromSeconds(1)); + } + + [Fact] + public async Task Should_cancel_by_id() + { + var sut = await CreateSutAsync(); + + var id = DomainId.NewGuid(); + var randomRuleId = DomainId.NewGuid(); + var randomAppId = DomainId.NewGuid(); + await EnqueueEvent(sut, id, randomRuleId, randomAppId); + + await sut.CancelByEventAsync(id); + + var found = await sut.FindAsync(id); + Assert.Null(found!.NextAttempt); + Assert.Equal(RuleJobResult.Cancelled, found.JobResult); + } + + [Fact] + public async Task Should_cancel_by_app_id() + { + var sut = await CreateSutAsync(); + + var id = DomainId.NewGuid(); + var randomRuleId = DomainId.NewGuid(); + var randomAppId = DomainId.NewGuid(); + await EnqueueEvent(sut, id, randomRuleId, randomAppId); + + await sut.CancelByAppAsync(randomAppId); + + var found = await sut.FindAsync(id); + Assert.Null(found!.NextAttempt); + Assert.Equal(RuleJobResult.Cancelled, found.JobResult); + } + + [Fact] + public async Task Should_cancel_by_rule_id() + { + var sut = await CreateSutAsync(); + + var id = DomainId.NewGuid(); + var randomRuleId = DomainId.NewGuid(); + var randomAppId = DomainId.NewGuid(); + await EnqueueEvent(sut, id, randomRuleId, randomAppId); + + await sut.CancelByRuleAsync(randomRuleId); + + var found = await sut.FindAsync(id); + Assert.Null(found!.NextAttempt); + Assert.Equal(RuleJobResult.Cancelled, found.JobResult); + } + + private static async Task EnqueueEvent(IRuleEventRepository sut, DomainId id, DomainId ruleId, DomainId appId) + { + await sut.EnqueueAsync([ + new RuleEventWrite + { + Job = new RuleJob + { + Id = id, + ActionData = $"Data", + ActionName = "Action", + AppId = appId, + Created = SystemClock.Instance.GetCurrentInstant(), + Description = $"Description", + EventName = "Event", + ExecutionPartition = 0, + RuleId = ruleId, + }, + NextAttempt = SystemClock.Instance.GetCurrentInstant(), + }, + ]); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/RuleRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/RuleRepositoryTests.cs new file mode 100644 index 000000000..cf1773628 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/RuleRepositoryTests.cs @@ -0,0 +1,132 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Rules.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Shared; + +public abstract class RuleRepositoryTests +{ + private static readonly DomainId KnownId = DomainId.Create("3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"); + + protected abstract Task CreateSutAsync(); + + protected virtual async Task PrepareAsync(IRuleRepository sut, Rule[] rules) + { + if (sut is not ISnapshotStore store) + { + return; + } + + var writes = rules.Select(x => new SnapshotWriteJob(x.Id, x, 0)); + + await store.WriteManyAsync(writes); + } + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if ((await sut.QueryAllAsync(KnownId)).Count > 0) + { + return sut; + } + + var created = SystemClock.Instance.GetCurrentInstant(); + var createdBy = RefToken.Client("client1"); + + var rule1 = new Rule + { + AppId = NamedId.Of(KnownId, "my-app"), + Id = DomainId.NewGuid(), + Name = "rule1", + Created = created, + CreatedBy = createdBy, + }; + + var rule2 = new Rule + { + AppId = NamedId.Of(KnownId, "my-app"), + Id = KnownId, + Name = "rule2", + Created = created, + CreatedBy = createdBy, + }; + + var otherApp = new Rule + { + AppId = NamedId.Of(DomainId.NewGuid(), "my-app"), + Id = DomainId.NewGuid(), + Name = "rule3", + Created = created, + CreatedBy = createdBy, + }; + + await PrepareAsync(sut, [ + rule1, + rule2, + otherApp, + ]); + + return sut; + } + + [Fact] + public async Task Should_query_by_app() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.QueryAllAsync(KnownId); + + Assert.Equal(2, found.Count); + } + + [Fact] + public async Task Should_delete_by_app() + { + var sut = await CreateSutAsync(); + if (sut is not IDeleter deleter) + { + return; + } + + var appId = NamedId.Of(DomainId.NewGuid(), "my-app"); + + var rule1 = new Rule + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-rule", + }; + + var rule2 = new Rule + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-rule", + }; + + await PrepareAsync(sut, [ + rule1, + rule2, + ]); + + var found1 = await sut.QueryAllAsync(appId.Id); + Assert.Equal(2, found1.Count); + + await deleter.DeleteAppAsync(new App { Id = appId.Id }, default); + + var found2 = await sut.QueryAllAsync(appId.Id); + Assert.Empty(found2); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/SchemaRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/SchemaRepositoryTests.cs new file mode 100644 index 000000000..189977e61 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/SchemaRepositoryTests.cs @@ -0,0 +1,206 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Shared; + +public abstract class SchemaRepositoryTests +{ + private static readonly DomainId KnownId = DomainId.Create("3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"); + + protected abstract Task CreateSutAsync(); + + protected virtual async Task PrepareAsync(ISchemaRepository sut, Schema[] schemas) + { + if (sut is not ISnapshotStore store) + { + return; + } + + var writes = schemas.Select(x => new SnapshotWriteJob(x.Id, x, 0)); + + await store.WriteManyAsync(writes); + } + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if (await sut.FindAsync(KnownId, KnownId) != null) + { + return sut; + } + + var created = SystemClock.Instance.GetCurrentInstant(); + var createdBy = RefToken.Client("client1"); + + var schema1 = new Schema + { + AppId = NamedId.Of(KnownId, "my-app"), + Id = DomainId.NewGuid(), + Name = "schema1", + Created = created, + CreatedBy = createdBy, + }; + + var schema2 = new Schema + { + AppId = NamedId.Of(KnownId, "my-app"), + Id = KnownId, + Name = "schema2", + Created = created, + CreatedBy = createdBy, + }; + + var otherApp = new Schema + { + AppId = NamedId.Of(DomainId.NewGuid(), "my-app"), + Id = DomainId.NewGuid(), + Name = "schema3", + Created = created, + CreatedBy = createdBy, + }; + + await PrepareAsync(sut, [ + schema1, + schema2, + otherApp, + ]); + + return sut; + } + + [Fact] + public async Task Should_find_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.FindAsync(KnownId, KnownId); + + Assert.Equal(KnownId, found!.Id); + } + + [Fact] + public async Task Should_find_by_name() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.FindAsync(KnownId, "schema2"); + + Assert.Equal(KnownId, found!.Id); + } + + [Fact] + public async Task Should_query_by_app() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.QueryAllAsync(KnownId); + + Assert.Equal(2, found.Count); + } + + [Fact] + public async Task Should_calculate_schema_hash() + { + var sut = await CreateAndPrepareSutAsync(); + if (sut is not ISchemasHash hash) + { + return; + } + + var found = await hash.GetCurrentHashAsync(new App { Id = KnownId }); + + Assert.Equal(2, found.Count); + } + + [Fact] + public async Task Should_delete_by_app() + { + var sut = await CreateSutAsync(); + if (sut is not IDeleter deleter) + { + return; + } + + var appId = NamedId.Of(DomainId.NewGuid(), "my-app"); + + var schema1 = new Schema + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-schema", + }; + + var schema2 = new Schema + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-schema", + }; + + await PrepareAsync(sut, [ + schema1, + schema2, + ]); + + var found1 = await sut.QueryAllAsync(appId.Id); + Assert.Equal(2, found1.Count); + + await deleter.DeleteAppAsync(new App { Id = appId.Id }, default); + + var found2 = await sut.QueryAllAsync(appId.Id); + Assert.Empty(found2); + } + + [Fact] + public async Task Should_delete_by_schema() + { + var sut = await CreateSutAsync(); + if (sut is not IDeleter deleter) + { + return; + } + + var appId = NamedId.Of(DomainId.NewGuid(), "my-app"); + + var schema1 = new Schema + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-schema", + }; + + var schema2 = new Schema + { + AppId = appId, + Id = DomainId.NewGuid(), + Name = "my-schema", + }; + + await PrepareAsync(sut, [ + schema1, + schema2, + ]); + + var found1 = await sut.QueryAllAsync(appId.Id); + Assert.Equal(2, found1.Count); + + await deleter.DeleteSchemaAsync(new App { Id = appId.Id }, schema2, default); + + var found2 = await sut.QueryAllAsync(appId.Id); + Assert.Equal(schema1.Id, found2.Single().Id); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests.cs b/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests.cs new file mode 100644 index 000000000..d31a2cbda --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; + +namespace Squidex.Shared; + +public abstract class SnapshotStoreTests : SnapshotStoreTests +{ + protected override SnapshotValue CreateEntity(DomainId id, int version) + { + return new SnapshotValue { Value = $"{id}_{version}" }; + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests_T.cs b/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests_T.cs new file mode 100644 index 000000000..2c1fd8595 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/SnapshotStoreTests_T.cs @@ -0,0 +1,197 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using FluentAssertions.Equivalency; +using NodaTime; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +#pragma warning disable MA0048 // File name must match type name + +namespace Squidex.Shared; + +public abstract class SnapshotStoreTests +{ + protected abstract Task> CreateSutAsync(); + + protected abstract TEntity CreateEntity(DomainId id, int version); + + protected virtual bool CheckConsistencyOnWrite => true; + + protected virtual TEntity Cleanup(TEntity expected) + { + return expected; + } + + [Fact] + public async Task Should_insert_value() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue = CreateEntity(sourceKey, 0); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 0)); + + var found = await sut.ReadAsync(sourceKey); + + found.Value.Should().BeEquivalentTo(sourceValue, CompareOptions); + } + + [Fact] + public async Task Should_update_value() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue0 = CreateEntity(sourceKey, 0); + var sourceValue1 = CreateEntity(sourceKey, 1); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue0, 0)); + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue1, 1)); + + var found = await sut.ReadAsync(sourceKey); + + found.Value.Should().BeEquivalentTo(sourceValue1, CompareOptions); + } + + [Fact] + public async Task Should_update_value_with_expected_value() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue0 = CreateEntity(sourceKey, 0); + var sourceValue1 = CreateEntity(sourceKey, 1); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue0, 0)); + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue1, 1, 0)); + + var found = await sut.ReadAsync(sourceKey); + + found.Value.Should().BeEquivalentTo(sourceValue1, CompareOptions); + } + + [Fact] + public async Task Should_not_throw_exception_if_inserted_with_wrong_expected_version() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue = CreateEntity(sourceKey, 0); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 2, 1)); + } + + [Fact] + public async Task Should_throw_exception_if_update_expected_but_wrong_version_found() + { + if (!CheckConsistencyOnWrite) + { + return; + } + + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue = CreateEntity(sourceKey, 42); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 42)); + + var ex = await Assert.ThrowsAsync(() => sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 2, 1))); + + Assert.Equal(42, ex.VersionCurrent); + } + + [Fact] + public async Task Should_remove_entity() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue = CreateEntity(sourceKey, 0); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 42)); + await sut.RemoveAsync(sourceKey); + + var found = await sut.ReadAsync(sourceKey); + + Assert.Null(found.Value); + } + + [Fact] + public async Task Should_remove_entities() + { + var sut = await CreateSutAsync(); + + var sourceKey = DomainId.NewGuid(); + var sourceValue = CreateEntity(sourceKey, 0); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey, sourceValue, 42)); + await sut.ClearAsync(); + + var found = await sut.ReadAsync(sourceKey); + + Assert.Null(found.Value); + } + + [Fact] + public async Task Should_query_all_entities() + { + var sut = await CreateSutAsync(); + + var sourceKey1 = DomainId.NewGuid(); + var sourceValue1 = CreateEntity(sourceKey1, 41); + + var sourceKey2 = DomainId.NewGuid(); + var sourceValue2 = CreateEntity(sourceKey2, 42); + + await sut.WriteAsync(new SnapshotWriteJob(sourceKey1, sourceValue1, 41)); + await sut.WriteAsync(new SnapshotWriteJob(sourceKey2, sourceValue2, 42)); + + var found = await sut.ReadAllAsync().ToListAsync(); + + var found1 = found.Single(x => x.Key == sourceKey1); + var found2 = found.Single(x => x.Key == sourceKey2); + + found1.Should().BeEquivalentTo(new SnapshotResult(sourceKey1, sourceValue1, 41), CompareOptions); + found2.Should().BeEquivalentTo(new SnapshotResult(sourceKey2, sourceValue2, 42), CompareOptions); + } + + [Fact] + public async Task Should_write_many_and_query_all_entities() + { + var sut = await CreateSutAsync(); + + var sourceKey1 = DomainId.NewGuid(); + var sourceValue1 = CreateEntity(sourceKey1, 41); + + var sourceKey2 = DomainId.NewGuid(); + var sourceValue2 = CreateEntity(sourceKey2, 42); + + await sut.WriteManyAsync([ + new SnapshotWriteJob(sourceKey1, sourceValue1, 41), + new SnapshotWriteJob(sourceKey2, sourceValue2, 42), + ]); + + var found = await sut.ReadAllAsync().ToListAsync(); + + var found1 = found.Single(x => x.Key == sourceKey1); + var found2 = found.Single(x => x.Key == sourceKey2); + + found1.Should().BeEquivalentTo(new SnapshotResult(sourceKey1, sourceValue1, 41), CompareOptions); + found2.Should().BeEquivalentTo(new SnapshotResult(sourceKey2, sourceValue2, 42), CompareOptions); + } + + protected virtual EquivalencyAssertionOptions CompareOptions(EquivalencyAssertionOptions options) + { + return options + .RespectingDeclaredTypes() + .Using(c => c.Subject.Should().BeCloseTo(c.Expectation, Duration.FromSeconds(1))) + .WhenTypeIs(); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/SnapshotValue.cs b/backend/tests/Squidex.Data.Tests/Shared/SnapshotValue.cs new file mode 100644 index 000000000..5622c2016 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/SnapshotValue.cs @@ -0,0 +1,13 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Shared; + +public record SnapshotValue +{ + public string Value { get; set; } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/TeamRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/TeamRepositoryTests.cs new file mode 100644 index 000000000..55d8e8562 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/TeamRepositoryTests.cs @@ -0,0 +1,114 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NodaTime; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.Teams; +using Squidex.Domain.Apps.Entities.Teams.Repositories; +using Squidex.Infrastructure; +using Squidex.Infrastructure.States; + +namespace Squidex.Shared; + +public abstract class TeamRepositoryTests +{ + private static readonly DomainId KnownId = DomainId.Create("3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"); + + protected abstract Task CreateSutAsync(); + + protected virtual async Task PrepareAsync(ITeamRepository sut, Team[] teams) + { + if (sut is not ISnapshotStore store) + { + return; + } + + var writes = teams.Select(x => new SnapshotWriteJob(x.Id, x, 0)); + + await store.WriteManyAsync(writes); + } + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if (await sut.FindAsync(KnownId) != null) + { + return sut; + } + + var created = SystemClock.Instance.GetCurrentInstant(); + var createdBy = RefToken.Client("client1"); + + var known = new Team + { + Id = KnownId, + Name = "team1", + Created = created, + CreatedBy = createdBy, + }; + + var byAuth = new Team + { + Id = DomainId.NewGuid(), + Name = "by-auth", + Created = created, + CreatedBy = createdBy, + AuthScheme = new AuthScheme { Domain = "squidex.io" }, + }; + + var byContributors = new Team + { + Id = DomainId.NewGuid(), + Name = "by-contributor", + Created = created, + CreatedBy = createdBy, + Contributors = Contributors.Empty.Assign("1", Role.Owner).Assign("2", Role.Owner), + }; + + await PrepareAsync(sut, [ + known, + byAuth, + byContributors, + ]); + + return sut; + } + + [Fact] + public async Task Should_find_by_id() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.FindAsync(KnownId); + + Assert.Equal(KnownId, found!.Id); + } + + [Fact] + public async Task Should_find_by_auth_schema() + { + var sut = await CreateAndPrepareSutAsync(); + + var found = await sut.FindByAuthDomainAsync("squidex.io"); + + Assert.Equal("by-auth", found!.Name); + } + + [Theory] + [InlineData("1")] + [InlineData("2")] + public async Task Should_query_by_contributor(string id) + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryAllAsync(id); + + Assert.Equal("by-contributor", result.Single().Name); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/TestExtensions.cs b/backend/tests/Squidex.Data.Tests/Shared/TestExtensions.cs new file mode 100644 index 000000000..bbdca50da --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/TestExtensions.cs @@ -0,0 +1,37 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using FluentAssertions.Execution; +using FluentAssertions.Numeric; +using NodaTime; + +namespace Squidex.Shared; + +public static class TestExtensions +{ + public static ComparableTypeAssertions BeCloseTo(this ComparableTypeAssertions assertions, Instant expected, Duration precision, + string because = "", params object[] becauseArgs) + { + if (assertions.Subject is not Instant instant) + { + throw new InvalidOperationException("Not an instant value."); + } + + var difference = instant - expected; + + var absoluteTicks = Math.Abs(difference.TotalTicks); + var absoluteDiff = Duration.FromTicks(absoluteTicks); + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(absoluteDiff <= precision) + .FailWith("Expected {context:instant} to be within {0} of {1}{reason}, but it differed by {2}.", + precision, expected, difference); + + return assertions; + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/TextIndexerStateTests.cs b/backend/tests/Squidex.Data.Tests/Shared/TextIndexerStateTests.cs new file mode 100644 index 000000000..39344ba37 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/TextIndexerStateTests.cs @@ -0,0 +1,143 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Core.TestHelpers; +using Squidex.Domain.Apps.Entities; +using Squidex.Domain.Apps.Entities.Contents; +using Squidex.Domain.Apps.Entities.Contents.Repositories; +using Squidex.Domain.Apps.Entities.Contents.Text; +using Squidex.Domain.Apps.Entities.Contents.Text.State; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; + +#pragma warning disable MA0040 // Forward the CancellationToken parameter to methods that take one + +namespace Squidex.Shared; + +public abstract class TextIndexerStateTests : GivenContext +{ + private readonly IContentRepository contentRepository = A.Fake(); + + protected abstract Task CreateSutAsync(IContentRepository contentRepository); + + [Fact] + public async Task Should_add_state() + { + var sut = await CreateSutAsync(contentRepository); + + var id1 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + var id2 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + var id3 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + + await sut.SetAsync( + [ + new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, + new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, + new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None }, + ]); + + var actual = await sut.GetAsync(HashSet.Of(id1, id2)); + + actual.Should().BeEquivalentTo(new Dictionary + { + [id1] = new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, + [id2] = new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, + }); + } + + [Fact] + public async Task Should_remove_state() + { + var sut = await CreateSutAsync(contentRepository); + + var id = new UniqueContentId(DomainId.NewGuid(), DomainId.NewGuid()); + + await sut.SetAsync( + [ + new TextContentState { UniqueContentId = id, State = TextState.Stage0_Draft__Stage1_None }, + ]); + + await sut.SetAsync( + [ + new TextContentState { UniqueContentId = id, State = TextState.Deleted }, + ]); + + var actual = await sut.GetAsync(HashSet.Of(id)); + + Assert.Empty(actual); + } + + [Fact] + public async Task Should_remove_by_app() + { + var sut = await CreateSutAsync(contentRepository); + if (sut is not IDeleter deleter) + { + return; + } + + var app1 = new App { Id = DomainId.NewGuid() }; + var app2 = new App { Id = DomainId.NewGuid() }; + + var id1 = new UniqueContentId(app1.Id, DomainId.NewGuid()); + var id2 = new UniqueContentId(app1.Id, DomainId.NewGuid()); + var id3 = new UniqueContentId(app2.Id, DomainId.NewGuid()); + + await sut.SetAsync( + [ + new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, + new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, + new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None }, + ]); + + A.CallTo(() => contentRepository.StreamIds(app1.Id, null, SearchScope.All, default)) + .Returns(new[] { id1.ContentId, id2.ContentId }.ToAsyncEnumerable()); + + await deleter.DeleteAppAsync(app1, default); + + var actual = await sut.GetAsync(HashSet.Of(id1, id2, id3)); + + actual.Should().BeEquivalentTo(new Dictionary + { + [id3] = new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None }, + }); + } + + [Fact] + public async Task Should_remove_by_schema() + { + var sut = await CreateSutAsync(contentRepository); + if (sut is not IDeleter deleter) + { + return; + } + + var id1 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + var id2 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + var id3 = new UniqueContentId(AppId.Id, DomainId.NewGuid()); + + await sut.SetAsync( + [ + new TextContentState { UniqueContentId = id1, State = TextState.Stage0_Draft__Stage1_None }, + new TextContentState { UniqueContentId = id2, State = TextState.Stage0_Published__Stage1_Draft }, + new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None }, + ]); + + A.CallTo(() => contentRepository.StreamIds(AppId.Id, A>.That.Is(Schema.Id), SearchScope.All, default)) + .Returns(new[] { id1.ContentId, id2.ContentId }.ToAsyncEnumerable()); + + await deleter.DeleteSchemaAsync(App, Schema, default); + + var actual = await sut.GetAsync(HashSet.Of(id1, id2, id3)); + + actual.Should().BeEquivalentTo(new Dictionary + { + [id3] = new TextContentState { UniqueContentId = id3, State = TextState.Stage0_Published__Stage1_None }, + }); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Shared/UsageRepositoryTests.cs b/backend/tests/Squidex.Data.Tests/Shared/UsageRepositoryTests.cs new file mode 100644 index 000000000..c452d0338 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/Shared/UsageRepositoryTests.cs @@ -0,0 +1,166 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure.UsageTracking; + +namespace Squidex.Shared; + +public abstract class UsageRepositoryTests +{ + private readonly string knownKey = "3e764e15-3cf5-427f-bb6f-f0fa29a40a2d"; + private readonly DateOnly startDate = new DateOnly(2023, 12, 11); + + protected abstract Task CreateSutAsync(); + + private async Task CreateAndPrepareSutAsync() + { + var sut = await CreateSutAsync(); + + if ((await sut.QueryAsync(knownKey, DateOnly.MinValue, DateOnly.MaxValue)).Count > 0) + { + return sut; + } + + var otherKey = Guid.NewGuid().ToString(); + + var writes = new UsageUpdate[] + { + new UsageUpdate(startDate, knownKey, "Category1", + new Counters + { + ["Key1"] = 1, + ["key2"] = 2, + }), + new UsageUpdate(startDate, knownKey, "Category2", + new Counters + { + ["Key1"] = 3, + ["key2"] = 4, + }), + new UsageUpdate(startDate.AddDays(1), knownKey, "Category1", + new Counters + { + ["Key1"] = 5, + ["key2"] = 6, + }), + new UsageUpdate(startDate.AddDays(2), knownKey, "Category2", + new Counters + { + ["Key1"] = 7, + ["key2"] = 8, + }), + new UsageUpdate(startDate, otherKey, "Category2", + new Counters + { + ["Key1"] = 9, + ["key2"] = 10, + }), + }; + + await sut.TrackUsagesAsync(writes); + return sut; + } + + public async Task Should_query_results() + { + var sut = await CreateAndPrepareSutAsync(); + + var result = await sut.QueryAsync(knownKey, startDate, startDate.AddDays(1)); + + result.Should().BeEquivalentTo(new StoredUsage[] + { + new StoredUsage("Category1", startDate, + new Counters + { + ["Key1"] = 1, + ["key2"] = 2, + }), + new StoredUsage("Category2", startDate, + new Counters + { + ["Key1"] = 3, + ["key2"] = 4, + }), + new StoredUsage("Category3", startDate.AddDays(1), + new Counters + { + ["Key1"] = 5, + ["key2"] = 6, + }), + }); + } + + [Fact] + public async Task Should_delete_by_key() + { + var sut = await CreateSutAsync(); + + var key1 = Guid.NewGuid().ToString(); + var key2 = Guid.NewGuid().ToString(); + + var writes = new UsageUpdate[] + { + new UsageUpdate(startDate, key1, "Category1", + new Counters + { + ["Key1"] = 1, + ["key2"] = 2, + }), + new UsageUpdate(startDate, key1, "Category1", + new Counters + { + ["Key1"] = 3, + ["key2"] = 4, + }), + }; + + await sut.TrackUsagesAsync(writes); + + await sut.DeleteAsync(key1); + + var byKey1 = await sut.QueryAsync(key1, DateOnly.MinValue, DateOnly.MaxValue); + Assert.Empty(byKey1); + + await sut.DeleteAsync(key2); + + var byKey2 = await sut.QueryAsync(key2, DateOnly.MinValue, DateOnly.MaxValue); + Assert.Empty(byKey2); + } + + [Fact] + public async Task Should_update_in_parallel() + { + var sut = await CreateSutAsync(); + + var sharedKey = Guid.NewGuid().ToString(); + + await Parallel.ForEachAsync(Enumerable.Range(0, 20), async (_, ct) => + { + var writes = new UsageUpdate[] + { + new UsageUpdate(startDate, sharedKey, "Category", + new Counters + { + ["Key"] = 1, + }), + }; + + await sut.TrackUsagesAsync(writes, ct); + }); + + var result = await sut.QueryAsync(sharedKey, DateOnly.MinValue, DateOnly.MaxValue); + + result.Should().BeEquivalentTo(new StoredUsage[] + { + new StoredUsage("Category", startDate, + new Counters + { + ["Key"] = 20, + }), + }); + } +} diff --git a/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj b/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj index 368256d81..0e7b70f9d 100644 --- a/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj +++ b/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj @@ -9,6 +9,7 @@ en + @@ -31,6 +32,9 @@ + + + all diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs index d458ae373..ce5145312 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs @@ -90,7 +90,7 @@ public class LanguagesConfigTests config_0.Values.Should().BeEquivalentTo( new Dictionary { - [Language.EN] = new LanguageConfig() + [Language.EN] = new LanguageConfig(), }); Assert.Equal(Language.EN, config_0.Master); @@ -112,7 +112,7 @@ public class LanguagesConfigTests [Language.EN] = new LanguageConfig(), [Language.DE] = new LanguageConfig(), [Language.ES] = new LanguageConfig(true), - [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.ES)) + [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.ES)), }); Assert.Equal(Language.DE, config.Master); @@ -146,7 +146,7 @@ public class LanguagesConfigTests { [Language.EN] = new LanguageConfig(), [Language.DE] = new LanguageConfig(), - [Language.IT] = new LanguageConfig() + [Language.IT] = new LanguageConfig(), }); Assert.Equal(Language.IT, config.Master); @@ -186,7 +186,7 @@ public class LanguagesConfigTests new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig() + [Language.IT] = new LanguageConfig(), }); Assert.Equal(Language.EN, config_3.Master); @@ -203,7 +203,7 @@ public class LanguagesConfigTests new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig(true) + [Language.IT] = new LanguageConfig(true), }); Assert.Equal(Language.EN, config_3.Master); @@ -228,7 +228,7 @@ public class LanguagesConfigTests new Dictionary { [Language.DE] = new LanguageConfig(), - [Language.IT] = new LanguageConfig() + [Language.IT] = new LanguageConfig(), }); Assert.Equal(Language.DE, config_3.Master); @@ -252,7 +252,7 @@ public class LanguagesConfigTests new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.EN)) + [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.EN)), }); Assert.Equal(Language.EN, config_2.Master); @@ -269,7 +269,7 @@ public class LanguagesConfigTests new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.EN)) + [Language.IT] = new LanguageConfig(true, ReadonlyList.Create(Language.EN)), }); Assert.Equal(Language.EN, config_2.Master); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs index a6b1d3b02..3b7ba7232 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesJsonTests.cs @@ -22,8 +22,8 @@ public class RolesJsonTests ["Custom"] = [ "Permission1", - "Permission2" - ] + "Permission2", + ], }; var expected = diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Assets/AssetMetadataTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Assets/AssetMetadataTests.cs index 14bdfa5f2..92fedbfd1 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Assets/AssetMetadataTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Assets/AssetMetadataTests.cs @@ -74,7 +74,7 @@ public class AssetMetadataTests { var sut = new AssetMetadata { - ["someValue"] = JsonValue.Create(800) + ["someValue"] = JsonValue.Create(800), }; var found = sut.TryGetByPath("someValue", out var actual); @@ -103,7 +103,7 @@ public class AssetMetadataTests JsonValue.Object() .Add("nested1", JsonValue.Object() - .Add("nested2", 12)) + .Add("nested2", 12)), }; var found = sut.TryGetByPath("meta.nested1.nested2", out var actual); @@ -119,7 +119,7 @@ public class AssetMetadataTests var sut = new AssetMetadata { - ["string"] = JsonValue.Create(value) + ["string"] = JsonValue.Create(value), }; var found = sut.TryGetString("string", out var actual); @@ -133,7 +133,7 @@ public class AssetMetadataTests { var sut = new AssetMetadata { - ["string"] = JsonValue.Create(12) + ["string"] = JsonValue.Create(12), }; var found = sut.TryGetString("string", out var actual); @@ -160,7 +160,7 @@ public class AssetMetadataTests var sut = new AssetMetadata { - ["number"] = JsonValue.Create(value) + ["number"] = JsonValue.Create(value), }; var found = sut.TryGetNumber("number", out var actual); @@ -174,7 +174,7 @@ public class AssetMetadataTests { var sut = new AssetMetadata { - ["number"] = JsonValue.Create(true) + ["number"] = JsonValue.Create(true), }; var found = sut.TryGetNumber("number", out var actual); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs index da6800dcb..2db965424 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentDataTests.cs @@ -224,7 +224,7 @@ public class ContentDataTests var source = new ContentData { ["field1"] = [], - ["field2"] = [] + ["field2"] = [], }; var clone = source.Clone(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentFieldDataTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentFieldDataTests.cs index 3dab4f7ba..73d48321d 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentFieldDataTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/ContentFieldDataTests.cs @@ -67,7 +67,7 @@ public class ContentFieldDataTests var source = new ContentFieldData { ["en"] = new JsonArray(), - ["de"] = new JsonArray() + ["de"] = new JsonArray(), }; var clone = source.Clone(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs index 108a5f34f..84c9d32c8 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs @@ -114,7 +114,7 @@ public class StatusTests { var dictionary = new Dictionary { - [Status.Draft] = 123 + [Status.Draft] = 123, }; var serialized = dictionary.SerializeAndDeserializeAsJson(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/TranslationStatusTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/TranslationStatusTests.cs index e934d1cde..c3b063988 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/TranslationStatusTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/TranslationStatusTests.cs @@ -27,7 +27,7 @@ public class TranslationStatusTests { [Language.EN] = 100, [Language.DE] = 100, - [Language.IT] = 100 + [Language.IT] = 100, }, actual); } @@ -44,7 +44,7 @@ public class TranslationStatusTests { [Language.EN] = 100, [Language.DE] = 100, - [Language.IT] = 100 + [Language.IT] = 100, }, actual); } @@ -61,7 +61,7 @@ public class TranslationStatusTests { [Language.EN] = 0, [Language.DE] = 0, - [Language.IT] = 0 + [Language.IT] = 0, }, actual); } @@ -95,7 +95,7 @@ public class TranslationStatusTests { [Language.EN] = 100, [Language.DE] = 67, - [Language.IT] = 0 + [Language.IT] = 0, }, actual); } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs index 10ee0799c..b3fdb68a4 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs @@ -25,11 +25,11 @@ public class WorkflowJsonTests [Status.Draft] = new WorkflowStep( new Dictionary { - [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2") + [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2"), }.ToReadonlyDictionary(), "#00ff00", NoUpdate.When("Expression", "Role1", "Role2"), - true) + true), }.ToReadonlyDictionary(), ReadonlyList.Create(DomainId.NewGuid()), "MyName"); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs index ce920b2e7..af1750224 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs @@ -21,13 +21,13 @@ public class WorkflowTests new Dictionary { [Status.Archived] = WorkflowTransition.When("ToArchivedExpr", "ToArchivedRole"), - [Status.Published] = WorkflowTransition.When("ToPublishedExpr", "ToPublishedRole") + [Status.Published] = WorkflowTransition.When("ToPublishedExpr", "ToPublishedRole"), }.ToReadonlyDictionary(), StatusColors.Draft), [Status.Archived] = new WorkflowStep(), [Status.Published] = - new WorkflowStep() + new WorkflowStep(), }.ToReadonlyDictionary()); [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WriteContentTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WriteContentTests.cs index 12c9be29a..70d134eb8 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WriteContentTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WriteContentTests.cs @@ -45,7 +45,7 @@ public class WriteContentTests .AddField("location", new ContentFieldData() .AddInvariant(JsonValue.Create("London"))), - NewStatus = Status.Draft + NewStatus = Status.Draft, }; var actual = source.ToContent(); @@ -76,7 +76,7 @@ public class WriteContentTests .AddField("location", new ContentFieldData() .AddInvariant(JsonValue.Create("Berlin"))), - NewStatus = null + NewStatus = null, }; var actual = source.ToContent(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs index fa90226c1..a1f471ae0 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/ArrayFieldTests.cs @@ -137,11 +137,11 @@ public class ArrayfieldTests { var properties1 = new NumberFieldProperties { - MinValue = 10 + MinValue = 10, }; var properties2 = new NumberFieldProperties { - MinValue = 10 + MinValue = 10, }; var parent_1 = parent_0.AddField(CreateField(1)); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldCompareTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldCompareTests.cs index e33d6724e..611b0d288 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldCompareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldCompareTests.cs @@ -19,16 +19,16 @@ public class FieldCompareTests { DefaultValues = new LocalizedValue(new Dictionary { - ["iv"] = "ABC" - }) + ["iv"] = "ABC", + }), }; var rhs = new StringFieldProperties { DefaultValues = new LocalizedValue(new Dictionary { - ["iv"] = "ABC" - }) + ["iv"] = "ABC", + }), }; Assert.Equal(lhs, rhs); @@ -41,16 +41,16 @@ public class FieldCompareTests { DefaultValues = new LocalizedValue?>(new Dictionary?> { - ["iv"] = ReadonlyList.Create("A", "B", "C") - }) + ["iv"] = ReadonlyList.Create("A", "B", "C"), + }), }; var rhs = new TagsFieldProperties { DefaultValues = new LocalizedValue?>(new Dictionary?> { - ["iv"] = ReadonlyList.Create("A", "B", "C") - }) + ["iv"] = ReadonlyList.Create("A", "B", "C"), + }), }; Assert.Equal(lhs, rhs); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldNamesTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldNamesTests.cs index ec865d916..a20d3b2e3 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldNamesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/FieldNamesTests.cs @@ -128,7 +128,7 @@ public class FieldNamesTests "lastModified", "lastModifiedBy.avatar", "data.data1", - "data.data2.iv" + "data.data2.iv", }, migrated.ToArray()); } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs index ed5505fe5..ad9b45204 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs @@ -195,11 +195,11 @@ public class SchemaTests { var properties1 = new NumberFieldProperties { - MinValue = 10 + MinValue = 10, }; var properties2 = new NumberFieldProperties { - MinValue = 10 + MinValue = 10, }; var schema_1 = schema_0.AddField(CreateField(1)); @@ -426,11 +426,11 @@ public class SchemaTests { var scripts1 = new SchemaScripts { - Query = "" + Query = "", }; var scripts2 = new SchemaScripts { - Query = "" + Query = "", }; var schema_1 = schema_0.SetScripts(scripts1); @@ -454,11 +454,11 @@ public class SchemaTests { var urls1 = new Dictionary { - ["web"] = "Url" + ["web"] = "Url", }.ToReadonlyDictionary(); var urls2 = new Dictionary { - ["web"] = "Url" + ["web"] = "Url", }.ToReadonlyDictionary(); var schema_1 = schema_0.SetPreviewUrls(urls1); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs index e999b6f40..52bca45c1 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionFlatTests.cs @@ -53,7 +53,7 @@ public class ContentConversionFlatTests .AddLocalized("it", 4) }, { "field3", JsonValue.Create(6) }, - { "field4", JsonValue.Create(7) } + { "field4", JsonValue.Create(7) }, }; Assert.True(expected.EqualsDictionary(output)); @@ -69,7 +69,7 @@ public class ContentConversionFlatTests { "field1", JsonValue.Create(1) }, { "field2", JsonValue.Create(4) }, { "field3", JsonValue.Create(6) }, - { "field4", JsonValue.Create(7) } + { "field4", JsonValue.Create(7) }, }; Assert.True(expected.EqualsDictionary(output)); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs index 40007ccb1..a4ed44f56 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ContentConversionTests.cs @@ -32,7 +32,7 @@ public class ContentConversionTests components = new ResolvedComponents(new Dictionary { - [DomainId.Empty] = schema + [DomainId.Empty] = schema, }); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValueFactoryTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValueFactoryTests.cs index be1632721..1f948bfae 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValueFactoryTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValueFactoryTests.cs @@ -68,9 +68,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue?>(new Dictionary?> { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = ReadonlyList.Create("1", "2") + DefaultValue = ReadonlyList.Create("1", "2"), }); Assert.Equal(new JsonArray(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -95,9 +95,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue(new Dictionary { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = true + DefaultValue = true, }); Assert.Equal(JsonValue.Null, DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -162,9 +162,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue(new Dictionary { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = FutureDays(15) + DefaultValue = FutureDays(15), }); Assert.Equal(JsonValue.Null, DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -209,9 +209,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue(new Dictionary { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = 12 + DefaultValue = 12, }); Assert.Equal(JsonValue.Null, DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -246,9 +246,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue?>(new Dictionary?> { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = ReadonlyList.Create("1", "2") + DefaultValue = ReadonlyList.Create("1", "2"), }); Assert.Equal(new JsonArray(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -281,9 +281,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue(new Dictionary { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = "default" + DefaultValue = "default", }); Assert.Equal(JsonValue.Null, DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -308,9 +308,9 @@ public class DefaultValueFactoryTests { DefaultValues = new LocalizedValue?>(new Dictionary?> { - [language.Iso2Code] = null + [language.Iso2Code] = null, }), - DefaultValue = ReadonlyList.Create("tag1", "tag2") + DefaultValue = ReadonlyList.Create("tag1", "tag2"), }); Assert.Equal(new JsonArray(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValuesTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValuesTests.cs index ba82127bc..af06db644 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValuesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/DefaultValuesTests.cs @@ -221,7 +221,7 @@ public class DefaultValuesTests new ContentConverter(ResolvedComponents.Empty, schema) .Add(new AddDefaultValues(languages.ToResolver()) { - FieldNames = ["myString"] + FieldNames = ["myString"], }) .Convert(source); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs index 68a03e3cc..3c6961dc6 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs @@ -613,7 +613,7 @@ public class FieldConvertersTests var component = new Schema { Name = "my-component" }; var components = new ResolvedComponents(new Dictionary { - [componentId] = component + [componentId] = component, }); var source = diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/StringFormatterTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/StringFormatterTests.cs index dc868b206..8e161c048 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/StringFormatterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/StringFormatterTests.cs @@ -11,7 +11,7 @@ using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Core.Operations.ConvertContent; -public sealed class StringFormatterTests +public class StringFormatterTests { [Fact] public void Should_format_null_value() diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/UpdateValuesConverterTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/UpdateValuesConverterTests.cs index 5d4fc1051..503c237e5 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/UpdateValuesConverterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/UpdateValuesConverterTests.cs @@ -25,7 +25,7 @@ public class UpdateValuesConverterTests Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), })); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs index 95ae22706..61856b58e 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs @@ -158,7 +158,7 @@ public class ValueConvertersTests var component = new Schema { Name = "my-component" }; var components = new ResolvedComponents(new Dictionary { - [componentId] = component + [componentId] = component, }); var source = @@ -186,7 +186,7 @@ public class ValueConvertersTests var component = new Schema { Name = "my-component" }; var components = new ResolvedComponents(new Dictionary { - [componentId] = component + [componentId] = component, }); var source = @@ -212,7 +212,7 @@ public class ValueConvertersTests var component = new Schema { Name = "my-component" }; var components = new ResolvedComponents(new Dictionary { - [componentId] = component + [componentId] = component, }); var source = @@ -237,7 +237,7 @@ public class ValueConvertersTests var component = new Schema { Name = "my-component" }; var components = new ResolvedComponents(new Dictionary { - [componentId] = component + [componentId] = component, }); var source = diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs index 4c467baa3..4aa5117cc 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/EventSynchronization/SchemaSynchronizerTests.cs @@ -64,7 +64,7 @@ public class SchemaSynchronizerTests { var scripts = new SchemaScripts { - Create = "" + Create = "", }; var sourceSchema = @@ -86,7 +86,7 @@ public class SchemaSynchronizerTests { var previewUrls = new Dictionary { - ["web"] = "Url" + ["web"] = "Url", }.ToReadonlyDictionary(); var sourceSchema = diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs index c92698f84..32ecfb26b 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ExtractReferenceIds/ReferenceExtractionTests.cs @@ -47,7 +47,7 @@ public class ReferenceExtractionTests components = new ResolvedComponents(new Dictionary { - [DomainId.Empty] = schema + [DomainId.Empty] = schema, }); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs index d89ce96ea..e47af71db 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/EventEnricherTests.cs @@ -54,7 +54,7 @@ public class EventEnricherTests var @event = Envelope.Create(new ContentCreated { - AppId = appId + AppId = appId, }); var enrichedEvent = new EnrichedContentEvent(); @@ -72,7 +72,7 @@ public class EventEnricherTests var @event = Envelope.Create(new ContentCreated { - Actor = actor + Actor = actor, }); var enrichedEvent = new EnrichedContentEvent(); @@ -98,7 +98,7 @@ public class EventEnricherTests var @event = Envelope.Create(new ContentCreated { - Actor = actor + Actor = actor, }); var enrichedEvent = new EnrichedContentEvent(); @@ -124,13 +124,13 @@ public class EventEnricherTests var event1 = Envelope.Create(new ContentCreated { - Actor = actor + Actor = actor, }); var event2 = Envelope.Create(new ContentCreated { - Actor = actor + Actor = actor, }); var enrichedEvent1 = new EnrichedContentEvent(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/ExpressionsAttribute.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/ExpressionsAttribute.cs index ce9e1aec1..1f124fecc 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/ExpressionsAttribute.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/ExpressionsAttribute.cs @@ -28,7 +28,7 @@ public sealed class ExpressionsAttribute(string? interpolationOld, string? inter { yield return new object[] { - $"Script(`{script}`)" + $"Script(`{script}`)", }; } @@ -36,7 +36,7 @@ public sealed class ExpressionsAttribute(string? interpolationOld, string? inter { yield return new object[] { - $"Liquid({liquid})" + $"Liquid({liquid})", }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs index 6a3bf5156..d3fcacc84 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterCompareTests.cs @@ -73,7 +73,7 @@ public class RuleEventFormatterCompareTests var formatters = new IRuleEventFormatter[] { new PredefinedPatternsFormatter(urlGenerator), - new FakeContentResolver() + new FakeContentResolver(), }; sut = new RuleEventFormatter(TestUtils.DefaultSerializer, formatters, BuildTemplateEngine(), BuildScriptEngine()); @@ -88,7 +88,7 @@ public class RuleEventFormatterCompareTests new EventFluidExtensions(urlGenerator), new StringFluidExtension(), new StringWordsFluidExtension(), - new UserFluidExtension() + new UserFluidExtension(), }; return new FluidTemplateEngine(extensions); @@ -101,14 +101,14 @@ public class RuleEventFormatterCompareTests new DateTimeJintExtension(), new EventJintExtension(urlGenerator), new StringJintExtension(), - new StringWordsJintExtension() + new StringWordsJintExtension(), }; return new JintScriptEngine(new MemoryCache(Options.Create(new MemoryCacheOptions())), Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), }), extensions); } @@ -503,7 +503,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("country", new ContentFieldData() - .AddLocalized("zh-TW", "Berlin")) + .AddLocalized("zh-TW", "Berlin")), }; var actual = await sut.FormatAsync(script, @event); @@ -527,7 +527,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant("Berlin")) + .AddInvariant("Berlin")), }; var actual = await sut.FormatAsync(script, @event); @@ -551,7 +551,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant("Berlin")) + .AddInvariant("Berlin")), }; var actual = await sut.FormatAsync(script, @event); @@ -575,7 +575,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(new JsonArray())) + .AddInvariant(new JsonArray())), }; var actual = await sut.FormatAsync(script, @event); @@ -599,7 +599,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Object().Add("name", "Berlin"))) + .AddInvariant(JsonValue.Object().Add("name", "Berlin"))), }; var actual = await sut.FormatAsync(script, @event); @@ -623,7 +623,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant("Berlin")) + .AddInvariant("Berlin")), }; var actual = await sut.FormatAsync(script, @event); @@ -647,7 +647,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Array("Berlin"))) + .AddInvariant(JsonValue.Array("Berlin"))), }; var actual = await sut.FormatAsync(script, @event); @@ -671,7 +671,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Object().Add("name", "Berlin"))) + .AddInvariant(JsonValue.Object().Add("name", "Berlin"))), }; var actual = await sut.FormatAsync(script, @event); @@ -695,7 +695,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Object().Add("name", "Berlin"))) + .AddInvariant(JsonValue.Object().Add("name", "Berlin"))), }; var actual = await sut.FormatAsync(script, @event); @@ -719,7 +719,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Array(1, 2, 3))) + .AddInvariant(JsonValue.Array(1, 2, 3))), }; var actual = await sut.FormatAsync(script, @event); @@ -743,7 +743,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(JsonValue.Object().Add("name", "Berlin"))) + .AddInvariant(JsonValue.Object().Add("name", "Berlin"))), }; var actual = await sut.FormatAsync(script, @event); @@ -798,7 +798,7 @@ public class RuleEventFormatterCompareTests new ContentData() .AddField("time", new ContentFieldData() - .AddInvariant("2020-06-01T10:10:20Z")) + .AddInvariant("2020-06-01T10:10:20Z")), }; var actual = await sut.FormatAsync(script, @event); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index d71914ddf..ad21aab6d 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -67,7 +67,7 @@ public class RuleEventFormatterTests var formatters = new IRuleEventFormatter[] { new PredefinedPatternsFormatter(urlGenerator), - new FakeContentResolver() + new FakeContentResolver(), }; sut = new RuleEventFormatter(TestUtils.DefaultSerializer, formatters, BuildTemplateEngine(), BuildScriptEngine()); @@ -78,7 +78,7 @@ public class RuleEventFormatterTests var extensions = new IFluidExtension[] { new DateTimeFluidExtension(), - new UserFluidExtension() + new UserFluidExtension(), }; return new FluidTemplateEngine(extensions); @@ -91,14 +91,14 @@ public class RuleEventFormatterTests new DateTimeJintExtension(), new EventJintExtension(urlGenerator), new StringJintExtension(), - new StringWordsJintExtension() + new StringWordsJintExtension(), }; return new JintScriptEngine(new MemoryCache(Options.Create(new MemoryCacheOptions())), Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), }), extensions); } @@ -140,7 +140,7 @@ public class RuleEventFormatterTests new ContentData() .AddField("city", new ContentFieldData() - .AddInvariant(new JsonArray())) + .AddInvariant(new JsonArray())), }; var actual = await sut.FormatAsync("${CONTENT_DATA.city.iv.data.name}", @event); @@ -280,7 +280,7 @@ public class RuleEventFormatterTests new ContentData() .AddField("categories", new ContentFieldData() - .AddInvariant(JsonValue.Array("ref1", "ref2", "ref3"))) + .AddInvariant(JsonValue.Array("ref1", "ref2", "ref3"))), }; var script = @" diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index ff101342e..ce1022a15 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -86,7 +86,7 @@ public class RuleServiceTests [ruleActionHandler], eventEnricher, TestUtils.DefaultSerializer, log, typeRegistry) { - Clock = clock + Clock = clock, }; } @@ -256,7 +256,7 @@ public class RuleServiceTests .Returns(new List { new EnrichedContentEvent { AppId = appId }, - new EnrichedContentEvent { AppId = appId } + new EnrichedContentEvent { AppId = appId }, }.ToAsyncEnumerable()); var jobs = await sut.CreateSnapshotJobsAsync(context).ToListAsync(); @@ -279,7 +279,7 @@ public class RuleServiceTests .Returns(new List { new EnrichedContentEvent { AppId = appId }, - new EnrichedContentEvent { AppId = appId } + new EnrichedContentEvent { AppId = appId }, }.ToAsyncEnumerable()); var jobs = await sut.CreateSnapshotJobsAsync(context).ToListAsync(); @@ -302,7 +302,7 @@ public class RuleServiceTests .Returns(new List { new EnrichedContentEvent { AppId = appId }, - new EnrichedContentEvent { AppId = appId } + new EnrichedContentEvent { AppId = appId }, }.ToAsyncEnumerable()); var jobs = await sut.CreateSnapshotJobsAsync(context).ToListAsync(); @@ -787,7 +787,7 @@ public class RuleServiceTests AppId = appId, IncludeStale = includeStale, IncludeSkipped = includeSkipped, - Rule = rule + Rule = rule, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleTypeProviderTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleTypeProviderTests.cs index 5671ff0ec..c079ac411 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleTypeProviderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleTypeProviderTests.cs @@ -23,7 +23,7 @@ public class RuleTypeProviderTests public enum ActionEnum { Yes, - No + No, } [RuleAction( @@ -83,7 +83,7 @@ public class RuleTypeProviderTests IconColor = "#1e5470", Display = "Action display", Description = "Action description.", - ReadMore = "https://www.readmore.com/" + ReadMore = "https://www.readmore.com/", }; expected.Properties.Add(new RuleActionProperty @@ -93,7 +93,7 @@ public class RuleTypeProviderTests Description = "Url Description", Editor = RuleFieldEditor.Url, IsFormattable = true, - IsRequired = true + IsRequired = true, }); expected.Properties.Add(new RuleActionProperty @@ -102,7 +102,7 @@ public class RuleTypeProviderTests Display = "Script", Description = null, Editor = RuleFieldEditor.Javascript, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -111,7 +111,7 @@ public class RuleTypeProviderTests Display = "Text", Description = null, Editor = RuleFieldEditor.Text, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -120,7 +120,7 @@ public class RuleTypeProviderTests Display = "TextMultiline", Description = null, Editor = RuleFieldEditor.TextArea, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -129,7 +129,7 @@ public class RuleTypeProviderTests Display = "Password", Description = null, Editor = RuleFieldEditor.Password, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -139,7 +139,7 @@ public class RuleTypeProviderTests Description = null, Editor = RuleFieldEditor.Dropdown, IsRequired = false, - Options = ["Yes", "No"] + Options = ["Yes", "No"], }); expected.Properties.Add(new RuleActionProperty @@ -149,7 +149,7 @@ public class RuleTypeProviderTests Description = null, Editor = RuleFieldEditor.Dropdown, IsRequired = false, - Options = ["Yes", "No"] + Options = ["Yes", "No"], }); expected.Properties.Add(new RuleActionProperty @@ -158,7 +158,7 @@ public class RuleTypeProviderTests Display = "Boolean", Description = null, Editor = RuleFieldEditor.Checkbox, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -167,7 +167,7 @@ public class RuleTypeProviderTests Display = "BooleanOptional", Description = null, Editor = RuleFieldEditor.Checkbox, - IsRequired = false + IsRequired = false, }); expected.Properties.Add(new RuleActionProperty @@ -176,7 +176,7 @@ public class RuleTypeProviderTests Display = "Number", Description = null, Editor = RuleFieldEditor.Number, - IsRequired = true + IsRequired = true, }); expected.Properties.Add(new RuleActionProperty @@ -185,7 +185,7 @@ public class RuleTypeProviderTests Display = "NumberOptional", Description = null, Editor = RuleFieldEditor.Number, - IsRequired = false + IsRequired = false, }); sut.Add(); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineHelperTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineHelperTests.cs index 0619af76d..6575fbcae 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineHelperTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineHelperTests.cs @@ -35,14 +35,14 @@ public class JintScriptEngineHelperTests : IClassFixture new HttpJintExtension(httpClientFactory), new StringJintExtension(), new StringWordsJintExtension(), - new StringAsyncJintExtension(translator, chatAgent) + new StringAsyncJintExtension(translator, chatAgent), }; sut = new JintScriptEngine(new MemoryCache(Options.Create(new MemoryCacheOptions())), Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), }), extensions); } @@ -52,7 +52,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "

Hello World

" + ["value"] = "

Hello World

", }; const string script = @" @@ -69,7 +69,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "## Hello World" + ["value"] = "## Hello World", }; const string script = @" @@ -86,7 +86,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "Hello, World" + ["value"] = "Hello, World", }; const string script = @" @@ -103,7 +103,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "Hello, World" + ["value"] = "Hello, World", }; const string script = @" @@ -120,7 +120,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "Hello World" + ["value"] = "Hello World", }; const string script = @" @@ -137,7 +137,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "Hello World" + ["value"] = "Hello World", }; const string script = @" @@ -154,7 +154,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "4 Häuser" + ["value"] = "4 Häuser", }; const string script = @" @@ -171,7 +171,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "4 Häuser" + ["value"] = "4 Häuser", }; const string script = @" @@ -188,7 +188,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "HelloWorld" + ["value"] = "HelloWorld", }; const string script = @" @@ -205,7 +205,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "HelloWorld" + ["value"] = "HelloWorld", }; const string script = @" @@ -222,7 +222,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var vars = new ScriptVars { - ["value"] = "HelloWorld" + ["value"] = "HelloWorld", }; const string script = @" @@ -255,7 +255,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var options = new ScriptOptions { - CanReject = true + CanReject = true, }; var vars = new ScriptVars @@ -276,7 +276,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var options = new ScriptOptions { - CanReject = true + CanReject = true, }; var vars = new ScriptVars @@ -297,7 +297,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var options = new ScriptOptions { - CanReject = true + CanReject = true, }; var vars = new ScriptVars @@ -318,7 +318,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var options = new ScriptOptions { - CanDisallow = true + CanDisallow = true, }; var vars = new ScriptVars @@ -343,7 +343,7 @@ public class JintScriptEngineHelperTests : IClassFixture var options = new ScriptOptions { - CanDisallow = true + CanDisallow = true, }; var vars = new ScriptVars @@ -809,7 +809,7 @@ public class JintScriptEngineHelperTests : IClassFixture { var httpResponse = new HttpResponseMessage(statusCode) { - Content = new StringContent("{ \"key\": 42 }", Encoding.UTF8, "application/json") + Content = new StringContent("{ \"key\": 42 }", Encoding.UTF8, "application/json"), }; var httpHandler = new MockupHttpHandler(httpResponse); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs index 4bbf21baa..dba5dce73 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/JintScriptEngineTests.cs @@ -27,7 +27,7 @@ public class JintScriptEngineTests : IClassFixture { CanReject = true, CanDisallow = true, - AsContext = true + AsContext = true, }; private readonly IHttpClientFactory httpClientFactory = A.Fake(); @@ -46,7 +46,7 @@ public class JintScriptEngineTests : IClassFixture var httpResponse = new HttpResponseMessage(HttpStatusCode.OK) { - Content = new StringContent("{ \"key\": 42 }") + Content = new StringContent("{ \"key\": 42 }"), }; var httpHandler = new MockupHttpHandler(httpResponse); @@ -58,7 +58,7 @@ public class JintScriptEngineTests : IClassFixture Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), }), extensions); } @@ -118,7 +118,7 @@ public class JintScriptEngineTests : IClassFixture var vars = new DataScriptVars { - ["data"] = content + ["data"] = content, }; const string script = @" @@ -152,7 +152,7 @@ public class JintScriptEngineTests : IClassFixture var vars = new DataScriptVars { - ["data"] = content + ["data"] = content, }; const string script = @" @@ -186,7 +186,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["data"] = new ContentData() + ["data"] = new ContentData(), }; const string script = @" @@ -201,7 +201,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["data"] = new ContentData() + ["data"] = new ContentData(), }; const string script = @" @@ -218,7 +218,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["data"] = new ContentData() + ["data"] = new ContentData(), }; const string script = @" @@ -249,7 +249,7 @@ public class JintScriptEngineTests : IClassFixture { ["data"] = content, ["dataOld"] = null, - ["operation"] = "MyOperation" + ["operation"] = "MyOperation", }; const string script = @" @@ -280,7 +280,7 @@ public class JintScriptEngineTests : IClassFixture { ["data"] = content, ["dataOld"] = null, - ["operation"] = "MyOperation" + ["operation"] = "MyOperation", }; const string script = @" @@ -306,7 +306,7 @@ public class JintScriptEngineTests : IClassFixture { ["data"] = new ContentData(), ["dataOld"] = null, - ["operation"] = "MyOperation" + ["operation"] = "MyOperation", }; const string script = @" @@ -332,7 +332,7 @@ public class JintScriptEngineTests : IClassFixture { ["data"] = new ContentData(), ["dataOld"] = null, - ["operation"] = "MyOperation" + ["operation"] = "MyOperation", }; const string script = @" @@ -368,7 +368,7 @@ public class JintScriptEngineTests : IClassFixture var vars = new DataScriptVars { - ["data"] = content + ["data"] = content, }; const string script = @" @@ -417,7 +417,7 @@ public class JintScriptEngineTests : IClassFixture { ["data"] = content, ["dataOld"] = oldContent, - ["user"] = userPrincipal + ["user"] = userPrincipal, }; const string script = @" @@ -436,7 +436,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = new { i = 2 } + ["value"] = new { i = 2 }, }; const string script = @" @@ -453,7 +453,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = new { status = Status.Published } + ["value"] = new { status = Status.Published }, }; const string script = @" @@ -470,7 +470,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = new { i = 2 } + ["value"] = new { i = 2 }, }; const string script = @" @@ -487,7 +487,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = new { i = 2 } + ["value"] = new { i = 2 }, }; const string script = @" @@ -506,7 +506,7 @@ public class JintScriptEngineTests : IClassFixture var vars = new ScriptVars { - ["value"] = id + ["value"] = id, }; const string script = @" @@ -523,7 +523,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = null + ["value"] = null, }; const string script = @" @@ -554,7 +554,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = 13 + ["value"] = 13, }; const string script1 = @" @@ -577,7 +577,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new ScriptVars { - ["value"] = 13 + ["value"] = 13, }; const string script1 = @" @@ -600,7 +600,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["value"] = 13 + ["value"] = 13, }; const string script1 = @" @@ -616,7 +616,7 @@ public class JintScriptEngineTests : IClassFixture var vars2 = new DataScriptVars { - ["data"] = new ContentData() + ["data"] = new ContentData(), }; vars2.CopyFrom(vars); @@ -635,7 +635,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["value"] = 13 + ["value"] = 13, }; var script = @$" @@ -662,7 +662,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["value"] = 13 + ["value"] = 13, }; var script = @$" @@ -687,7 +687,7 @@ public class JintScriptEngineTests : IClassFixture { var vars = new DataScriptVars { - ["metadata"] = new AssetMetadata() + ["metadata"] = new AssetMetadata(), }; var script = @$" diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs index 024861e25..47c450301 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Scripting/ScriptingCompleterTests.cs @@ -71,7 +71,7 @@ public class ScriptingCompleterTests "ctx.schemaName", "ctx.status", "ctx.statusOld", - "ctx.validate" + "ctx.validate", ]); } @@ -114,7 +114,7 @@ public class ScriptingCompleterTests "ctx.command.parentPath", "ctx.command.permanent", "ctx.command.tags", - "ctx.operation" + "ctx.operation", ]); } @@ -167,7 +167,7 @@ public class ScriptingCompleterTests "event.status", "event.timestamp", "event.type", - "event.version" + "event.version", ]); } @@ -188,7 +188,7 @@ public class ScriptingCompleterTests "user.displayName", "user.email", "user.id", - "user.role" + "user.role", }); } @@ -205,7 +205,7 @@ public class ScriptingCompleterTests "data['my-field']", "data['my-field'].iv", "id", - "version" + "version", }); } @@ -261,7 +261,7 @@ public class ScriptingCompleterTests "event.slug", "event.timestamp", "event.type", - "event.version" + "event.version", ]); } @@ -295,7 +295,7 @@ public class ScriptingCompleterTests "event.name", "event.text", "event.timestamp", - "event.version" + "event.version", ]); } @@ -331,7 +331,7 @@ public class ScriptingCompleterTests "event.schemaId.name", "event.timestamp", "event.type", - "event.version" + "event.version", ]); } @@ -364,7 +364,7 @@ public class ScriptingCompleterTests "event.callsLimit", "event.name", "event.timestamp", - "event.version" + "event.version", }); } @@ -391,7 +391,7 @@ public class ScriptingCompleterTests [ $"{path}", $"{path}.identifier", - $"{path}.type" + $"{path}.type", ]; } @@ -405,7 +405,7 @@ public class ScriptingCompleterTests $"{path}.email", $"{path}.id", $"{path}.isClient", - $"{path}.isUser" + $"{path}.isUser", ]; } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Templates/FluidTemplateEngineTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Templates/FluidTemplateEngineTests.cs index 83d2d7765..e7fbdc05b 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Templates/FluidTemplateEngineTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/Templates/FluidTemplateEngineTests.cs @@ -24,7 +24,7 @@ public class FluidTemplateEngineTests new ContentFluidExtension(), new DateTimeFluidExtension(), new StringFluidExtension(), - new StringWordsFluidExtension() + new StringWordsFluidExtension(), }; sut = new FluidTemplateEngine(extensions); @@ -38,10 +38,10 @@ public class FluidTemplateEngineTests { var value = new { - User = RefToken.User("me") + User = RefToken.User("me"), }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal(expected, actual); } @@ -54,10 +54,10 @@ public class FluidTemplateEngineTests { var value = new { - Id = NamedId.Of("42", "my-app") + Id = NamedId.Of("42", "my-app"), }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal(expected, actual); } @@ -67,12 +67,12 @@ public class FluidTemplateEngineTests { var value = new { - Id = DomainId.NewGuid() + Id = DomainId.NewGuid(), }; var template = "{{ e.id }}"; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal(value.Id.ToString(), actual); } @@ -82,12 +82,12 @@ public class FluidTemplateEngineTests { var value = new { - Type = EnrichedContentEventType.Created + Type = EnrichedContentEventType.Created, }; var template = "{{ e.type }}"; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal(value.Type.ToString(), actual); } @@ -99,12 +99,12 @@ public class FluidTemplateEngineTests var value = new { - Timestamp = now + Timestamp = now, }; var template = "{{ e.timestamp | format_date: 'yyyy-MM-dd-hh-mm-ss' }}"; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal($"{now:yyyy-MM-dd-hh-mm-ss}", actual); } @@ -120,10 +120,10 @@ public class FluidTemplateEngineTests new ContentData() .AddField("value", new ContentFieldData() - .AddLocalized("en", "Hello")) + .AddLocalized("en", "Hello")), }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("Hello", actual); } @@ -135,10 +135,10 @@ public class FluidTemplateEngineTests var value = new { - Text = "

Hello World

" + Text = "

Hello World

", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("Hello World", actual); } @@ -150,10 +150,10 @@ public class FluidTemplateEngineTests var value = new { - Text = "## Hello World" + Text = "## Hello World", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("Hello World", actual); } @@ -165,10 +165,10 @@ public class FluidTemplateEngineTests var value = new { - Text = "Hello World" + Text = "Hello World", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("2", actual); } @@ -180,10 +180,10 @@ public class FluidTemplateEngineTests var value = new { - text = "Hello World" + text = "Hello World", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("10", actual); } @@ -195,10 +195,10 @@ public class FluidTemplateEngineTests var value = new { - text = "HelloWorld" + text = "HelloWorld", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("HelloWorld".ToMD5(), actual); } @@ -210,10 +210,10 @@ public class FluidTemplateEngineTests var value = new { - text = "HelloWorld" + text = "HelloWorld", }; - var actual = await RenderAync(template, value); + var actual = await RenderAsync(template, value); Assert.Equal("HelloWorld".ToSha256(), actual); } @@ -226,7 +226,7 @@ public class FluidTemplateEngineTests await Assert.ThrowsAsync(() => sut.RenderAsync(template, [])); } - private Task RenderAync(string template, object value) + private Task RenderAsync(string template, object value) { return sut.RenderAsync(template, new TemplateVars { ["e"] = value }); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs index 7fa9bc904..a7a28c355 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/AssetsFieldTests.cs @@ -58,7 +58,7 @@ public class AssetsFieldTests : IClassFixture { IsRequired = true, MinItems = 1, - MaxItems = 3 + MaxItems = 3, }); await sut.ValidateAsync(CreateValue(asset1), errors, factory: factory); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentFieldTests.cs index 968df8a9f..60073ac34 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentFieldTests.cs @@ -181,7 +181,7 @@ public class ComponentFieldTests : IClassFixture var components = new ResolvedComponents(new Dictionary { [schemaId1] = schema, - [schemaId2] = schema + [schemaId2] = schema, }); return (schemaId1, field, components); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentsFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentsFieldTests.cs index e31f38d44..1d2ebf184 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentsFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ComponentsFieldTests.cs @@ -229,7 +229,7 @@ public class ComponentsFieldTests : IClassFixture var components = new ResolvedComponents(new Dictionary { [schemaId1] = schema, - [schemaId2] = schema + [schemaId2] = schema, }); return (schemaId1, field, components); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs index 22a447f52..771d04850 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ContentValidationTests.cs @@ -49,7 +49,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Validation failed with internal error.", "myField.iv") + new ValidationError("Validation failed with internal error.", "myField.iv"), }); } @@ -80,7 +80,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Validation failed with internal error.", "myField") + new ValidationError("Validation failed with internal error.", "myField"), }); } @@ -97,7 +97,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Not a known field.", "unknown") + new ValidationError("Not a known field.", "unknown"), }); } @@ -118,7 +118,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Must be less or equal to 100.", "myField.iv") + new ValidationError("Must be less or equal to 100.", "myField.iv"), }); } @@ -140,7 +140,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Not a known invariant value.", "myField.es"), - new ValidationError("Not a known invariant value.", "myField.it") + new ValidationError("Not a known invariant value.", "myField.it"), }); } @@ -159,7 +159,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Field is required.", "myField.de"), - new ValidationError("Field is required.", "myField.en") + new ValidationError("Field is required.", "myField.en"), }); } @@ -177,7 +177,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } @@ -195,7 +195,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } @@ -216,7 +216,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Not a known language.", "myField.ru") + new ValidationError("Not a known language.", "myField.ru"), }); } @@ -261,7 +261,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Not a known language.", "myField.es"), - new ValidationError("Not a known language.", "myField.it") + new ValidationError("Not a known language.", "myField.it"), }); } @@ -278,7 +278,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Not a known field.", "unknown") + new ValidationError("Not a known field.", "unknown"), }); } @@ -299,7 +299,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Must be less or equal to 100.", "myField.iv") + new ValidationError("Must be less or equal to 100.", "myField.iv"), }); } @@ -321,7 +321,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Not a known invariant value.", "myField.es"), - new ValidationError("Not a known invariant value.", "myField.it") + new ValidationError("Not a known invariant value.", "myField.it"), }); } @@ -370,7 +370,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Not a known language.", "myField.ru") + new ValidationError("Not a known language.", "myField.ru"), }); } @@ -392,7 +392,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Not a known language.", "myField.es"), - new ValidationError("Not a known language.", "myField.it") + new ValidationError("Not a known language.", "myField.it"), }); } @@ -418,7 +418,7 @@ public class ContentValidationTests : IClassFixture new List { new ValidationError("Field is required.", "myField.iv[1].myNested"), - new ValidationError("Field is required.", "myField.iv[3].myNested") + new ValidationError("Field is required.", "myField.iv[3].myNested"), }); } @@ -471,7 +471,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } @@ -494,7 +494,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } @@ -515,7 +515,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } @@ -538,7 +538,7 @@ public class ContentValidationTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Field is required.", "myField.iv") + new ValidationError("Field is required.", "myField.iv"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs index be5a17757..44571ae41 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ReferencesFieldTests.cs @@ -66,7 +66,7 @@ public class ReferencesFieldTests : IClassFixture { IsRequired = true, MinItems = 1, - MaxItems = 3 + MaxItems = 3, }); await sut.ValidateAsync(CreateValue(ref1), errors, factory: factory); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs index e5a19d3dd..d81d67140 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/UIFieldTests.cs @@ -80,7 +80,7 @@ public class UIFieldTests : IClassFixture new[] { new ValidationError("Value must not be defined.", "myUI1"), - new ValidationError("Value must not be defined.", "myUI2") + new ValidationError("Value must not be defined.", "myUI2"), }); } @@ -107,7 +107,7 @@ public class UIFieldTests : IClassFixture dataErrors.Should().BeEquivalentTo( new[] { - new ValidationError("Value must not be defined.", "myArray.iv[1].myUI") + new ValidationError("Value must not be defined.", "myArray.iv[1].myUI"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs index 59c2eb559..b00661ed9 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/AssetsValidatorTests.cs @@ -27,7 +27,7 @@ public class AssetsValidatorTests : IClassFixture public static readonly TheoryData AssetsWithDimensions = new TheoryData { { Image1.Id }, - { Video.Id } + { Video.Id }, }; [Fact] @@ -240,7 +240,7 @@ public class AssetsValidatorTests : IClassFixture errors.Should().BeEquivalentTo( [ "[1]: Must be an allowed extension.", - "[2]: Must be an allowed extension." + "[2]: Must be an allowed extension.", ]); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs index e93e3dea4..4d90866f6 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/CollectionItemValidatorTests.cs @@ -44,7 +44,7 @@ public class CollectionItemValidatorTests : IClassFixture errors.Should().BeEquivalentTo( [ "[2]: Must be between 2 and 4.", - "[4]: Must be between 2 and 4." + "[4]: Must be between 2 and 4.", ]); } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/UniqueObjectValuesValidatorTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/UniqueObjectValuesValidatorTests.cs index d545b23e9..3de83c97b 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/UniqueObjectValuesValidatorTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/Validators/UniqueObjectValuesValidatorTests.cs @@ -45,7 +45,7 @@ public class UniqueObjectValuesValidatorTests : IClassFixture var foundIds = new List { - new ContentIdStatus(id, id, Status.Draft) + new ContentIdStatus(id, id, Status.Draft), }; return Task.FromResult>(foundIds); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs index 5802a633c..6b0161669 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestAssets.cs @@ -19,7 +19,7 @@ public static class TestAssets Id = id, FileName = "MyDocument.pdf", FileSize = 1024 * 4, - Type = AssetType.Unknown + Type = AssetType.Unknown, }; } @@ -35,7 +35,7 @@ public static class TestAssets { [KnownMetadataKeys.PixelWidth] = 800, [KnownMetadataKeys.PixelHeight] = 600, - } + }, }; } @@ -51,7 +51,7 @@ public static class TestAssets { [KnownMetadataKeys.VideoWidth] = 800, [KnownMetadataKeys.VideoHeight] = 600, - } + }, }; } @@ -63,7 +63,7 @@ public static class TestAssets FileName = "MyImage.png", FileSize = 1024 * 8, Type = AssetType.Unknown, - MimeType = "image/svg+xml" + MimeType = "image/svg+xml", }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestSchema.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestSchema.cs index 8c4ba0ba1..ba72eb675 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestSchema.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestSchema.cs @@ -56,7 +56,7 @@ public static class TestSchema var resolvedComponents = new ResolvedComponents(new Dictionary { [componentId1] = component1, - [componentId2] = component2 + [componentId2] = component2, }); var schema = new Schema diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs index 61cc18106..ba3268312 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs @@ -34,7 +34,7 @@ public class AppProviderExtensionsTests : GivenContext Schema = Schema .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = SchemaId.Id + SchemaId = SchemaId.Id, }); var components = await AppProvider.GetComponentsAsync(Schema, ct: CancellationToken); @@ -54,7 +54,7 @@ public class AppProviderExtensionsTests : GivenContext Schema = Schema .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, componentId1.Id, false, CancellationToken)) @@ -74,7 +74,7 @@ public class AppProviderExtensionsTests : GivenContext Schema = Schema .AddComponents(1, "1", Partitioning.Invariant, new ComponentsFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, componentId1.Id, false, CancellationToken)) @@ -95,7 +95,7 @@ public class AppProviderExtensionsTests : GivenContext .AddArray(1, "1", Partitioning.Invariant, a => a .AddComponent(2, "2", new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, })); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, componentId1.Id, false, CancellationToken)) @@ -113,13 +113,13 @@ public class AppProviderExtensionsTests : GivenContext var component = new Schema { Id = componentId1.Id, Name = componentId1.Name } .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); Schema = Schema .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, componentId1.Id, false, CancellationToken)) @@ -137,13 +137,13 @@ public class AppProviderExtensionsTests : GivenContext var component1 = new Schema { Id = componentId1.Id, Name = componentId1.Name } .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId2.Id + SchemaId = componentId2.Id, }); var component2 = new Schema { Id = componentId2.Id, Name = componentId2.Name } .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, componentId1.Id, false, CancellationToken)) @@ -155,7 +155,7 @@ public class AppProviderExtensionsTests : GivenContext Schema = Schema .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { - SchemaId = componentId1.Id + SchemaId = componentId1.Id, }); var components = await AppProvider.GetComponentsAsync(Schema, ct: CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppChatToolsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppChatToolsTests.cs index 52010af75..121077164 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppChatToolsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppChatToolsTests.cs @@ -29,12 +29,12 @@ public class AppChatToolsTests : GivenContext { App = App with { - Clients = App.Clients.Add("default", "secret") + Clients = App.Clients.Add("default", "secret"), }; var chatContext = new AppChatContext { - BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppClientsRead, App.Name).Id) + BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppClientsRead, App.Name).Id), }; var tool = await sut.GetToolsAsync(chatContext, default).FirstOrDefaultAsync(); @@ -56,12 +56,12 @@ public class AppChatToolsTests : GivenContext { App = App with { - Languages = App.Languages.Set(Language.DE) + Languages = App.Languages.Set(Language.DE), }; var chatContext = new AppChatContext { - BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppLanguages, App.Name).Id) + BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppLanguages, App.Name).Id), }; var tool = await sut.GetToolsAsync(chatContext, default).FirstOrDefaultAsync(); @@ -83,12 +83,12 @@ public class AppChatToolsTests : GivenContext { App = App with { - Roles = App.Roles.Add("viewers") + Roles = App.Roles.Add("viewers"), }; var chatContext = new AppChatContext { - BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppRoles, App.Name).Id) + BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppRoles, App.Name).Id), }; var tool = await sut.GetToolsAsync(chatContext, default).FirstOrDefaultAsync(); @@ -110,12 +110,12 @@ public class AppChatToolsTests : GivenContext { App = App with { - Plan = new AssignedPlan(User, "Business") + Plan = new AssignedPlan(User, "Business"), }; var chatContext = new AppChatContext { - BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppPlans, App.Name).Id) + BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppPlans, App.Name).Id), }; var tool = await sut.GetToolsAsync(chatContext, default).FirstOrDefaultAsync(); @@ -137,7 +137,7 @@ public class AppChatToolsTests : GivenContext { var chatContext = new AppChatContext { - BaseContext = FrontendContext + BaseContext = FrontendContext, }; var tool = await sut.GetToolsAsync(chatContext, default).FirstOrDefaultAsync(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppEventDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppEventDeleterTests.cs index 221e2a470..d00c36d89 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppEventDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppEventDeleterTests.cs @@ -34,7 +34,7 @@ public class AppEventDeleterTests : GivenContext await sut.DeleteAppAsync(App, CancellationToken); A.CallTo(() => eventStore.DeleteAsync( - A.That.Matches(x => x.Prefixes!.Contains($"([a-zA-Z0-9]+)-{AppId.Id}")), + A.That.Matches(x => x.Prefixes!.Contains($"%-{AppId.Id}")), CancellationToken)) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs index 2a692198b..1e6558acc 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppPermanentDeleterTests.cs @@ -89,7 +89,7 @@ public class AppPermanentDeleterTests : GivenContext await sut.On(Envelope.Create(new AppContributorRemoved { AppId = AppId, - ContributorId = "user1" + ContributorId = "user1", })); A.CallTo(() => deleter1.DeleteContributorAsync(AppId.Id, "user1", default)) @@ -108,7 +108,7 @@ public class AppPermanentDeleterTests : GivenContext await sut.On(Envelope.Create(new AppDeleted { AppId = AppId, - Permanent = false + Permanent = false, })); A.CallTo(() => deleter1.DeleteAppAsync(App, default)) @@ -127,7 +127,7 @@ public class AppPermanentDeleterTests : GivenContext await sut.On(Envelope.Create(new AppDeleted { AppId = AppId, - Permanent = true + Permanent = true, })); A.CallTo(() => deleter1.DeleteAppAsync(App, default)) @@ -145,7 +145,7 @@ public class AppPermanentDeleterTests : GivenContext await sut.On(Envelope.Create(new AppDeleted { AppId = AppId, - Permanent = false + Permanent = false, })); A.CallTo(() => deleter1.DeleteAppAsync(App, default)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppSettingsSearchSourceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppSettingsSearchSourceTests.cs index 3630ff241..85fe55d39 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppSettingsSearchSourceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppSettingsSearchSourceTests.cs @@ -12,7 +12,7 @@ using Squidex.Shared; namespace Squidex.Domain.Apps.Entities.Apps; -public sealed class AppSettingsSearchSourceTests : GivenContext +public class AppSettingsSearchSourceTests : GivenContext { private readonly IUrlGenerator urlGenerator = A.Fake(); private readonly AppSettingsSearchSource sut; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs index 7758d30f0..bfaaca5ae 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsTests.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure.TestHelpers; namespace Squidex.Domain.Apps.Entities.Apps; -public sealed class AppUISettingsTests : GivenContext +public class AppUISettingsTests : GivenContext { private readonly TestState state; private readonly string userId = Guid.NewGuid().ToString(); @@ -50,7 +50,7 @@ public sealed class AppUISettingsTests : GivenContext { App = App with { - Contributors = Contributors.Empty.Assign(userId, Role.Owner) + Contributors = Contributors.Empty.Assign(userId, Role.Owner), }; var rootState = new TestState(AppId.Id, state.PersistenceFactory); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/BackupAppsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/BackupAppsTests.cs index 4257259fa..e4b51ebb5 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/BackupAppsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/BackupAppsTests.cs @@ -49,7 +49,7 @@ public class BackupAppsTests : GivenContext await sut.RestoreEventAsync(Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }), context, CancellationToken); A.CallTo(() => appsIndex.ReserveAsync(AppId.Id, AppId.Name, A._)) @@ -74,7 +74,7 @@ public class BackupAppsTests : GivenContext await sut.RestoreEventAsync(Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }), context, CancellationToken); await sut.RestoreAsync(context, CancellationToken); @@ -97,7 +97,7 @@ public class BackupAppsTests : GivenContext await sut.RestoreEventAsync(Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }), context, CancellationToken); await sut.CleanupRestoreErrorAsync(AppId.Id); @@ -116,7 +116,7 @@ public class BackupAppsTests : GivenContext var @event = Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }); await Assert.ThrowsAsync(() => sut.RestoreEventAsync(@event, context, CancellationToken)); @@ -189,7 +189,7 @@ public class BackupAppsTests : GivenContext var @event = Envelope.Create(new AppContributorAssigned { - ContributorId = "found" + ContributorId = "found", }); var actual = await sut.RestoreEventAsync(@event, context, CancellationToken); @@ -205,7 +205,7 @@ public class BackupAppsTests : GivenContext var @event = Envelope.Create(new AppContributorAssigned { - ContributorId = "unknown" + ContributorId = "unknown", }); var actual = await sut.RestoreEventAsync(@event, context, CancellationToken); @@ -221,7 +221,7 @@ public class BackupAppsTests : GivenContext var @event = Envelope.Create(new AppContributorRemoved { - ContributorId = "found" + ContributorId = "found", }); var actual = await sut.RestoreEventAsync(@event, context, CancellationToken); @@ -237,7 +237,7 @@ public class BackupAppsTests : GivenContext var @event = Envelope.Create(new AppContributorRemoved { - ContributorId = "unknown" + ContributorId = "unknown", }); var actual = await sut.RestoreEventAsync(@event, context, CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DefaultAppLogStoreTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DefaultAppLogStoreTests.cs index b8c68a5a0..81811857d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DefaultAppLogStoreTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DefaultAppLogStoreTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using System.Globalization; +using NodaTime; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure.Log; @@ -98,26 +99,24 @@ public class DefaultAppLogStoreTests : GivenContext [Fact] public async Task Should_write_to_stream() { - var dateFrom = DateTime.UtcNow.Date.AddDays(-30); - var dateTo = DateTime.UtcNow.Date; + var timeFrom = SystemClock.Instance.GetCurrentInstant(); + var timeTo = timeFrom.Plus(Duration.FromDays(4)); - A.CallTo(() => requestLogStore.QueryAllAsync(AppId.Id.ToString(), dateFrom, dateTo, CancellationToken)) + A.CallTo(() => requestLogStore.QueryAllAsync(AppId.Id.ToString(), timeFrom, timeTo, CancellationToken)) .Returns(new[] { CreateRecord(), CreateRecord(), CreateRecord(), - CreateRecord() + CreateRecord(), }.ToAsyncEnumerable()); var stream = new MemoryStream(); - await sut.ReadLogAsync(AppId.Id, dateFrom, dateTo, stream, CancellationToken); - + await sut.ReadLogAsync(AppId.Id, timeFrom, timeTo, stream, CancellationToken); stream.Position = 0; var lines = 0; - using (var reader = new StreamReader(stream)) { while (await reader.ReadLineAsync(default) != null) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs index c214dfdfa..b6ada1067 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/AppDomainObjectTests.cs @@ -48,7 +48,7 @@ public class AppDomainObjectTests : HandlerTestBase Team = Team with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Owner) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Owner), }; A.CallTo(() => userResolver.FindByIdOrEmailAsync(contributorId, CancellationToken)) @@ -71,8 +71,8 @@ public class AppDomainObjectTests : HandlerTestBase { Settings = new AppSettings { - HideScheduler = true - } + HideScheduler = true, + }, }; var serviceProvider = @@ -140,8 +140,8 @@ public class AppDomainObjectTests : HandlerTestBase { Settings = new AppSettings { - HideDateTimeModeButton = true - } + HideDateTimeModeButton = true, + }, }; await ExecuteCreateAsync(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppClientsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppClientsTests.cs index 109464cf0..e855a135a 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppClientsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppClientsTests.cs @@ -32,7 +32,7 @@ public class GuardAppClientsTests : GivenContext, IClassFixture GuardAppClients.CanAttach(command, App), @@ -46,7 +46,7 @@ public class GuardAppClientsTests : GivenContext, IClassFixture GuardAppClients.CanUpdate(command, App), @@ -120,7 +120,7 @@ public class GuardAppClientsTests : GivenContext, IClassFixture GuardAppClients.CanUpdate(command, App), @@ -134,7 +134,7 @@ public class GuardAppClientsTests : GivenContext, IClassFixture GuardAppClients.CanUpdate(command, App), @@ -148,7 +148,7 @@ public class GuardAppClientsTests : GivenContext, IClassFixture GuardAppContributors.CanAssign(command, App, users, planWithLimit), @@ -131,7 +131,7 @@ public class GuardAppContributorsTests : GivenContext, IClassFixture GuardAppContributors.CanRemove(command, App), @@ -222,7 +222,7 @@ public class GuardAppContributorsTests : GivenContext, IClassFixture App = App with { - TeamId = default + TeamId = default, }; Team = Team with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Owner) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Owner), }; } @@ -105,7 +105,7 @@ public class GuardAppTests : GivenContext, IClassFixture App = App with { - Plan = new AssignedPlan(RefToken.User("other"), "premium") + Plan = new AssignedPlan(RefToken.User("other"), "premium"), }; ValidationAssert.Throws(() => GuardApp.CanChangePlan(command, App, billingPlans), @@ -119,7 +119,7 @@ public class GuardAppTests : GivenContext, IClassFixture App = App with { - TeamId = Team.Id + TeamId = Team.Id, }; ValidationAssert.Throws(() => GuardApp.CanChangePlan(command, App, billingPlans), @@ -133,7 +133,7 @@ public class GuardAppTests : GivenContext, IClassFixture App = App with { - Plan = new AssignedPlan(User, "premium") + Plan = new AssignedPlan(User, "premium"), }; GuardApp.CanChangePlan(command, App, billingPlans); @@ -146,7 +146,7 @@ public class GuardAppTests : GivenContext, IClassFixture App = App with { - Plan = new AssignedPlan(User, "premium") + Plan = new AssignedPlan(User, "premium"), }; GuardApp.CanChangePlan(command, App, billingPlans); @@ -199,7 +199,7 @@ public class GuardAppTests : GivenContext, IClassFixture App = App with { - Plan = new AssignedPlan(RefToken.User("other"), "premium") + Plan = new AssignedPlan(RefToken.User("other"), "premium"), }; await ValidationAssert.ThrowsAsync(() => GuardApp.CanTransfer(command, App, AppProvider, default), @@ -222,8 +222,8 @@ public class GuardAppTests : GivenContext, IClassFixture { Settings = new AppSettings { - Patterns = null! - } + Patterns = null!, + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -238,8 +238,8 @@ public class GuardAppTests : GivenContext, IClassFixture Settings = new AppSettings { Patterns = ReadonlyList.Create( - new Pattern(null!, "[a-z]")) - } + new Pattern(null!, "[a-z]")), + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -254,8 +254,8 @@ public class GuardAppTests : GivenContext, IClassFixture Settings = new AppSettings { Patterns = ReadonlyList.Create( - new Pattern("name", null!)) - } + new Pattern("name", null!)), + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -269,8 +269,8 @@ public class GuardAppTests : GivenContext, IClassFixture { Settings = new AppSettings { - Editors = null! - } + Editors = null!, + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -285,8 +285,8 @@ public class GuardAppTests : GivenContext, IClassFixture Settings = new AppSettings { Editors = ReadonlyList.Create( - new Editor(null!, "[a-z]")) - } + new Editor(null!, "[a-z]")), + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -301,8 +301,8 @@ public class GuardAppTests : GivenContext, IClassFixture Settings = new AppSettings { Editors = ReadonlyList.Create( - new Editor("name", null!)) - } + new Editor("name", null!)), + }, }; ValidationAssert.Throws(() => GuardApp.CanUpdateSettings(command), @@ -319,8 +319,8 @@ public class GuardAppTests : GivenContext, IClassFixture Patterns = ReadonlyList.Create( new Pattern("name", "[a-z]")), Editors = ReadonlyList.Create( - new Editor("name", "url/to/editor")) - } + new Editor("name", "url/to/editor")), + }, }; GuardApp.CanUpdateSettings(command); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppWorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppWorkflowTests.cs index 3f431d0d2..848dd5d4c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppWorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppWorkflowTests.cs @@ -23,7 +23,7 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture(() => GuardAppWorkflows.CanUpdate(command, App)); @@ -74,9 +74,9 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { - [Status.Published] = new WorkflowStep() + [Status.Published] = new WorkflowStep(), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), @@ -92,9 +92,9 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { - [Status.Published] = new WorkflowStep() + [Status.Published] = new WorkflowStep(), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), @@ -110,9 +110,9 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { - [Status.Draft] = new WorkflowStep() + [Status.Draft] = new WorkflowStep(), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), @@ -129,9 +129,9 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { [Status.Published] = null!, - [Status.Draft] = new WorkflowStep() + [Status.Draft] = new WorkflowStep(), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), @@ -151,11 +151,11 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { - [Status.Archived] = WorkflowTransition.Always + [Status.Archived] = WorkflowTransition.Always, }.ToReadonlyDictionary()), - [Status.Draft] = new WorkflowStep() + [Status.Draft] = new WorkflowStep(), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), @@ -177,10 +177,10 @@ public class GuardAppWorkflowTests : GivenContext, IClassFixture { - [Status.Draft] = null! - }.ToReadonlyDictionary()) + [Status.Draft] = null!, + }.ToReadonlyDictionary()), }.ToReadonlyDictionary()), - WorkflowId = workflowId + WorkflowId = workflowId, }; ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command, App), diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/RestrictAppsCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/RestrictAppsCommandMiddlewareTests.cs index 981de8197..f4fec1eac 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/RestrictAppsCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Plans/RestrictAppsCommandMiddlewareTests.cs @@ -17,7 +17,7 @@ using Squidex.Shared.Users; namespace Squidex.Domain.Apps.Entities.Apps.Plans; -public sealed class RestrictAppsCommandMiddlewareTests : GivenContext +public class RestrictAppsCommandMiddlewareTests : GivenContext { private readonly IUserResolver userResolver = A.Fake(); private readonly ICommandBus commandBus = A.Fake(); @@ -36,7 +36,7 @@ public sealed class RestrictAppsCommandMiddlewareTests : GivenContext var command = new CreateApp { - Actor = RefToken.User(userId) + Actor = RefToken.User(userId), }; var commandContext = new CommandContext(command, commandBus); @@ -73,7 +73,7 @@ public sealed class RestrictAppsCommandMiddlewareTests : GivenContext var command = new CreateApp { - Actor = RefToken.User(userId) + Actor = RefToken.User(userId), }; var commandContext = new CommandContext(command, commandBus); @@ -107,7 +107,7 @@ public sealed class RestrictAppsCommandMiddlewareTests : GivenContext { var command = new CreateApp { - Actor = RefToken.Client(Guid.NewGuid().ToString()) + Actor = RefToken.Client(Guid.NewGuid().ToString()), }; var commandContext = new CommandContext(command, commandBus); @@ -130,7 +130,7 @@ public sealed class RestrictAppsCommandMiddlewareTests : GivenContext { var command = new CreateApp { - Actor = RefToken.User(Guid.NewGuid().ToString()) + Actor = RefToken.User(Guid.NewGuid().ToString()), }; var commandContext = new CommandContext(command, commandBus); @@ -153,7 +153,7 @@ public sealed class RestrictAppsCommandMiddlewareTests : GivenContext { var command = new UpdateApp { - Actor = RefToken.User(Guid.NewGuid().ToString()) + Actor = RefToken.User(Guid.NewGuid().ToString()), }; var commandContext = new CommandContext(command, commandBus); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs index 21aa4f30b..81227bf4c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/RolePermissionsProviderTests.cs @@ -28,7 +28,7 @@ public class RolePermissionsProviderTests : GivenContext .Returns( [ Schema.WithId(DomainId.NewGuid(), "my-schema1"), - Schema.WithId(DomainId.NewGuid(), "my-schema2") + Schema.WithId(DomainId.NewGuid(), "my-schema2"), ]); var actual = await sut.GetPermissionsAsync(App); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesClientTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesClientTests.cs index 75d219ee0..74f5ee1ca 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesClientTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Templates/TemplatesClientTests.cs @@ -33,9 +33,9 @@ public class TemplatesClientTests [ new TemplateRepository { - ContentUrl = "https://raw.githubusercontent.com/Squidex/templates/main" + ContentUrl = "https://raw.githubusercontent.com/Squidex/templates/main", }, - ] + ], })); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs index 170bfc278..64f5438b4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetChangedTriggerHandlerTests.cs @@ -33,7 +33,7 @@ public class AssetChangedTriggerHandlerTests : GivenContext { TestUtils.CreateEvent(), EnrichedAssetEventType.Created }, { TestUtils.CreateEvent(), EnrichedAssetEventType.Updated }, { TestUtils.CreateEvent(), EnrichedAssetEventType.Annotated }, - { TestUtils.CreateEvent(), EnrichedAssetEventType.Deleted } + { TestUtils.CreateEvent(), EnrichedAssetEventType.Deleted }, }; public AssetChangedTriggerHandlerTests() @@ -80,7 +80,7 @@ public class AssetChangedTriggerHandlerTests : GivenContext .Returns(new List { CreateAsset(), - CreateAsset() + CreateAsset(), }.ToAsyncEnumerable()); var actual = await sut.CreateSnapshotEventsAsync(ctx, CancellationToken).ToListAsync(CancellationToken); @@ -155,7 +155,7 @@ public class AssetChangedTriggerHandlerTests : GivenContext { var trigger = new AssetChangedTriggerV2 { - Condition = condition + Condition = condition, }; action(Context(trigger)); @@ -181,7 +181,7 @@ public class AssetChangedTriggerHandlerTests : GivenContext AppId = AppId, IncludeSkipped = false, IncludeStale = false, - Rule = CreateRule() with { Trigger = trigger } + Rule = CreateRule() with { Trigger = trigger }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetUsageTrackerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetUsageTrackerTests.cs index 95e4eb094..41364653e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetUsageTrackerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetUsageTrackerTests.cs @@ -30,7 +30,7 @@ public class AssetUsageTrackerTests : GivenContext { { new AssetCreated { FileSize = 128 }, 128, 1 }, { new AssetUpdated { FileSize = 512 }, 512, 0 }, - { new AssetDeleted { DeletedSize = 512 }, -512, -1 } + { new AssetDeleted { DeletedSize = 512 }, -512, -1 }, }; public AssetUsageTrackerTests() @@ -91,9 +91,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" + "tag2", ], - AssetId = assetId + AssetId = assetId, }; var envelope = @@ -110,7 +110,7 @@ public class AssetUsageTrackerTests : GivenContext update.Should().BeEquivalentTo(new Dictionary { ["tag1"] = 1, - ["tag2"] = 1 + ["tag2"] = 1, }); } @@ -123,9 +123,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" + "tag2", ], - AssetId = assetId + AssetId = assetId, }; var @event2 = new AssetCreated @@ -134,9 +134,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag2", - "tag3" + "tag3", ], - AssetId = assetId + AssetId = assetId, }; var envelope1 = @@ -158,7 +158,7 @@ public class AssetUsageTrackerTests : GivenContext { ["tag1"] = 1, ["tag2"] = 2, - ["tag3"] = 1 + ["tag3"] = 1, }); A.CallTo(() => store.WriteManyAsync(A>>._, default)) @@ -174,9 +174,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" + "tag2", ], - AssetId = assetId + AssetId = assetId, }; var @event2 = new AssetAnnotated @@ -185,9 +185,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag2", - "tag3" + "tag3", ], - AssetId = assetId + AssetId = assetId, }; var envelope1 = @@ -209,7 +209,7 @@ public class AssetUsageTrackerTests : GivenContext { ["tag1"] = 0, ["tag2"] = 1, - ["tag3"] = 1 + ["tag3"] = 1, }); } @@ -222,9 +222,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" + "tag2", ], - AssetId = assetId + AssetId = assetId, }; var @event2 = new AssetAnnotated @@ -233,9 +233,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag2", - "tag3" + "tag3", ], - AssetId = assetId + AssetId = assetId, }; var envelope1 = @@ -258,7 +258,7 @@ public class AssetUsageTrackerTests : GivenContext { ["tag1"] = -1, ["tag2"] = 0, - ["tag3"] = 1 + ["tag3"] = 1, }); } @@ -271,9 +271,9 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" + "tag2", ], - AssetId = assetId + AssetId = assetId, }; var @event2 = new AssetDeleted { AppId = AppId, AssetId = assetId }; @@ -296,7 +296,7 @@ public class AssetUsageTrackerTests : GivenContext update.Should().BeEquivalentTo(new Dictionary { ["tag1"] = 0, - ["tag2"] = 0 + ["tag2"] = 0, }); } @@ -308,8 +308,8 @@ public class AssetUsageTrackerTests : GivenContext Tags = [ "tag1", - "tag2" - ] + "tag2", + ], }; A.CallTo(() => store.ReadAsync(assetKey, default)) @@ -331,7 +331,7 @@ public class AssetUsageTrackerTests : GivenContext update.Should().BeEquivalentTo(new Dictionary { ["tag1"] = -1, - ["tag2"] = -1 + ["tag2"] = -1, }); } @@ -340,7 +340,7 @@ public class AssetUsageTrackerTests : GivenContext { var asset = CreateAsset() with { - Tags = ["tag1", "tag2"] + Tags = ["tag1", "tag2"], }; A.CallTo(() => assetLoader.GetAsync(AppId.Id, assetId, 41, default)) @@ -363,7 +363,7 @@ public class AssetUsageTrackerTests : GivenContext update.Should().BeEquivalentTo(new Dictionary { ["tag1"] = -1, - ["tag2"] = -1 + ["tag2"] = -1, }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs index 0896a9500..9d4fafe94 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsFluidExtensionTests.cs @@ -42,7 +42,7 @@ public class AssetsFluidExtensionTests : GivenContext var extensions = new IFluidExtension[] { new ContentFluidExtension(), - new AssetsFluidExtension(serviceProvider) + new AssetsFluidExtension(serviceProvider), }; sut = new FluidTemplateEngine(extensions); @@ -169,14 +169,14 @@ public class AssetsFluidExtensionTests : GivenContext Id = DomainId.NewGuid(), FileVersion = 0, FileSize = 100, - AppId = AppId + AppId = AppId, }; SetupText(@event.ToRef(), Encode(encoding, "hello+assets")); var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; var template = $@" @@ -266,14 +266,14 @@ public class AssetsFluidExtensionTests : GivenContext AssetType = AssetType.Image, FileVersion = 0, FileSize = 100, - AppId = AppId + AppId = AppId, }; SetupBlurHash(@event.ToRef(), "Hash"); var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; var template = @" @@ -313,7 +313,7 @@ public class AssetsFluidExtensionTests : GivenContext .AddField("assets", new ContentFieldData() .AddInvariant(JsonValue.Array(assetId))), - AppId = AppId + AppId = AppId, }; A.CallTo(() => assetQuery.FindAsync(A._, assetId, false, EtagVersion.Any, A._)) @@ -321,7 +321,7 @@ public class AssetsFluidExtensionTests : GivenContext var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; return (vars, asset); @@ -341,7 +341,7 @@ public class AssetsFluidExtensionTests : GivenContext .AddField("assets", new ContentFieldData() .AddInvariant(JsonValue.Array(assetId1, assetId2))), - AppId = AppId + AppId = AppId, }; A.CallTo(() => assetQuery.FindAsync(A._, assetId1, false, EtagVersion.Any, A._)) @@ -352,7 +352,7 @@ public class AssetsFluidExtensionTests : GivenContext var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; return (vars, new[] { asset1, asset2 }); @@ -367,7 +367,7 @@ public class AssetsFluidExtensionTests : GivenContext FileSize = fileSize, FileName = $"file{index}.jpg", MimeType = "image/jpg", - Type = type + Type = type, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs index cf7ef0751..54d588b1f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetsJintExtensionTests.cs @@ -49,14 +49,14 @@ public class AssetsJintExtensionTests : GivenContext, IClassFixture { - ["tag1"] = "new-name" + ["tag1"] = "new-name", }, - Tags = [] + Tags = [], }; var context = CreateBackupContext(); @@ -96,7 +96,7 @@ public class BackupAssetsTests : GivenContext var envelope = new Envelope(new AppCreated { - AppId = AppId + AppId = AppId, }); A.CallTo(() => context.Reader.HasFileAsync(A._, CancellationToken)) @@ -121,7 +121,7 @@ public class BackupAssetsTests : GivenContext var envelope = new Envelope(new AppCreated { - AppId = AppId + AppId = AppId, }); A.CallTo(() => context.Reader.HasFileAsync(A._, CancellationToken)) @@ -146,7 +146,7 @@ public class BackupAssetsTests : GivenContext var envelope = new Envelope(new AppCreated { - AppId = AppId + AppId = AppId, }); A.CallTo(() => context.Reader.HasFileAsync(A._, CancellationToken)) @@ -305,17 +305,17 @@ public class BackupAssetsTests : GivenContext await sut.RestoreEventAsync(AppEvent(new AssetCreated { - AssetId = assetId1 + AssetId = assetId1, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new AssetCreated { - AssetId = assetId2 + AssetId = assetId2, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new AssetDeleted { - AssetId = assetId2 + AssetId = assetId2, }), context, CancellationToken); var rebuildAssets = new HashSet(); @@ -328,7 +328,7 @@ public class BackupAssetsTests : GivenContext Assert.Equal( [ DomainId.Combine(AppId, assetId1), - DomainId.Combine(AppId, assetId2) + DomainId.Combine(AppId, assetId2), ], rebuildAssets); } @@ -342,17 +342,17 @@ public class BackupAssetsTests : GivenContext await sut.RestoreEventAsync(AppEvent(new AssetFolderCreated { - AssetFolderId = assetFolderId1 + AssetFolderId = assetFolderId1, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new AssetFolderCreated { - AssetFolderId = assetFolderId2 + AssetFolderId = assetFolderId2, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new AssetFolderDeleted { - AssetFolderId = assetFolderId2 + AssetFolderId = assetFolderId2, }), context, CancellationToken); var rebuildAssetFolders = new HashSet(); @@ -365,7 +365,7 @@ public class BackupAssetsTests : GivenContext Assert.Equal( [ DomainId.Combine(AppId, assetFolderId1), - DomainId.Combine(AppId, assetFolderId2) + DomainId.Combine(AppId, assetFolderId2), ], rebuildAssetFolders); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DefaultAssetFileStoreTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DefaultAssetFileStoreTests.cs index 21e433d6b..5983addd6 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DefaultAssetFileStoreTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DefaultAssetFileStoreTests.cs @@ -28,13 +28,13 @@ public class DefaultAssetFileStoreTests : GivenContext { true, "resize=100", "{appId}/{assetId}_{assetFileVersion}_resize=100" }, { true, string.Empty, "{appId}/{assetId}_{assetFileVersion}" }, { false, "resize=100", "{appId}_{assetId}_{assetFileVersion}_resize=100" }, - { false, string.Empty, "{appId}_{assetId}_{assetFileVersion}" } + { false, string.Empty, "{appId}_{assetId}_{assetFileVersion}" }, }; public static readonly TheoryData PathCasesOld = new TheoryData { { "resize=100", "{assetId}_{assetFileVersion}_resize=100" }, - { string.Empty, "{assetId}_{assetFileVersion}" } + { string.Empty, "{assetId}_{assetFileVersion}" }, }; public DefaultAssetFileStoreTests() diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs index c8475815c..e18ba7da8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetCommandMiddlewareTests.cs @@ -225,7 +225,7 @@ public class AssetCommandMiddlewareTests : HandlerTestBase duplicate = CreateAsset() with { FileName = fileName, - FileSize = fileSize + FileSize = fileSize, }; A.CallTo(() => assetQuery.FindByHashAsync(ApiContext, A._, fileName, fileSize, CancellationToken)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs index fcade70c4..d44216c04 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetDomainObjectTests.cs @@ -46,8 +46,8 @@ public class AssetDomainObjectTests : HandlerTestBase Create = "", Delete = "", Move = "", - Update = "" - } + Update = "", + }, }; A.CallTo(() => assetQuery.FindAssetFolderAsync(AppId.Id, parentId, A._)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetsBulkUpdateCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetsBulkUpdateCommandMiddlewareTests.cs index 0b358188f..d37c2148e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetsBulkUpdateCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/AssetsBulkUpdateCommandMiddlewareTests.cs @@ -173,9 +173,9 @@ public class AssetsBulkUpdateCommandMiddlewareTests : GivenContext { Type = type, Id = id, - FileName = fileName + FileName = fileName, }, - ] + ], }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/GuardAssetFolderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/GuardAssetFolderTests.cs index 1a72c31bc..3034129d8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/GuardAssetFolderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/GuardAssetFolderTests.cs @@ -102,7 +102,7 @@ public class GuardAssetFolderTests : GivenContext, IClassFixture { CreateAssetFolder().WithId(operation.CommandId), - CreateAssetFolder().WithId(parentId) with { ParentId = operation.CommandId } + CreateAssetFolder().WithId(parentId) with { ParentId = operation.CommandId }, }); await ValidationAssert.ThrowsAsync(() => operation.MustMoveToValidFolder(parentId, CancellationToken), @@ -125,7 +125,7 @@ public class GuardAssetFolderTests : GivenContext, IClassFixture { App = App, CommandId = asset.Id, - Command = new CreateAsset { User = currentUser, Actor = User } + Command = new CreateAsset { User = currentUser, Actor = User }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/ScriptingExtensionsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/ScriptingExtensionsTests.cs index a3d2eeba6..6c14e04df 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/ScriptingExtensionsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/DomainObject/Guards/ScriptingExtensionsTests.cs @@ -16,7 +16,7 @@ using Squidex.Infrastructure.Validation; namespace Squidex.Domain.Apps.Entities.Assets.DomainObject.Guards; -public sealed class ScriptingExtensionsTests : GivenContext +public class ScriptingExtensionsTests : GivenContext { [Fact] public async Task Should_add_tag_in_script() @@ -76,8 +76,8 @@ public sealed class ScriptingExtensionsTests : GivenContext { AssetScripts = new AssetScripts { - Annotate = script - } + Annotate = script, + }, }; var serviceProvider = @@ -95,7 +95,7 @@ public sealed class ScriptingExtensionsTests : GivenContext { App = App, CommandId = asset.Id, - Command = command + Command = command, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTagAssetMetadataSourceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTagAssetMetadataSourceTests.cs index 34e2b7b00..6d51e6160 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTagAssetMetadataSourceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTagAssetMetadataSourceTests.cs @@ -92,9 +92,9 @@ public class FileTagAssetMetadataSourceTests : GivenContext { [KnownMetadataKeys.VideoWidth] = JsonValue.Create(128), [KnownMetadataKeys.VideoHeight] = JsonValue.Create(55), - [KnownMetadataKeys.Duration] = JsonValue.Create("00:10:12") + [KnownMetadataKeys.Duration] = JsonValue.Create("00:10:12"), }, - Type = AssetType.Video + Type = AssetType.Video, }; var formatted = sut.Format(source); @@ -109,9 +109,9 @@ public class FileTagAssetMetadataSourceTests : GivenContext { Metadata = new AssetMetadata { - [KnownMetadataKeys.Duration] = JsonValue.Create("00:10:12") + [KnownMetadataKeys.Duration] = JsonValue.Create("00:10:12"), }, - Type = AssetType.Audio + Type = AssetType.Audio, }; var formatted = sut.Format(source); @@ -124,7 +124,7 @@ public class FileTagAssetMetadataSourceTests : GivenContext { var source = CreateAsset() with { - Type = AssetType.Image + Type = AssetType.Image, }; var formatted = sut.Format(source); @@ -138,7 +138,7 @@ public class FileTagAssetMetadataSourceTests : GivenContext return new CreateAsset { - File = new DelegateAssetFile(file.Name, "mime", file.Length, file.OpenRead) + File = new DelegateAssetFile(file.Name, "mime", file.Length, file.OpenRead), }; } @@ -148,7 +148,7 @@ public class FileTagAssetMetadataSourceTests : GivenContext return new CreateAsset { - File = new DelegateAssetFile(name, "mime", stream.Length, () => stream) + File = new DelegateAssetFile(name, "mime", stream.Length, () => stream), }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTypeAssetMetadataSourceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTypeAssetMetadataSourceTests.cs index 408d91365..e66f84165 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTypeAssetMetadataSourceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/FileTypeAssetMetadataSourceTests.cs @@ -29,7 +29,7 @@ public class FileTypeAssetMetadataSourceTests : GivenContext { var command = new CreateAsset { - File = new NoopAssetFile("File.DOCX") + File = new NoopAssetFile("File.DOCX"), }; await sut.EnhanceAsync(command, default); @@ -42,7 +42,7 @@ public class FileTypeAssetMetadataSourceTests : GivenContext { var command = new CreateAsset { - File = new NoopAssetFile("File") + File = new NoopAssetFile("File"), }; await sut.EnhanceAsync(command, default); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/ImageAssetMetadataSourceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/ImageAssetMetadataSourceTests.cs index e20168037..128eb3571 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/ImageAssetMetadataSourceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/ImageAssetMetadataSourceTests.cs @@ -160,9 +160,9 @@ public class ImageAssetMetadataSourceTests : GivenContext Metadata = new AssetMetadata { [KnownMetadataKeys.PixelWidth] = 128, - [KnownMetadataKeys.PixelHeight] = 55 + [KnownMetadataKeys.PixelHeight] = 55, }, - Type = AssetType.Image + Type = AssetType.Image, }; var formatted = sut.Format(source); @@ -175,7 +175,7 @@ public class ImageAssetMetadataSourceTests : GivenContext { var source = CreateAsset() with { - Type = AssetType.Video + Type = AssetType.Video, }; var formatted = sut.Format(source); @@ -188,7 +188,7 @@ public class ImageAssetMetadataSourceTests : GivenContext { var source = CreateAsset() with { - Type = AssetType.Audio + Type = AssetType.Audio, }; var formatted = sut.Format(source); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ConvertTagsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ConvertTagsTests.cs index f5f2969d1..d44a09d0c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ConvertTagsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ConvertTagsTests.cs @@ -48,14 +48,14 @@ public class ConvertTagsTests : GivenContext { var asset = CreateAsset() with { - Tags = ["id1", "id2"] + Tags = ["id1", "id2"], }; A.CallTo(() => tagService.GetTagNamesAsync(AppId.Id, TagGroups.Assets, A>.That.Is("id1", "id2"), CancellationToken)) .Returns(new Dictionary { ["id1"] = "name1", - ["id2"] = "name2" + ["id2"] = "name2", }); await sut.EnrichAsync(ApiContext, Enumerable.Repeat(asset, 1), CancellationToken); @@ -68,12 +68,12 @@ public class ConvertTagsTests : GivenContext { var asset1 = CreateAsset() with { - Tags = ["id1", "id2"] + Tags = ["id1", "id2"], }; var asset2 = CreateAsset() with { - Tags = ["id2", "id3"] + Tags = ["id2", "id3"], }; A.CallTo(() => tagService.GetTagNamesAsync(AppId.Id, TagGroups.Assets, A>.That.Is("id1", "id2", "id3"), CancellationToken)) @@ -81,7 +81,7 @@ public class ConvertTagsTests : GivenContext { ["id1"] = "name1", ["id2"] = "name2", - ["id3"] = "name3" + ["id3"] = "name3", }); await sut.EnrichAsync(ApiContext, [asset1, asset2], CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs index 7162ec246..ecba8a159 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichForCachingTests.cs @@ -42,7 +42,7 @@ public class EnrichForCachingTests : GivenContext "X-NoResolveLanguages", "X-ResolveFlow", "X-ResolveUrls", - "X-Unpublished" + "X-Unpublished", ], headers); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichWithMetadataTextTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichWithMetadataTextTests.cs index ffdf24594..630603d72 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichWithMetadataTextTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/EnrichWithMetadataTextTests.cs @@ -22,7 +22,7 @@ public class EnrichWithMetadataTextTests : GivenContext var assetMetadataSources = new[] { assetMetadataSource1, - assetMetadataSource2 + assetMetadataSource2, }; sut = new EnrichWithMetadataText(assetMetadataSources); @@ -51,8 +51,8 @@ public class EnrichWithMetadataTextTests : GivenContext Tags = [ "id1", - "id2" - ] + "id2", + ], }; A.CallTo(() => assetMetadataSource1.Format(asset)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ScriptAssetTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ScriptAssetTests.cs index 185485db0..403398fa4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ScriptAssetTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/Queries/ScriptAssetTests.cs @@ -120,8 +120,8 @@ public class ScriptAssetTests : GivenContext AssetScripts = new AssetScripts { Query = query, - QueryPre = queryPre - } + QueryPre = queryPre, + }, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs index 0da40c77a..162fd60e4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/RepairFilesTests.cs @@ -109,7 +109,7 @@ public class RepairFilesTests : GivenContext var storedEvents = new List { - storedEvent + storedEvent, }; if (@event != null) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/SvgAssetMetadataSourceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/SvgAssetMetadataSourceTests.cs index a4c24b55c..326eedc28 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/SvgAssetMetadataSourceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/SvgAssetMetadataSourceTests.cs @@ -112,9 +112,9 @@ public class SvgAssetMetadataSourceTests : GivenContext Metadata = new AssetMetadata { [KnownMetadataKeys.SvgWidth] = "128", - [KnownMetadataKeys.SvgHeight] = "55" + [KnownMetadataKeys.SvgHeight] = "55", }, - MimeType = "image/svg+xml" + MimeType = "image/svg+xml", }; var formatted = sut.Format(source); @@ -130,9 +130,9 @@ public class SvgAssetMetadataSourceTests : GivenContext Metadata = new AssetMetadata { [KnownMetadataKeys.SvgWidth] = 128, - [KnownMetadataKeys.SvgHeight] = 55 + [KnownMetadataKeys.SvgHeight] = 55, }, - MimeType = "image/svg+xml" + MimeType = "image/svg+xml", }; var formatted = sut.Format(source); @@ -146,7 +146,7 @@ public class SvgAssetMetadataSourceTests : GivenContext return new CreateAsset { - File = new DelegateAssetFile(file.Name, "mime", file.Length, file.OpenRead) + File = new DelegateAssetFile(file.Name, "mime", file.Length, file.OpenRead), }; } @@ -156,7 +156,7 @@ public class SvgAssetMetadataSourceTests : GivenContext return new CreateAsset { - File = new DelegateAssetFile(name, "mime", stream.Length, () => stream) + File = new DelegateAssetFile(name, "mime", stream.Length, () => stream), }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs index 65b36560b..4d92e3b6d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Backup/UserMappingTests.cs @@ -36,7 +36,7 @@ public class UserMappingTests : GivenContext var users = new Dictionary { [user1.Id] = user1, - [user2.Id] = user2 + [user2.Id] = user2, }; var userResolver = A.Fake(); @@ -56,7 +56,7 @@ public class UserMappingTests : GivenContext Assert.Equal(new Dictionary { [user1.Id] = user1.Email, - [user2.Id] = user2.Email + [user2.Id] = user2.Email, }, storedUsers); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/ConfigPlansProviderTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/ConfigPlansProviderTests.cs index 02a782038..96747fe9c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/ConfigPlansProviderTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/ConfigPlansProviderTests.cs @@ -16,7 +16,7 @@ public class ConfigPlansProviderTests MaxApiCalls = -1, MaxAssetSize = -1, MaxContributors = -1, - BlockingApiCalls = -1 + BlockingApiCalls = -1, }; private static readonly Plan FreePlan = new Plan @@ -27,7 +27,7 @@ public class ConfigPlansProviderTests MaxAssetSize = 1024 * 1024 * 10, MaxContributors = 2, BlockingApiCalls = 50000, - IsFree = true + IsFree = true, }; private static readonly Plan BasicPlan = new Plan @@ -40,7 +40,7 @@ public class ConfigPlansProviderTests YearlyCosts = "100€", YearlyId = "basic_yearly", BlockingApiCalls = 150000, - IsFree = false + IsFree = false, }; private static readonly Plan[] Plans = [BasicPlan, FreePlan]; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs index db36bc250..26ac42896 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageGateTests.cs @@ -32,7 +32,7 @@ public class UsageGateTests : GivenContext { App = App with { - TeamId = default + TeamId = default, }; A.CallTo(() => billingPlans.GetActualPlan(A._)) @@ -87,7 +87,7 @@ public class UsageGateTests : GivenContext { App = App with { - TeamId = TeamId + TeamId = TeamId, }; var plan = await sut.GetPlanForAppAsync(App, false, CancellationToken); @@ -100,7 +100,7 @@ public class UsageGateTests : GivenContext { App = App with { - Plan = new AssignedPlan(User, planPaid.Id) + Plan = new AssignedPlan(User, planPaid.Id), }; var plan = await sut.GetPlanForAppAsync(App, false, CancellationToken); @@ -113,7 +113,7 @@ public class UsageGateTests : GivenContext { App = App with { - Plan = new AssignedPlan(User, planPaid.Id) + Plan = new AssignedPlan(User, planPaid.Id), }; var plan = await sut.GetPlanForAppAsync(AppId.Id, false, CancellationToken); @@ -127,7 +127,7 @@ public class UsageGateTests : GivenContext App = App with { Plan = new AssignedPlan(User, planPaid.Id), - TeamId = TeamId + TeamId = TeamId, }; var plan = await sut.GetPlanForAppAsync(App, false, CancellationToken); @@ -140,7 +140,7 @@ public class UsageGateTests : GivenContext { App = App with { - Clients = AppClients.Empty.Add(clientId, clientId).Update(clientId, apiCallsLimit: 1000) + Clients = AppClients.Empty.Add(clientId, clientId).Update(clientId, apiCallsLimit: 1000), }; var plan = new Plan { Id = "custom", BlockingApiCalls = 1600, MaxApiCalls = 1600 }; @@ -287,7 +287,7 @@ public class UsageGateTests : GivenContext { App = App with { - TeamId = TeamId + TeamId = TeamId, }; await sut.TrackRequestAsync(App, "client", today, 42, 50, 512, CancellationToken); @@ -316,7 +316,7 @@ public class UsageGateTests : GivenContext { [UsageGate.RulesKeys.TotalCreated] = 100, [UsageGate.RulesKeys.TotalSucceeded] = 120, - [UsageGate.RulesKeys.TotalFailed] = 140 + [UsageGate.RulesKeys.TotalFailed] = 140, }; countersSummary.Should().BeEquivalentTo(expected); @@ -328,7 +328,7 @@ public class UsageGateTests : GivenContext { App = App with { - TeamId = TeamId + TeamId = TeamId, }; Counters? countersSummary = null; @@ -348,7 +348,7 @@ public class UsageGateTests : GivenContext { [UsageGate.RulesKeys.TotalCreated] = 100, [UsageGate.RulesKeys.TotalSucceeded] = 120, - [UsageGate.RulesKeys.TotalFailed] = 140 + [UsageGate.RulesKeys.TotalFailed] = 140, }; countersSummary.Should().BeEquivalentTo(expected); @@ -368,16 +368,16 @@ public class UsageGateTests : GivenContext { [UsageGate.RulesKeys.TotalCreated] = 100, [UsageGate.RulesKeys.TotalSucceeded] = 120, - [UsageGate.RulesKeys.TotalFailed] = 140 - }) - ] + [UsageGate.RulesKeys.TotalFailed] = 140, + }), + ], }); var total = await ((IRuleUsageTracker)sut).GetTotalByAppAsync(AppId.Id, CancellationToken); total.Should().BeEquivalentTo(new Dictionary { - [AppId.Id] = new RuleCounters(100, 120, 140) + [AppId.Id] = new RuleCounters(100, 120, 140), }); } @@ -392,7 +392,7 @@ public class UsageGateTests : GivenContext { new RuleStats(today.AddDays(0), new RuleCounters(100, 120, 140)), new RuleStats(today.AddDays(1), new RuleCounters(200, 220, 240)), - new RuleStats(today.AddDays(2), new RuleCounters(300, 320, 340)) + new RuleStats(today.AddDays(2), new RuleCounters(300, 320, 340)), }); } @@ -407,7 +407,7 @@ public class UsageGateTests : GivenContext { new RuleStats(today.AddDays(0), new RuleCounters(100, 120, 140)), new RuleStats(today.AddDays(1), new RuleCounters(200, 220, 240)), - new RuleStats(today.AddDays(2), new RuleCounters(300, 320, 340)) + new RuleStats(today.AddDays(2), new RuleCounters(300, 320, 340)), }); } @@ -422,20 +422,20 @@ public class UsageGateTests : GivenContext { [UsageGate.RulesKeys.TotalCreated] = 50, [UsageGate.RulesKeys.TotalSucceeded] = 60, - [UsageGate.RulesKeys.TotalFailed] = 70 + [UsageGate.RulesKeys.TotalFailed] = 70, }), (today.AddDays(1), new Counters { [UsageGate.RulesKeys.TotalCreated] = 200, [UsageGate.RulesKeys.TotalSucceeded] = 220, - [UsageGate.RulesKeys.TotalFailed] = 240 + [UsageGate.RulesKeys.TotalFailed] = 240, }), (today.AddDays(2), new Counters { [UsageGate.RulesKeys.TotalCreated] = 300, [UsageGate.RulesKeys.TotalSucceeded] = 320, - [UsageGate.RulesKeys.TotalFailed] = 340 - }) + [UsageGate.RulesKeys.TotalFailed] = 340, + }), ], ["Custom"] = [ @@ -443,9 +443,9 @@ public class UsageGateTests : GivenContext { [UsageGate.RulesKeys.TotalCreated] = 50, [UsageGate.RulesKeys.TotalSucceeded] = 60, - [UsageGate.RulesKeys.TotalFailed] = 70 - }) - ] + [UsageGate.RulesKeys.TotalFailed] = 70, + }), + ], }); } @@ -466,7 +466,7 @@ public class UsageGateTests : GivenContext var expected = new Counters { [UsageGate.AssetsKeys.TotalSize] = 512, - [UsageGate.AssetsKeys.TotalAssets] = 3 + [UsageGate.AssetsKeys.TotalAssets] = 3, }; countersSummary.Should().BeEquivalentTo(expected); @@ -478,7 +478,7 @@ public class UsageGateTests : GivenContext { App = App with { - TeamId = TeamId + TeamId = TeamId, }; Counters? countersSummary = null; @@ -495,7 +495,7 @@ public class UsageGateTests : GivenContext var expected = new Counters { [UsageGate.AssetsKeys.TotalSize] = 512, - [UsageGate.AssetsKeys.TotalAssets] = 3 + [UsageGate.AssetsKeys.TotalAssets] = 3, }; countersSummary.Should().BeEquivalentTo(expected); @@ -509,7 +509,7 @@ public class UsageGateTests : GivenContext .Returns(new Counters { [UsageGate.AssetsKeys.TotalSize] = 2048, - [UsageGate.AssetsKeys.TotalAssets] = 124 + [UsageGate.AssetsKeys.TotalAssets] = 124, }); var total = await ((IAssetUsageTracker)sut).GetTotalByAppAsync(AppId.Id, CancellationToken); @@ -524,7 +524,7 @@ public class UsageGateTests : GivenContext .Returns(new Counters { [UsageGate.AssetsKeys.TotalSize] = 2048, - [UsageGate.AssetsKeys.TotalAssets] = 124 + [UsageGate.AssetsKeys.TotalAssets] = 124, }); var total = await ((IAssetUsageTracker)sut).GetTotalByTeamAsync(AppId.Id, CancellationToken); @@ -543,7 +543,7 @@ public class UsageGateTests : GivenContext { new AssetStats(today.AddDays(0), new AssetCounters(128, 2)), new AssetStats(today.AddDays(1), new AssetCounters(256, 3)), - new AssetStats(today.AddDays(2), new AssetCounters(512, 4)) + new AssetStats(today.AddDays(2), new AssetCounters(512, 4)), }); } @@ -558,7 +558,7 @@ public class UsageGateTests : GivenContext { new AssetStats(today.AddDays(0), new AssetCounters(128, 2)), new AssetStats(today.AddDays(1), new AssetCounters(256, 3)), - new AssetStats(today.AddDays(2), new AssetCounters(512, 4)) + new AssetStats(today.AddDays(2), new AssetCounters(512, 4)), }); } @@ -572,27 +572,27 @@ public class UsageGateTests : GivenContext (today.AddDays(0), new Counters { [UsageGate.AssetsKeys.TotalSize] = 64, - [UsageGate.AssetsKeys.TotalAssets] = 1 + [UsageGate.AssetsKeys.TotalAssets] = 1, }), (today.AddDays(1), new Counters { [UsageGate.AssetsKeys.TotalSize] = 256, - [UsageGate.AssetsKeys.TotalAssets] = 3 + [UsageGate.AssetsKeys.TotalAssets] = 3, }), (today.AddDays(2), new Counters { [UsageGate.AssetsKeys.TotalSize] = 512, - [UsageGate.AssetsKeys.TotalAssets] = 4 - }) + [UsageGate.AssetsKeys.TotalAssets] = 4, + }), ], ["Custom"] = [ (today.AddDays(0), new Counters { [UsageGate.AssetsKeys.TotalSize] = 64, - [UsageGate.AssetsKeys.TotalAssets] = 1 - }) - ] + [UsageGate.AssetsKeys.TotalAssets] = 1, + }), + ], }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageNotifierWorkerTest.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageNotifierWorkerTest.cs index 254a9272a..c41628258 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageNotifierWorkerTest.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Billing/UsageNotifierWorkerTest.cs @@ -35,7 +35,7 @@ public class UsageNotifierWorkerTest : GivenContext sut = new UsageNotifierWorker(state.PersistenceFactory, AppProvider, notificationSender, userResolver) { - Clock = clock + Clock = clock, }; } @@ -59,7 +59,7 @@ public class UsageNotifierWorkerTest : GivenContext AppId = AppId.Id, Usage = 1000, UsageLimit = 3000, - Users = ["1", "2"] + Users = ["1", "2"], }; await sut.HandleAsync(message, default); @@ -80,7 +80,7 @@ public class UsageNotifierWorkerTest : GivenContext AppId = AppId.Id, Usage = 1000, UsageLimit = 3000, - Users = ["1", "2", "3"] + Users = ["1", "2", "3"], }; await sut.HandleAsync(message, default); @@ -105,7 +105,7 @@ public class UsageNotifierWorkerTest : GivenContext AppId = AppId.Id, Usage = 1000, UsageLimit = 3000, - Users = ["1"] + Users = ["1"], }; await sut.HandleAsync(message, default); @@ -125,7 +125,7 @@ public class UsageNotifierWorkerTest : GivenContext AppId = AppId.Id, Usage = 1000, UsageLimit = 3000, - Users = ["1"] + Users = ["1"], }; await sut.HandleAsync(message, default); @@ -153,7 +153,7 @@ public class UsageNotifierWorkerTest : GivenContext AppId = AppId.Id, Usage = 1000, UsageLimit = 3000, - Users = ["1"] + Users = ["1"], }; await sut.HandleAsync(message, default); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentCollaborationHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentCollaborationHandlerTests.cs index 0d3ef4b07..99f3fccc0 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentCollaborationHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentCollaborationHandlerTests.cs @@ -202,7 +202,7 @@ public class CommentCollaborationHandlerTests : GivenContext CommentId = default, CommentsId = commentsId, Text = commentItem.Text, - Mentions = ["id1", "id2"] + Mentions = ["id1", "id2"], }, opts => opts.Excluding(x => x.CommentId)); } @@ -227,7 +227,7 @@ public class CommentCollaborationHandlerTests : GivenContext CommentId = default, CommentsId = commentsId, Text = commentItem.Text, - Mentions = ["id1", "id2"] + Mentions = ["id1", "id2"], }, opts => opts.Excluding(x => x.CommentId)); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentTriggerHandlerTests.cs index 4e7625a22..c1e4fb1fd 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Collaboration/CommentTriggerHandlerTests.cs @@ -198,7 +198,7 @@ public class CommentTriggerHandlerTests : GivenContext { var @event = new EnrichedCommentEvent { - MentionedUser = UserMocks.User("1", "1@email.com") + MentionedUser = UserMocks.User("1", "1@email.com"), }; var actual = handler.Trigger(@event, ctx.Rule.Trigger); @@ -214,7 +214,7 @@ public class CommentTriggerHandlerTests : GivenContext { var @event = new EnrichedCommentEvent { - MentionedUser = UserMocks.User("1", "1@email.com") + MentionedUser = UserMocks.User("1", "1@email.com"), }; var actual = handler.Trigger(@event, ctx.Rule.Trigger); @@ -230,7 +230,7 @@ public class CommentTriggerHandlerTests : GivenContext { var @event = new EnrichedCommentEvent { - Text = "very_urgent_text" + Text = "very_urgent_text", }; var actual = handler.Trigger(@event, ctx.Rule.Trigger); @@ -246,7 +246,7 @@ public class CommentTriggerHandlerTests : GivenContext { var @event = new EnrichedCommentEvent { - Text = "just_gossip" + Text = "just_gossip", }; var actual = handler.Trigger(@event, ctx.Rule.Trigger); @@ -259,7 +259,7 @@ public class CommentTriggerHandlerTests : GivenContext { var trigger = new CommentTrigger { - Condition = condition + Condition = condition, }; var realScriptEngine = @@ -267,7 +267,7 @@ public class CommentTriggerHandlerTests : GivenContext Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), })); var handler = new CommentTriggerHandler(realScriptEngine, userResolver); @@ -279,7 +279,7 @@ public class CommentTriggerHandlerTests : GivenContext { var trigger = new CommentTrigger { - Condition = condition + Condition = condition, }; action(Context(trigger)); @@ -305,7 +305,7 @@ public class CommentTriggerHandlerTests : GivenContext AppId = AppId, IncludeSkipped = false, IncludeStale = false, - Rule = CreateRule() with { Trigger = trigger } + Rule = CreateRule() with { Trigger = trigger }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BackupContentsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BackupContentsTests.cs index 62ab4509d..0b29c3efa 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BackupContentsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/BackupContentsTests.cs @@ -57,7 +57,7 @@ public class BackupContentsTests : GivenContext await sut.BackupEventAsync(Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }), context, CancellationToken); A.CallTo(() => writer.WriteJsonAsync(A._, @@ -90,7 +90,7 @@ public class BackupContentsTests : GivenContext .Returns(new BackupContents.Urls { Assets = oldAssetsUrl, - AssetsApp = oldAssetsUrlApp + AssetsApp = oldAssetsUrlApp, }); var data = @@ -131,12 +131,12 @@ public class BackupContentsTests : GivenContext await sut.RestoreEventAsync(Envelope.Create(new AppCreated { - Name = AppId.Name + Name = AppId.Name, }), context, CancellationToken); await sut.RestoreEventAsync(Envelope.Create(new ContentUpdated { - Data = data + Data = data, }), context, CancellationToken); Assert.Equal(updateData, data); @@ -159,30 +159,30 @@ public class BackupContentsTests : GivenContext await sut.RestoreEventAsync(ContentEvent(new ContentCreated { ContentId = contentId1, - SchemaId = schemaId1 + SchemaId = schemaId1, }), context, CancellationToken); await sut.RestoreEventAsync(ContentEvent(new ContentCreated { ContentId = contentId2, - SchemaId = schemaId1 + SchemaId = schemaId1, }), context, CancellationToken); await sut.RestoreEventAsync(ContentEvent(new ContentCreated { ContentId = contentId3, - SchemaId = schemaId2 + SchemaId = schemaId2, }), context, CancellationToken); await sut.RestoreEventAsync(ContentEvent(new ContentDeleted { ContentId = contentId2, - SchemaId = schemaId1 + SchemaId = schemaId1, }), context, CancellationToken); await sut.RestoreEventAsync(Envelope.Create(new SchemaDeleted { - SchemaId = schemaId2 + SchemaId = schemaId2, }), context, CancellationToken); var rebuildContents = new HashSet(); @@ -195,7 +195,7 @@ public class BackupContentsTests : GivenContext Assert.Equal( [ DomainId.Combine(AppId, contentId1), - DomainId.Combine(AppId, contentId2) + DomainId.Combine(AppId, contentId2), ], rebuildContents); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs index 87a5abb76..a26d4b37c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs @@ -40,7 +40,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext { TestUtils.CreateEvent(), EnrichedContentEventType.Deleted }, { TestUtils.CreateEvent(x => x.Change = StatusChange.Change), EnrichedContentEventType.StatusChanged }, { TestUtils.CreateEvent(x => x.Change = StatusChange.Published), EnrichedContentEventType.Published }, - { TestUtils.CreateEvent(x => x.Change = StatusChange.Unpublished), EnrichedContentEventType.Unpublished } + { TestUtils.CreateEvent(x => x.Change = StatusChange.Unpublished), EnrichedContentEventType.Unpublished }, }; public ContentChangedTriggerHandlerTests() @@ -129,7 +129,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext .Returns(new List { CreateContent() with { SchemaId = schemaMatching }, - CreateContent() with { SchemaId = schemaNotMatching } + CreateContent() with { SchemaId = schemaNotMatching }, }.ToAsyncEnumerable()); var actual = await sut.CreateSnapshotEventsAsync(ctx, CancellationToken).ToListAsync(CancellationToken); @@ -151,8 +151,8 @@ public class ContentChangedTriggerHandlerTests : GivenContext Schemas = ReadonlyList.Create( new SchemaCondition { - SchemaId = schemaMatching.Id - }) + SchemaId = schemaMatching.Id, + }), }; var ctx = Context(trigger); @@ -161,7 +161,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext .Returns(new List { CreateContent() with { SchemaId = schemaMatching }, - CreateContent() with { SchemaId = schemaMatching } + CreateContent() with { SchemaId = schemaMatching }, }.ToAsyncEnumerable()); var actual = await sut.CreateSnapshotEventsAsync(ctx, CancellationToken).ToListAsync(CancellationToken); @@ -235,7 +235,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext .Returns(new List { new Content { SchemaId = schemaMatching }, - new Content { SchemaId = schemaMatching } + new Content { SchemaId = schemaMatching }, }.ToAsyncEnumerable()); var actual = await sut.CreateEnrichedEventsAsync(envelope, ctx, CancellationToken).ToListAsync(CancellationToken); @@ -457,7 +457,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext { var trigger = new ContentChangedTriggerV2 { - HandleAll = handleAll + HandleAll = handleAll, }; if (schemaId != null) @@ -468,8 +468,8 @@ public class ContentChangedTriggerHandlerTests : GivenContext new SchemaCondition { SchemaId = schemaId.Id, - Condition = condition - }) + Condition = condition, + }), }; } @@ -510,8 +510,8 @@ public class ContentChangedTriggerHandlerTests : GivenContext new SchemaCondition { SchemaId = schemaId.Id, - } - }.ToReadonlyList() + }, + }.ToReadonlyList(), }; return new RulesContext @@ -522,9 +522,9 @@ public class ContentChangedTriggerHandlerTests : GivenContext IncludeStale = true, Rules = new Dictionary { - [DomainId.NewGuid()] = CreateRule() with { Trigger = trigger } + [DomainId.NewGuid()] = CreateRule() with { Trigger = trigger }, }.ToReadonlyDictionary(), - AllowExtraEvents = allowExtra + AllowExtraEvents = allowExtra, }; } @@ -537,7 +537,7 @@ public class ContentChangedTriggerHandlerTests : GivenContext AppId = AppId, IncludeSkipped = false, IncludeStale = false, - Rule = CreateRule() with { Trigger = trigger } + Rule = CreateRule() with { Trigger = trigger }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEventDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEventDeleterTests.cs index 8db1c172e..624a57446 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEventDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEventDeleterTests.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Contents.Repositories; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Events; @@ -47,7 +48,7 @@ public class ContentEventDeleterTests : GivenContext var id2 = DomainId.NewGuid(); var ids = new[] { id1, id2 }; - A.CallTo(() => contentRepository.StreamIds(App.Id, Schema.Id, SearchScope.All, CancellationToken)) + A.CallTo(() => contentRepository.StreamIds(App.Id, A>.That.Is(Schema.Id), SearchScope.All, CancellationToken)) .Returns(ids.ToAsyncEnumerable()); await sut.DeleteSchemAsync(App, Schema, CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentSchedulerProcessTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentSchedulerProcessTests.cs index 85edecee2..78c805676 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentSchedulerProcessTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentSchedulerProcessTests.cs @@ -27,7 +27,7 @@ public class ContentSchedulerProcessTests : GivenContext { sut = new ContentSchedulerProcess(contentRepository, commandBus, A.Fake>()) { - Clock = clock + Clock = clock, }; } @@ -38,12 +38,12 @@ public class ContentSchedulerProcessTests : GivenContext var content1 = CreateContent() with { - ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Archived, null!, now) + ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Archived, null!, now), }; var content2 = CreateContent() with { - ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Draft, null!, now) + ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Draft, null!, now), }; A.CallTo(() => clock.GetCurrentInstant()) @@ -78,7 +78,7 @@ public class ContentSchedulerProcessTests : GivenContext var content1 = CreateContent() with { - ScheduleJob = null + ScheduleJob = null, }; A.CallTo(() => clock.GetCurrentInstant()) @@ -100,7 +100,7 @@ public class ContentSchedulerProcessTests : GivenContext var content1 = CreateContent() with { - ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Archived, null!, now) + ScheduleJob = new ScheduleJob(DomainId.NewGuid(), Status.Archived, null!, now), }; A.CallTo(() => clock.GetCurrentInstant()) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentsJintExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentsJintExtensionTests.cs index c13bf6836..fa02b908f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentsJintExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentsJintExtensionTests.cs @@ -34,14 +34,14 @@ public class ContentsJintExtensionTests : GivenContext, IClassFixture +public class ContentCommandMiddlewareTests : HandlerTestBase { private readonly IDomainObjectCache domainObjectCache = A.Fake(); private readonly IDomainObjectFactory domainObjectFactory = A.Fake(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs index b628fac73..8c318977d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentDomainObjectTests.cs @@ -90,7 +90,7 @@ public class ContentDomainObjectTests : HandlerTestBase Change = "", Create = "", Delete = "", - Update = "" + Update = "", }) .Publish(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs index 00b6d8846..4399522aa 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/ContentsBulkUpdateCommandMiddlewareTests.cs @@ -583,9 +583,9 @@ public class ContentsBulkUpdateCommandMiddlewareTests : GivenContext AppId = AppId, Jobs = [ - job + job, ], - SchemaId = schemaId + SchemaId = schemaId, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs index 57dc71332..49e85ad9e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs @@ -383,7 +383,7 @@ public class GuardContentTests : GivenContext, IClassFixture { - [Status.Draft] = WorkflowTransition.Always + [Status.Draft] = WorkflowTransition.Always, }.ToReadonlyDictionary(), StatusColors.Archived, NoUpdate.Always, Validate: true), [Status.Draft] = @@ -38,7 +38,7 @@ public class DynamicContentWorkflowTests : GivenContext new Dictionary { [Status.Archived] = WorkflowTransition.Always, - [Status.Published] = WorkflowTransition.When("data.field.iv === 2", Role.Editor) + [Status.Published] = WorkflowTransition.When("data.field.iv === 2", Role.Editor), }.ToReadonlyDictionary(), StatusColors.Draft), [Status.Published] = @@ -46,9 +46,9 @@ public class DynamicContentWorkflowTests : GivenContext new Dictionary { [Status.Archived] = WorkflowTransition.Always, - [Status.Draft] = WorkflowTransition.Always + [Status.Draft] = WorkflowTransition.Always, }.ToReadonlyDictionary(), - StatusColors.Published, NoUpdate.When("data.field.iv === 2", Role.Owner, Role.Editor)) + StatusColors.Published, NoUpdate.When("data.field.iv === 2", Role.Owner, Role.Editor)), }.ToReadonlyDictionary()); public DynamicContentWorkflowTests() @@ -61,29 +61,29 @@ public class DynamicContentWorkflowTests : GivenContext new WorkflowStep( new Dictionary { - [Status.Published] = WorkflowTransition.Always + [Status.Published] = WorkflowTransition.Always, }.ToReadonlyDictionary(), StatusColors.Draft), [Status.Published] = new WorkflowStep( new Dictionary { - [Status.Draft] = WorkflowTransition.Always + [Status.Draft] = WorkflowTransition.Always, }.ToReadonlyDictionary(), - StatusColors.Published) + StatusColors.Published), }.ToReadonlyDictionary(), ReadonlyList.Create(simpleSchemaId)); App = App with { - Workflows = Workflows.Empty.Set(workflow).Set(DomainId.NewGuid(), simpleWorkflow) + Workflows = Workflows.Empty.Set(workflow).Set(DomainId.NewGuid(), simpleWorkflow), }; var scriptEngine = new JintScriptEngine(new MemoryCache(Options.Create(new MemoryCacheOptions())), Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), })); sut = new DynamicContentWorkflow(scriptEngine, AppProvider); @@ -260,7 +260,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { - new StatusInfo(Status.Archived, StatusColors.Archived) + new StatusInfo(Status.Archived, StatusColors.Archived), }; var actual = await sut.GetNextAsync(content, content.Status, Mocks.FrontendUser(Role.Developer)); @@ -275,7 +275,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { - new StatusInfo(Status.Archived, StatusColors.Archived) + new StatusInfo(Status.Archived, StatusColors.Archived), }; var actual = await sut.GetNextAsync(content, content.Status, Mocks.FrontendUser(Role.Editor)); @@ -291,7 +291,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { new StatusInfo(Status.Archived, StatusColors.Archived), - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), }; var actual = await sut.GetNextAsync(content, content.Status, Mocks.FrontendUser(Role.Editor)); @@ -306,7 +306,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { - new StatusInfo(Status.Draft, StatusColors.Draft) + new StatusInfo(Status.Draft, StatusColors.Draft), }; var actual = await sut.GetNextAsync(content, content.Status, null!); @@ -322,7 +322,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { new StatusInfo(Status.Archived, StatusColors.Archived), - new StatusInfo(Status.Draft, StatusColors.Draft) + new StatusInfo(Status.Draft, StatusColors.Draft), }; var actual = await sut.GetNextAsync(content, content.Status, null!); @@ -337,7 +337,7 @@ public class DynamicContentWorkflowTests : GivenContext { new StatusInfo(Status.Archived, StatusColors.Archived), new StatusInfo(Status.Draft, StatusColors.Draft), - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), }; var actual = await sut.GetAllAsync(Schema); @@ -351,7 +351,7 @@ public class DynamicContentWorkflowTests : GivenContext var expected = new[] { new StatusInfo(Status.Draft, StatusColors.Draft), - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), }; var actual = await sut.GetAllAsync(Schema.WithId(simpleSchemaId, "simple-schema")); @@ -364,14 +364,14 @@ public class DynamicContentWorkflowTests : GivenContext { App = App with { - Workflows = Workflows.Empty + Workflows = Workflows.Empty, }; var expected = new[] { new StatusInfo(Status.Archived, StatusColors.Archived), new StatusInfo(Status.Draft, StatusColors.Draft), - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), }; var actual = await sut.GetAllAsync(Schema); @@ -392,7 +392,7 @@ public class DynamicContentWorkflowTests : GivenContext { var schema = Schema with { - Properties = new SchemaProperties { ValidateOnPublish = false } + Properties = new SchemaProperties { ValidateOnPublish = false }, }; var actual = await sut.ShouldValidateAsync(schema, Status.Published); @@ -405,7 +405,7 @@ public class DynamicContentWorkflowTests : GivenContext { var schema = Schema with { - Properties = new SchemaProperties { ValidateOnPublish = true } + Properties = new SchemaProperties { ValidateOnPublish = true }, }; var actual = await sut.ShouldValidateAsync(schema, Status.Published); @@ -429,7 +429,7 @@ public class DynamicContentWorkflowTests : GivenContext { content = content with { - SchemaId = NamedId.Of(simpleSchemaId, "my-simple-schema") + SchemaId = NamedId.Of(simpleSchemaId, "my-simple-schema"), }; } @@ -440,7 +440,7 @@ public class DynamicContentWorkflowTests : GivenContext new ContentData() .AddField("field", new ContentFieldData() - .AddInvariant(value)) + .AddInvariant(value)), }; return content; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs index 9276d6889..59838f726 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs @@ -110,7 +110,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase } } }", - OperationName = "IntrospectionQuery" + OperationName = "IntrospectionQuery", }); var json = serializer.Serialize(actual); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs index 645f71995..bb67911be 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs @@ -39,7 +39,7 @@ public class GraphQLMutationTests : GraphQLTestBase createMySchemaContent(data: { }) { id } - }" + }", }); var expected = new @@ -54,27 +54,27 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "createMySchemaContent" + "createMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, data = new { - createMySchemaContent = (object?)null - } + createMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -98,21 +98,21 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { data = TestContent.Input(content), }, - Permissions = [PermissionIds.AppContentsCreate] + Permissions = [PermissionIds.AppContentsCreate], }); var expected = new { data = new { - createMySchemaContent = TestContent.Response(content) - } + createMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -146,17 +146,17 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { location = new { latitude = 42, - longitude = 13 - } + longitude = 13, + }, }, - Permissions = [PermissionIds.AppContentsCreate] + Permissions = [PermissionIds.AppContentsCreate], }); var expected = new @@ -166,8 +166,8 @@ public class GraphQLMutationTests : GraphQLTestBase createMySchemaContent = new { id = content.Id, - } - } + }, + }, }; AssertResult(expected, actual); @@ -196,21 +196,21 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { - data = TestContent.Input(content) + data = TestContent.Input(content), }, - Permissions = [PermissionIds.AppContentsCreate] + Permissions = [PermissionIds.AppContentsCreate], }); var expected = new { data = new { - createMySchemaContent = TestContent.Response(content) - } + createMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -239,8 +239,8 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -255,27 +255,27 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "updateMySchemaContent" + "updateMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, data = new { - updateMySchemaContent = (object?)null - } + updateMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -300,21 +300,21 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { - data = TestContent.Input(content) + data = TestContent.Input(content), }, - Permissions = [PermissionIds.AppContentsUpdateOwn] + Permissions = [PermissionIds.AppContentsUpdateOwn], }); var expected = new { data = new { - updateMySchemaContent = TestContent.Response(content) - } + updateMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -339,7 +339,7 @@ public class GraphQLMutationTests : GraphQLTestBase upsertMySchemaContent(id: '{contentId}', data: { }) { id } - }" + }", }); var expected = new @@ -354,27 +354,27 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "upsertMySchemaContent" + "upsertMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, data = new { - upsertMySchemaContent = (object?)null - } + upsertMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -399,21 +399,21 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { - data = TestContent.Input(content) + data = TestContent.Input(content), }, - Permissions = [PermissionIds.AppContentsUpsert] + Permissions = [PermissionIds.AppContentsUpsert], }); var expected = new { data = new { - upsertMySchemaContent = TestContent.Response(content) - } + upsertMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -442,7 +442,7 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - contentId + contentId, }, }); @@ -458,27 +458,27 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "patchMySchemaContent" + "patchMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, data = new { - patchMySchemaContent = (object?)null - } + patchMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -503,21 +503,21 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, Variables = new { - data = TestContent.Input(content) + data = TestContent.Input(content), }, - Permissions = [PermissionIds.AppContentsUpdateOwn] + Permissions = [PermissionIds.AppContentsUpdateOwn], }); var expected = new { data = new { - patchMySchemaContent = TestContent.Response(content) - } + patchMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -545,7 +545,7 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - contentId + contentId, }, }); @@ -561,27 +561,27 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "changeMySchemaContent" + "changeMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, data = new { - changeMySchemaContent = (object?)null - } + changeMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -608,17 +608,17 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, - Permissions = [PermissionIds.AppContentsChangeStatusOwn] + Permissions = [PermissionIds.AppContentsChangeStatusOwn], }); var expected = new { data = new { - changeMySchemaContent = TestContent.Response(content) - } + changeMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -650,17 +650,17 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, - Permissions = [PermissionIds.AppContentsChangeStatusOwn] + Permissions = [PermissionIds.AppContentsChangeStatusOwn], }); var expected = new { data = new { - changeMySchemaContent = TestContent.Response(content) - } + changeMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -692,17 +692,17 @@ public class GraphQLMutationTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields + fields = TestContent.AllFields, }, - Permissions = [PermissionIds.AppContentsChangeStatusOwn] + Permissions = [PermissionIds.AppContentsChangeStatusOwn], }); var expected = new { data = new { - changeMySchemaContent = TestContent.Response(content) - } + changeMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -731,7 +731,7 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - contentId + contentId, }, }); @@ -747,24 +747,24 @@ public class GraphQLMutationTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "deleteMySchemaContent" + "deleteMySchemaContent", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, - data = (object?)null + data = (object?)null, }; AssertResult(expected, actual); @@ -788,9 +788,9 @@ public class GraphQLMutationTests : GraphQLTestBase }", Args = new { - contentId + contentId, }, - Permissions = [PermissionIds.AppContentsDeleteOwn] + Permissions = [PermissionIds.AppContentsDeleteOwn], }); var expected = new @@ -799,9 +799,9 @@ public class GraphQLMutationTests : GraphQLTestBase { deleteMySchemaContent = new { - version = 1 - } - } + version = 1, + }, + }, }; AssertResult(expected, actual); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index e3945531c..63ecba4eb 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -25,7 +25,7 @@ public class GraphQLQueriesTests : GraphQLTestBase { var actual = await ExecuteAsync(new TestQuery { - Query = query + Query = query, }); var expected = new @@ -40,11 +40,11 @@ public class GraphQLQueriesTests : GraphQLTestBase code = "NO_OPERATION", codes = new[] { - "NO_OPERATION" - } - } - } - } + "NO_OPERATION", + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -71,8 +71,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFlatFields - } + fields = TestContent.AllFlatFields, + }, }); var expected = new @@ -81,9 +81,9 @@ public class GraphQLQueriesTests : GraphQLTestBase { queryMySchemaContents = new[] { - TestContent.FlatResponse(content) - } - } + TestContent.FlatResponse(content), + }, + }, }; AssertResult(expected, actual); @@ -110,8 +110,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFlatFields - } + fields = TestContent.AllFlatFields, + }, }); var expected = new @@ -120,9 +120,9 @@ public class GraphQLQueriesTests : GraphQLTestBase { queryMySchemaContents = new[] { - TestContent.FlatResponse(content) - } - } + TestContent.FlatResponse(content), + }, + }, }; AssertResult(expected, actual); @@ -156,8 +156,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -171,11 +171,11 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentId, flatData = new { - myNumber = 1 - } - } - } - } + myNumber = 1, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -204,8 +204,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -216,10 +216,10 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - data = content.Data - } - } - } + data = content.Data, + }, + }, + }, }; AssertResult(expected, actual); @@ -245,8 +245,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestAsset.AllFields - } + fields = TestAsset.AllFields, + }, }); var expected = new @@ -255,9 +255,9 @@ public class GraphQLQueriesTests : GraphQLTestBase { queryAssets = new[] { - TestAsset.Response(asset) - } - } + TestAsset.Response(asset), + }, + }, }; AssertResult(expected, actual); @@ -286,8 +286,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestAsset.AllFields - } + fields = TestAsset.AllFields, + }, }); var expected = new @@ -299,10 +299,10 @@ public class GraphQLQueriesTests : GraphQLTestBase total = 10, items = new[] { - TestAsset.Response(asset) - } - } - } + TestAsset.Response(asset), + }, + }, + }, }; AssertResult(expected, actual); @@ -329,16 +329,16 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - assetId - } + assetId, + }, }); var expected = new { data = new { - findAsset = (object?)null - } + findAsset = (object?)null, + }, }; AssertResult(expected, actual); @@ -366,16 +366,16 @@ public class GraphQLQueriesTests : GraphQLTestBase Args = new { assetId, - fields = TestAsset.AllFields - } + fields = TestAsset.AllFields, + }, }); var expected = new { data = new { - findAsset = TestAsset.Response(asset) - } + findAsset = TestAsset.Response(asset), + }, }; AssertResult(expected, actual); @@ -402,8 +402,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFlatFields - } + fields = TestContent.AllFlatFields, + }, }); var expected = new @@ -412,9 +412,9 @@ public class GraphQLQueriesTests : GraphQLTestBase { queryMySchemaContents = new[] { - TestContent.FlatResponse(content) - } - } + TestContent.FlatResponse(content), + }, + }, }; AssertResult(expected, actual); @@ -441,8 +441,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFields - } + fields = TestContent.AllFields, + }, }); var expected = new @@ -451,9 +451,9 @@ public class GraphQLQueriesTests : GraphQLTestBase { queryMySchemaContents = new[] { - TestContent.Response(content) - } - } + TestContent.Response(content), + }, + }, }; AssertResult(expected, actual); @@ -483,8 +483,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - fields = TestContent.AllFields - } + fields = TestContent.AllFields, + }, }); var expected = new @@ -496,10 +496,10 @@ public class GraphQLQueriesTests : GraphQLTestBase total = 10, items = new[] { - TestContent.Response(content) - } - } - } + TestContent.Response(content), + }, + }, + }, }; AssertResult(expected, actual); @@ -526,16 +526,16 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new { data = new { - findMySchemaContent = (object?)null - } + findMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -563,16 +563,16 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new { data = new { - findMySchemaContent = (object?)null - } + findMySchemaContent = (object?)null, + }, }; AssertResult(expected, actual); @@ -600,16 +600,16 @@ public class GraphQLQueriesTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields - } + fields = TestContent.AllFields, + }, }); var expected = new { data = new { - findMySchemaContent = TestContent.Response(content) - } + findMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -636,16 +636,16 @@ public class GraphQLQueriesTests : GraphQLTestBase Args = new { contentId, - fields = TestContent.AllFields - } + fields = TestContent.AllFields, + }, }); var expected = new { data = new { - findMySchemaContent = TestContent.Response(content) - } + findMySchemaContent = TestContent.Response(content), + }, }; AssertResult(expected, actual); @@ -675,8 +675,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -688,10 +688,10 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentId, flatData = new { - singletonField = "Hello" - } - } - } + singletonField = "Hello", + }, + }, + }, }; AssertResult(expected, actual); @@ -720,8 +720,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -733,10 +733,10 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentId, flatData = new { - singletonField = "Hello" - } - } - } + singletonField = "Hello", + }, + }, + }, }; AssertResult(expected, actual); @@ -796,8 +796,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -823,16 +823,16 @@ public class GraphQLQueriesTests : GraphQLTestBase { reference1Field = new { - iv = "reference1" - } - } - } - } - } - } - } - } - } + iv = "reference1", + }, + }, + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -898,8 +898,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -925,10 +925,10 @@ public class GraphQLQueriesTests : GraphQLTestBase type = "link", attrs = new { - href = $"contents:{contentRefId}" - } - } - } + href = $"contents:{contentRefId}", + }, + }, + }, }, contents = new[] { @@ -939,16 +939,16 @@ public class GraphQLQueriesTests : GraphQLTestBase { reference1Field = new { - iv = "reference1" - } - } - } - } - } - } - } - } - } + iv = "reference1", + }, + }, + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1001,8 +1001,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1025,15 +1025,15 @@ public class GraphQLQueriesTests : GraphQLTestBase { reference1Field = new { - iv = "reference1" - } - } - } - } - } - } - } - } + iv = "reference1", + }, + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1079,8 +1079,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1096,12 +1096,12 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = contentRefId - } - } - } - } - } + id = contentRefId, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1147,8 +1147,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }; var actual1 = await ExecuteAsync(query); @@ -1167,12 +1167,12 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = contentRefId - } - } - } - } - } + id = contentRefId, + }, + }, + }, + }, + }, }; AssertResult(expected, actual1); @@ -1230,8 +1230,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId = contentRefId - } + contentId = contentRefId, + }, }); var expected = new @@ -1250,13 +1250,13 @@ public class GraphQLQueriesTests : GraphQLTestBase { myLocalizedString = new { - de_DE = "de-DE" - } - } - } - } - } - } + de_DE = "de-DE", + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1311,8 +1311,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId = contentRefId - } + contentId = contentRefId, + }, }); var expected = new @@ -1334,14 +1334,14 @@ public class GraphQLQueriesTests : GraphQLTestBase { myLocalizedString = new { - de_DE = "de-DE" - } - } - } - } - } - } - } + de_DE = "de-DE", + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1385,8 +1385,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1400,11 +1400,11 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = contentRefId - } - } - } - } + id = contentRefId, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1451,8 +1451,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1469,12 +1469,12 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = contentRefId - } - } - } - } - } + id = contentRefId, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1532,8 +1532,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1556,16 +1556,16 @@ public class GraphQLQueriesTests : GraphQLTestBase { reference1Field = new { - iv = "reference1" - } + iv = "reference1", + }, }, - __typename = "MyReference1" - } - } - } - } - } - } + __typename = "MyReference1", + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1616,8 +1616,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1638,14 +1638,14 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = assetRefId - } - } - } - } - } - } - } + id = assetRefId, + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1699,8 +1699,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1721,21 +1721,21 @@ public class GraphQLQueriesTests : GraphQLTestBase type = "image", attrs = new { - src = $"assets:{assetRefId}" - } + src = $"assets:{assetRefId}", + }, }, assets = new[] { new { - id = assetRefId - } - } - } - } - } - } - } + id = assetRefId, + }, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1783,8 +1783,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -1802,13 +1802,13 @@ public class GraphQLQueriesTests : GraphQLTestBase { new { - id = assetRefId - } - } - } - } - } - } + id = assetRefId, + }, + }, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -1846,8 +1846,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var json = serializer.Serialize(actual); @@ -1869,7 +1869,7 @@ public class GraphQLQueriesTests : GraphQLTestBase } } } - }" + }", }); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), @@ -1890,7 +1890,7 @@ public class GraphQLQueriesTests : GraphQLTestBase myNumber } } - }" + }", }); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), @@ -1913,7 +1913,7 @@ public class GraphQLQueriesTests : GraphQLTestBase } } } - }" + }", }); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), @@ -1935,7 +1935,7 @@ public class GraphQLQueriesTests : GraphQLTestBase } data__dynamic } - }" + }", }); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), @@ -1958,7 +1958,7 @@ public class GraphQLQueriesTests : GraphQLTestBase } } } - }" + }", }); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), @@ -1990,8 +1990,8 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - contentId - } + contentId, + }, }); var expected = new @@ -2002,10 +2002,10 @@ public class GraphQLQueriesTests : GraphQLTestBase { createdByUser = new { - id = content.CreatedBy.Identifier - } - } - } + id = content.CreatedBy.Identifier, + }, + }, + }, }; AssertResult(expected, actual); @@ -2044,9 +2044,9 @@ public class GraphQLQueriesTests : GraphQLTestBase }", Args = new { - assetId + assetId, }, - Permissions = [PermissionIds.AppPii] + Permissions = [PermissionIds.AppPii], }); var expected = new @@ -2059,16 +2059,16 @@ public class GraphQLQueriesTests : GraphQLTestBase { id = asset.CreatedBy.Identifier, email = $"{asset.CreatedBy.Identifier}@email.com", - displayName = $"{asset.CreatedBy.Identifier}name" + displayName = $"{asset.CreatedBy.Identifier}name", }, lastModifiedByUser = new { id = asset.LastModifiedBy.Identifier, email = $"{asset.LastModifiedBy}", - displayName = asset.LastModifiedBy.Identifier - } - } - } + displayName = asset.LastModifiedBy.Identifier, + }, + }, + }, }; AssertResult(expected, actual); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLSubscriptionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLSubscriptionTests.cs index 7906d25ea..777396143 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLSubscriptionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLSubscriptionTests.cs @@ -27,7 +27,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase { Id = id, FileName = "image.png", - FileSize = 1024 + FileSize = 1024, }); A.CallTo(() => subscriptionService.SubscribeAsync($"asset-{TestApp.Default.Id}", A._, default)) @@ -43,7 +43,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase fileSize } }", - Permissions = [PermissionIds.AppAssetsRead] + Permissions = [PermissionIds.AppAssetsRead], }); var expected = new @@ -54,9 +54,9 @@ public class GraphQLSubscriptionTests : GraphQLTestBase { id, fileName = "image.png", - fileSize = 1024 - } - } + fileSize = 1024, + }, + }, }; AssertResult(expected, actual); @@ -74,7 +74,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase fileName, fileSize } - }" + }", }); var expected = new @@ -89,24 +89,24 @@ public class GraphQLSubscriptionTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "assetChanges" + "assetChanges", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, - data = (object?)null + data = (object?)null, }; AssertResult(expected, actual); @@ -125,7 +125,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase Data = new ContentData() .AddField("field", new ContentFieldData() - .AddInvariant(42)) + .AddInvariant(42)), }); A.CallTo(() => subscriptionService.SubscribeAsync($"content-{TestApp.Default.Id}", A._, default)) @@ -140,7 +140,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase data } }", - Permissions = [PermissionIds.AppContentsRead] + Permissions = [PermissionIds.AppContentsRead], }); var expected = new @@ -154,11 +154,11 @@ public class GraphQLSubscriptionTests : GraphQLTestBase { field = new { - iv = 42 - } - } - } - } + iv = 42, + }, + }, + }, + }, }; AssertResult(expected, actual); @@ -175,7 +175,7 @@ public class GraphQLSubscriptionTests : GraphQLTestBase id, data } - }" + }", }); var expected = new @@ -190,24 +190,24 @@ public class GraphQLSubscriptionTests : GraphQLTestBase new { line = 3, - column = 19 - } + column = 19, + }, }, path = new[] { - "contentChanges" + "contentChanges", }, extensions = new { code = "DOMAIN_FORBIDDEN", codes = new[] { - "DOMAIN_FORBIDDEN" - } - } - } + "DOMAIN_FORBIDDEN", + }, + }, + }, }, - data = (object?)null + data = (object?)null, }; AssertResult(expected, actual); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/NamesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/NamesTests.cs index c371ef942..5708da6f3 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/NamesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/NamesTests.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL; -public sealed class NamesTests +public class NamesTests { [Fact] public void Should_return_name_if_not_taken() diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs index f11308f78..194f77413 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestAsset.cs @@ -85,7 +85,7 @@ public static class TestAsset [ "tag1", "tag2", - ] + ], }; return asset; @@ -103,7 +103,7 @@ public static class TestAsset { id = asset.CreatedBy.Identifier, email = "Hidden", - displayName = "Hidden" + displayName = "Hidden", }, editToken = $"token_{asset.Id}", lastModified = asset.LastModified, @@ -112,7 +112,7 @@ public static class TestAsset { id = asset.LastModifiedBy.Identifier, email = "Hidden", - displayName = "Hidden" + displayName = "Hidden", }, url = $"assets/{asset.AppId.Name}/{asset.Id}", thumbnailUrl = $"assets/{asset.AppId.Name}/{asset.Id}?width=100", @@ -135,9 +135,9 @@ public static class TestAsset metadata = new { pixelWidth = asset.Metadata.GetInt32(KnownMetadataKeys.PixelWidth), - pixelHeight = asset.Metadata.GetInt32(KnownMetadataKeys.PixelHeight) + pixelHeight = asset.Metadata.GetInt32(KnownMetadataKeys.PixelHeight), }, - slug = asset.Slug + slug = asset.Slug, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs index 1433202de..7193e0cd2 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs @@ -357,7 +357,7 @@ public static class TestContent Status = Status.Draft, StatusColor = "red", NewStatus = Status.Published, - NewStatusColor = "blue" + NewStatusColor = "blue", }; return content; @@ -388,7 +388,7 @@ public static class TestContent Status = Status.Draft, StatusColor = "red", NewStatus = Status.Published, - NewStatusColor = "blue" + NewStatusColor = "blue", }; return content; @@ -406,7 +406,7 @@ public static class TestContent { id = content.CreatedBy.Identifier, email = "Hidden", - displayName = "Hidden" + displayName = "Hidden", }, editToken = $"token_{content.Id}", lastModified = content.LastModified, @@ -415,14 +415,14 @@ public static class TestContent { id = content.LastModifiedBy.Identifier, email = "Hidden", - displayName = "Hidden" + displayName = "Hidden", }, status = "DRAFT", statusColor = "red", newStatus = "PUBLISHED", newStatusColor = "blue", url = $"contents/my-schema/{content.Id}", - data = Data(content) + data = Data(content), }; } @@ -454,7 +454,7 @@ public static class TestContent newStatus = "PUBLISHED", newStatusColor = "blue", url = $"contents/my-schema/{content.Id}", - flatData = FlatData(content) + flatData = FlatData(content), }; } @@ -466,8 +466,8 @@ public static class TestContent { iv = new { - value = 1 - } + value = 1, + }, }, ["myJson2"] = new { @@ -485,61 +485,61 @@ public static class TestContent ["nestedFloat"] = 3.14, ["nestedBoolean"] = true, ["nestedArray"] = new[] { "1", "2", "3" }, - } - } + }, + }, }, ["myString"] = new { - iv = (string?)null + iv = (string?)null, }, ["myStringEnum"] = new { - iv = "EnumA" + iv = "EnumA", }, ["myLocalizedString"] = new { - de_DE = "de-DE" + de_DE = "de-DE", }, ["myNumber"] = new { - iv = 1.0 + iv = 1.0, }, ["myAssets"] = new { iv = new[] { - TestSchemas.Component.Id - } + TestSchemas.Component.Id, + }, }, ["myBoolean"] = new { - iv = true + iv = true, }, ["myDatetime"] = new { - iv = content.LastModified.ToString() + iv = content.LastModified.ToString(), }, ["myReferences"] = new { iv = new[] { - TestSchemas.Reference1.Id - } + TestSchemas.Reference1.Id, + }, }, ["myUnion"] = new { iv = new[] { - TestSchemas.Reference2.Id - } + TestSchemas.Reference2.Id, + }, }, ["myGeolocation"] = new { iv = new { latitude = 10, - longitude = 20 - } + longitude = 20, + }, }, ["myComponent"] = new { @@ -547,8 +547,8 @@ public static class TestContent { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component1" - } + ["componentField"] = "Component1", + }, }, ["myComponents"] = new { @@ -558,37 +558,37 @@ public static class TestContent { ["schemaId"] = TestSchemas.Reference1.Id.ToString(), ["schemaName"] = TestSchemas.Reference1.Name, - ["reference1Field"] = "Component1" + ["reference1Field"] = "Component1", }, new Dictionary { ["schemaId"] = TestSchemas.Reference2.Id.ToString(), ["schemaName"] = TestSchemas.Reference2.Name, - ["reference2Field"] = "Component2" + ["reference2Field"] = "Component2", }, new Dictionary { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component3" - } - } + ["componentField"] = "Component3", + }, + }, }, ["myTags"] = new { iv = new[] { "tag1", - "tag2" - } + "tag2", + }, }, ["myTagsEnum"] = new { iv = new[] { "EnumA", - "EnumB" - } + "EnumB", + }, }, ["myArray"] = new { @@ -597,14 +597,14 @@ public static class TestContent new { nestedNumber = 42.0, - nestedBoolean = true + nestedBoolean = true, }, new { nestedNumber = 3.14, - nestedBoolean = false - } - } + nestedBoolean = false, + }, + }, }, ["myRichtext"] = new { @@ -617,10 +617,10 @@ public static class TestContent { type = "text", text = "Rich Text", - } - } - } - } + }, + }, + }, + }, }; return actual; @@ -634,9 +634,9 @@ public static class TestContent { iv = new { - value = 1 + value = 1, }, - ivValue = 1 + ivValue = 1, }, ["myJson2"] = new { @@ -656,40 +656,40 @@ public static class TestContent ["nestedFloat"] = 3.14, ["nestedBoolean"] = true, ["nestedArray"] = new[] { "1", "2", "3" }, - } - } + }, + }, }, ["myString"] = new { - iv = (string?)null + iv = (string?)null, }, ["myStringEnum"] = new { - iv = "EnumA" + iv = "EnumA", }, ["myLocalizedString"] = new { - de_DE = "de-DE" + de_DE = "de-DE", }, ["myNumber"] = new { - iv = 1.0 + iv = 1.0, }, ["myBoolean"] = new { - iv = true + iv = true, }, ["myDatetime"] = new { - iv = content.LastModified.ToString() + iv = content.LastModified.ToString(), }, ["myGeolocation"] = new { iv = new { latitude = 10, - longitude = 20 - } + longitude = 20, + }, }, ["myComponent__Dynamic"] = new { @@ -697,8 +697,8 @@ public static class TestContent { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["component-field"] = "Component1" - } + ["component-field"] = "Component1", + }, }, ["myComponent"] = new { @@ -706,8 +706,8 @@ public static class TestContent { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component1" - } + ["componentField"] = "Component1", + }, }, ["myComponents__Dynamic"] = new { @@ -717,21 +717,21 @@ public static class TestContent { ["schemaId"] = TestSchemas.Reference1.Id.ToString(), ["schemaName"] = TestSchemas.Reference1.Name, - ["reference1-field"] = "Component1" + ["reference1-field"] = "Component1", }, new Dictionary { ["schemaId"] = TestSchemas.Reference2.Id.ToString(), ["schemaName"] = TestSchemas.Reference2.Name, - ["reference2-field"] = "Component2" + ["reference2-field"] = "Component2", }, new Dictionary { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["component-field"] = "Component3" - } - } + ["component-field"] = "Component3", + }, + }, }, ["myComponents"] = new { @@ -742,39 +742,39 @@ public static class TestContent ["__typename"] = "MyReference1Component", ["schemaId"] = TestSchemas.Reference1.Id.ToString(), ["schemaName"] = TestSchemas.Reference1.Name, - ["reference1Field"] = "Component1" + ["reference1Field"] = "Component1", }, new Dictionary { ["__typename"] = "MyReference2Component", ["schemaId"] = TestSchemas.Reference2.Id.ToString(), ["schemaName"] = TestSchemas.Reference2.Name, - ["reference2Field"] = "Component2" + ["reference2Field"] = "Component2", }, new Dictionary { ["__typename"] = "MyComponentComponent", ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component3" - } - } + ["componentField"] = "Component3", + }, + }, }, ["myTags"] = new { iv = new[] { "tag1", - "tag2" - } + "tag2", + }, }, ["myTagsEnum"] = new { iv = new[] { "EnumA", - "EnumB" - } + "EnumB", + }, }, ["myArray"] = new { @@ -783,14 +783,14 @@ public static class TestContent new { nestedNumber = 42.0, - nestedBoolean = true + nestedBoolean = true, }, new { nestedNumber = 3.14, - nestedBoolean = false - } - } + nestedBoolean = false, + }, + }, }, ["myRichtext"] = new { @@ -805,15 +805,15 @@ public static class TestContent { type = "text", text = "Rich Text", - } - } + }, + }, }, htmlMinimized = "

Rich Text

", htmlNormal = "

Rich Text

", markdown = "# Rich Text", - text = "Rich Text" - } - } + text = "Rich Text", + }, + }, }; return actual; @@ -825,7 +825,7 @@ public static class TestContent { ["myJson"] = new { - value = 1 + value = 1, }, ["myJsonValue"] = 1, ["myJson2"] = new Dictionary @@ -844,7 +844,7 @@ public static class TestContent ["nestedFloat"] = 3.14, ["nestedBoolean"] = true, ["nestedArray"] = new[] { "1", "2", "3" }, - } + }, }, ["myString"] = null, ["myStringEnum"] = "EnumA", @@ -855,19 +855,19 @@ public static class TestContent ["myGeolocation"] = new { latitude = 10, - longitude = 20 + longitude = 20, }, ["myComponent__Dynamic"] = new Dictionary { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["component-field"] = "Component1" + ["component-field"] = "Component1", }, ["myComponent"] = new Dictionary { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component1" + ["componentField"] = "Component1", }, ["myComponents__Dynamic"] = new[] { @@ -875,20 +875,20 @@ public static class TestContent { ["schemaId"] = TestSchemas.Reference1.Id.ToString(), ["schemaName"] = TestSchemas.Reference1.Name, - ["reference1-field"] = "Component1" + ["reference1-field"] = "Component1", }, new Dictionary { ["schemaId"] = TestSchemas.Reference2.Id.ToString(), ["schemaName"] = TestSchemas.Reference2.Name, - ["reference2-field"] = "Component2" + ["reference2-field"] = "Component2", }, new Dictionary { ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["component-field"] = "Component3" - } + ["component-field"] = "Component3", + }, }, ["myComponents"] = new object[] { @@ -897,45 +897,45 @@ public static class TestContent ["__typename"] = "MyReference1Component", ["schemaId"] = TestSchemas.Reference1.Id.ToString(), ["schemaName"] = TestSchemas.Reference1.Name, - ["reference1Field"] = "Component1" + ["reference1Field"] = "Component1", }, new Dictionary { ["__typename"] = "MyReference2Component", ["schemaId"] = TestSchemas.Reference2.Id.ToString(), ["schemaName"] = TestSchemas.Reference2.Name, - ["reference2Field"] = "Component2" + ["reference2Field"] = "Component2", }, new Dictionary { ["__typename"] = "MyComponentComponent", ["schemaId"] = TestSchemas.Component.Id.ToString(), ["schemaName"] = TestSchemas.Component.Name, - ["componentField"] = "Component3" - } + ["componentField"] = "Component3", + }, }, ["myTags"] = new[] { "tag1", - "tag2" + "tag2", }, ["myTagsEnum"] = new[] { "EnumA", - "EnumB" + "EnumB", }, ["myArray"] = new[] { new { nestedNumber = 42.0, - nestedBoolean = true + nestedBoolean = true, }, new { nestedNumber = 3.14, - nestedBoolean = false - } + nestedBoolean = false, + }, }, ["myRichtext"] = new { @@ -948,14 +948,14 @@ public static class TestContent { type = "text", text = "Rich Text", - } - } + }, + }, }, htmlMinimized = "

Rich Text

", htmlNormal = "

Rich Text

", markdown = "# Rich Text", - text = "Rich Text" - } + text = "Rich Text", + }, }; return actual; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/CreateIndexJobTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/CreateIndexJobTests.cs index 763b8bf7e..bf46df6fb 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/CreateIndexJobTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/CreateIndexJobTests.cs @@ -44,7 +44,7 @@ public class CreateIndexJobTests : GivenContext ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, ["field_field1"] = "Ascending", - ["field_field2"] = "Descending" + ["field_field2"] = "Descending", }); } @@ -59,8 +59,8 @@ public class CreateIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaName"] = Schema.Name, ["field_field1"] = "Ascending", - ["field_field2"] = "Descending" - }.ToReadonlyDictionary() + ["field_field2"] = "Descending", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -79,8 +79,8 @@ public class CreateIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), ["field_field1"] = "Ascending", - ["field_field2"] = "Descending" - }.ToReadonlyDictionary() + ["field_field2"] = "Descending", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -100,8 +100,8 @@ public class CreateIndexJobTests : GivenContext ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, ["field_field1"] = "Invalid", - ["field_field2"] = "Descending" - }.ToReadonlyDictionary() + ["field_field2"] = "Descending", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -120,7 +120,7 @@ public class CreateIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, - }.ToReadonlyDictionary() + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -140,8 +140,8 @@ public class CreateIndexJobTests : GivenContext ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, ["field_field1"] = "Ascending", - ["field_field2"] = "Descending" - }.ToReadonlyDictionary() + ["field_field2"] = "Descending", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -156,7 +156,7 @@ public class CreateIndexJobTests : GivenContext index.Should().BeEquivalentTo( [ new IndexField("field1", SortOrder.Ascending), - new IndexField("field2", SortOrder.Descending) + new IndexField("field2", SortOrder.Descending), ]); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/DropIndexJobTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/DropIndexJobTests.cs index 0956548a9..3592291f9 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/DropIndexJobTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Indexes/DropIndexJobTests.cs @@ -36,7 +36,7 @@ public class DropIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, - ["indexName"] = "MyIndex" + ["indexName"] = "MyIndex", }); } @@ -50,8 +50,8 @@ public class DropIndexJobTests : GivenContext ["appId"] = App.Id.ToString(), ["appName"] = App.Name, ["schemaName"] = Schema.Name, - ["indexName"] = "MyIndex" - }.ToReadonlyDictionary() + ["indexName"] = "MyIndex", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -69,8 +69,8 @@ public class DropIndexJobTests : GivenContext ["appId"] = App.Id.ToString(), ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), - ["indexName"] = "MyIndex" - }.ToReadonlyDictionary() + ["indexName"] = "MyIndex", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -89,7 +89,7 @@ public class DropIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, - }.ToReadonlyDictionary() + }.ToReadonlyDictionary(), }; var context = CreateContext(job); @@ -108,8 +108,8 @@ public class DropIndexJobTests : GivenContext ["appName"] = App.Name, ["schemaId"] = Schema.Id.ToString(), ["schemaName"] = Schema.Name, - ["indexName"] = "MyIndex" - }.ToReadonlyDictionary() + ["indexName"] = "MyIndex", + }.ToReadonlyDictionary(), }; var context = CreateContext(job); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/CalculatePreviewTextTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/CalculatePreviewTextTests.cs index a4ff7cc18..fd102b3c0 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/CalculatePreviewTextTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/CalculatePreviewTextTests.cs @@ -73,7 +73,7 @@ public class CalculatePreviewTextTests : GivenContext .Add("text", "Text3"), JsonValue.Object() .Add("type", "text") - .Add("text", "Text4"))))) + .Add("text", "Text4"))))), }; return content; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs index 7f855e9ef..93f63b9af 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryParserTests.cs @@ -288,7 +288,7 @@ public class ContentQueryParserTests : GivenContext var query = Q.Empty.WithJsonQuery( new Query { - Filter = new CompareFilter("data.firstName.iv", CompareOperator.Equals, JsonValue.Create("ABC")) + Filter = new CompareFilter("data.firstName.iv", CompareOperator.Equals, JsonValue.Create("ABC")), }); var q = await sut.ParseAsync(ApiContext, query, Schema, CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs index 5e619a218..786955991 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ContentQueryServiceTests.cs @@ -32,9 +32,9 @@ public class ContentQueryServiceTests : GivenContext { Scripts = new SchemaScripts { - Query = "" + Query = "", }, - IsPublished = true + IsPublished = true, }; SetupEnricher(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs index b37162f0d..0fb78ef63 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ConvertDataTests.cs @@ -64,7 +64,7 @@ public class ConvertDataTests : GivenContext var content = CreateContent() with { - Data = source + Data = source, }; var expected = @@ -97,7 +97,7 @@ public class ConvertDataTests : GivenContext var content = CreateContent() with { - Data = source + Data = source, }; var expected = @@ -136,7 +136,7 @@ public class ConvertDataTests : GivenContext var content = CreateContent() with { - Data = source + Data = source, }; var expected = diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs index 5387119f9..8ae4acbab 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichForCachingTests.cs @@ -43,7 +43,7 @@ public class EnrichForCachingTests : GivenContext "X-NoResolveLanguages", "X-ResolveFlow", "X-ResolveUrls", - "X-Unpublished" + "X-Unpublished", ], headers); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs index 6ee7c86d6..79f505406 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/EnrichWithWorkflowsTests.cs @@ -30,7 +30,7 @@ public class EnrichWithWorkflowsTests : GivenContext var nexts = new[] { - new StatusInfo(Status.Published, StatusColors.Published) + new StatusInfo(Status.Published, StatusColors.Published), }; A.CallTo(() => workflow.GetNextAsync(content, content.Status, FrontendContext.UserPrincipal)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs index b2742a9d3..f6cf87c02 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveAssetsTests.cs @@ -33,13 +33,13 @@ public class ResolveAssetsTests : GivenContext { ResolveFirst = true, MinItems = 2, - MaxItems = 3 + MaxItems = 3, }) .AddAssets(2, "asset2", Partitioning.Language, new AssetsFieldProperties { ResolveFirst = true, MinItems = 1, - MaxItems = 1 + MaxItems = 1, }) .SetFieldsInLists(FieldNames.Create("asset1", "asset2")); @@ -74,7 +74,7 @@ public class ResolveAssetsTests : GivenContext [doc1.Id]), CreateContent( [doc2.Id], - [doc2.Id]) + [doc2.Id]), }; A.CallTo(() => assetQuery.QueryAsync( @@ -106,7 +106,7 @@ public class ResolveAssetsTests : GivenContext [img2.Id, img1.Id]), CreateContent( [doc1.Id], - [doc2.Id, doc1.Id]) + [doc2.Id, doc1.Id]), }; A.CallTo(() => assetQuery.QueryAsync( @@ -141,7 +141,7 @@ public class ResolveAssetsTests : GivenContext { var contents = new[] { - CreateContent([DomainId.NewGuid()], []) + CreateContent([DomainId.NewGuid()], []), }; await sut.EnrichAsync(ApiContext, contents, schemaProvider, CancellationToken); @@ -157,7 +157,7 @@ public class ResolveAssetsTests : GivenContext { var contents = new[] { - CreateContent([DomainId.NewGuid()], []) + CreateContent([DomainId.NewGuid()], []), }; await sut.EnrichAsync(FrontendContext.Clone(b => b.WithNoEnrichment(true)), contents, schemaProvider, CancellationToken); @@ -173,7 +173,7 @@ public class ResolveAssetsTests : GivenContext { var contents = new[] { - CreateContent([], []) + CreateContent([], []), }; await sut.EnrichAsync(FrontendContext, contents, schemaProvider, CancellationToken); @@ -192,7 +192,7 @@ public class ResolveAssetsTests : GivenContext var contents = new[] { - CreateContent([id1, id2], []) + CreateContent([id1, id2], []), }; await sut.EnrichAsync(FrontendContext, contents, schemaProvider, CancellationToken); @@ -215,7 +215,7 @@ public class ResolveAssetsTests : GivenContext .AddLocalized("iv", JsonValue.Array(assets1.Select(x => x.ToString())))) .AddField("asset2", new ContentFieldData() - .AddLocalized("en", JsonValue.Array(assets2.Select(x => x.ToString())))) + .AddLocalized("en", JsonValue.Array(assets2.Select(x => x.ToString())))), }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs index 656e01f10..832a0d2e4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs @@ -50,14 +50,14 @@ public class ResolveReferencesTests : GivenContext, IClassFixture contentQuery.QueryAsync( @@ -134,7 +134,7 @@ public class ResolveReferencesTests : GivenContext, IClassFixture contentQuery.QueryAsync( @@ -187,7 +187,7 @@ public class ResolveReferencesTests : GivenContext, IClassFixture contentQuery.QueryAsync( @@ -234,7 +234,7 @@ public class ResolveReferencesTests : GivenContext, IClassFixture b.WithNoEnrichment(true)), contents, schemaProvider, CancellationToken); @@ -266,7 +266,7 @@ public class ResolveReferencesTests : GivenContext, IClassFixture contentQuery.QueryAsync(A._, A.That.HasIds(referenceId1), A._)) @@ -102,7 +102,7 @@ public class ReferencesFluidExtensionTests : GivenContext var vars = new TemplateVars { - ["event"] = @event + ["event"] = @event, }; var expected = $@" @@ -127,7 +127,7 @@ public class ReferencesFluidExtensionTests : GivenContext .AddField("field2", new ContentFieldData() .AddInvariant(JsonValue.Create($"World {index}"))), - Id = referenceId + Id = referenceId, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs index 98d24ae89..a9a4f1f18 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ReferencesJintExtensionTests.cs @@ -34,14 +34,14 @@ public class ReferencesJintExtensionTests : GivenContext, IClassFixture { - [contentId] = state + [contentId] = state, }; A.CallTo(() => inner.GetAsync(A>.That.Is(contentIds), CancellationToken)) @@ -99,12 +99,12 @@ public class CachingTextIndexerStateTests : GivenContext await sut.SetAsync( [ - state + state, ], CancellationToken); await sut.SetAsync( [ - new TextContentState { UniqueContentId = contentId, State = TextState.Deleted } + new TextContentState { UniqueContentId = contentId, State = TextState.Deleted }, ], CancellationToken); var found1 = await sut.GetAsync(contentIds, CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs index b3628a6f8..12b195d3b 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Text/TextIndexerTestsBase.cs @@ -441,7 +441,7 @@ public abstract class TextIndexerTestsBase : GivenContext { var query = new GeoQuery(SchemaId.Id, field, latitude, longitude, 1000, 1000) { - SchemaId = default + SchemaId = default, }; var sut = await GetProcessAsync(); @@ -461,7 +461,7 @@ public abstract class TextIndexerTestsBase : GivenContext { var query = new TextQuery(text, 1000) { - RequiredSchemaIds = [SchemaId.Id] + RequiredSchemaIds = [SchemaId.Id], }; var sut = await GetProcessAsync(); @@ -481,7 +481,7 @@ public abstract class TextIndexerTestsBase : GivenContext { var query = new TextQuery(text, 1000) { - PreferredSchemaId = Schema.Id + PreferredSchemaId = Schema.Id, }; var sut = await GetProcessAsync(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Invitation/InvitationEventConsumerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Invitation/InvitationEventConsumerTests.cs index bfff642c7..6358e43db 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Invitation/InvitationEventConsumerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Invitation/InvitationEventConsumerTests.cs @@ -313,7 +313,7 @@ public class InvitationEventConsumerTests : GivenContext AppId = AppId, ContributorId = assigneeId, IsCreated = isNewUser, - IsAdded = isNewContributor + IsAdded = isNewContributor, }; var envelope = Envelope.Create(@event); @@ -332,7 +332,7 @@ public class InvitationEventConsumerTests : GivenContext ContributorId = assigneeId, IsCreated = isNewUser, IsAdded = isNewContributor, - TeamId = TeamId + TeamId = TeamId, }; var envelope = Envelope.Create(@event); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Jobs/DefaultJobsServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Jobs/DefaultJobsServiceTests.cs index ae581b64e..a387e0924 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Jobs/DefaultJobsServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Jobs/DefaultJobsServiceTests.cs @@ -115,7 +115,7 @@ public class DefaultJobsServiceTests : GivenContext { Id = jobId, Started = SystemClock.Instance.GetCurrentInstant(), - Stopped = SystemClock.Instance.GetCurrentInstant() + Stopped = SystemClock.Instance.GetCurrentInstant(), }; state.Snapshot.Jobs.Add(job); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/BackupRulesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/BackupRulesTests.cs index ec7e1f397..d038c13a7 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/BackupRulesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/BackupRulesTests.cs @@ -43,22 +43,22 @@ public class BackupRulesTests : GivenContext await sut.RestoreEventAsync(AppEvent(new RuleCreated { - RuleId = ruleId1 + RuleId = ruleId1, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new RuleCreated { - RuleId = ruleId2 + RuleId = ruleId2, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new RuleCreated { - RuleId = ruleId3 + RuleId = ruleId3, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new RuleDeleted { - RuleId = ruleId3 + RuleId = ruleId3, }), context, CancellationToken); var rebuildAssets = new HashSet(); @@ -71,7 +71,7 @@ public class BackupRulesTests : GivenContext Assert.Equal( [ DomainId.Combine(AppId, ruleId1), - DomainId.Combine(AppId, ruleId2) + DomainId.Combine(AppId, ruleId2), ], rebuildAssets); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs index d535a8483..d80e17dff 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs @@ -32,9 +32,9 @@ public class GuardRuleTests : GivenContext, IClassFixture { Action = new TestAction { - Url = validUrl + Url = validUrl, }, - Trigger = null! + Trigger = null!, }); await ValidationAssert.ThrowsAsync(() => GuardRule.CanCreate(command, AppProvider), @@ -48,9 +48,9 @@ public class GuardRuleTests : GivenContext, IClassFixture { Trigger = new ContentChangedTriggerV2 { - Schemas = [] + Schemas = [], }, - Action = null! + Action = null!, }); await ValidationAssert.ThrowsAsync(() => GuardRule.CanCreate(command, AppProvider), @@ -64,12 +64,12 @@ public class GuardRuleTests : GivenContext, IClassFixture { Trigger = new ContentChangedTriggerV2 { - Schemas = [] + Schemas = [], }, Action = new TestAction { - Url = validUrl - } + Url = validUrl, + }, }); await GuardRule.CanCreate(command, AppProvider); @@ -98,13 +98,13 @@ public class GuardRuleTests : GivenContext, IClassFixture { Trigger = new ContentChangedTriggerV2 { - Schemas = [] + Schemas = [], }, Action = new TestAction { - Url = validUrl + Url = validUrl, }, - Name = "NewName" + Name = "NewName", }; await GuardRule.CanUpdate(command, CreateRule(), AppProvider); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs index 3dc9c32f0..f95bb6161 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/Triggers/ContentChangedTriggerTests.cs @@ -22,7 +22,7 @@ public class ContentChangedTriggerTests : GivenContext, IClassFixture { - new ValidationError("Schema ID is required.", "Schemas") + new ValidationError("Schema ID is required.", "Schemas"), }); A.CallTo(() => AppProvider.GetSchemaAsync(AppId.Id, A._, false, default)) @@ -45,7 +45,7 @@ public class ContentChangedTriggerTests : GivenContext, IClassFixture { - new ValidationError($"Schema {SchemaId.Id} does not exist.", "Schemas") + new ValidationError($"Schema {SchemaId.Id} does not exist.", "Schemas"), }); } @@ -72,7 +72,7 @@ public class ContentChangedTriggerTests : GivenContext, IClassFixture { - new ValidationError("Num days must be between 1 and 30.", "NumDays") + new ValidationError("Num days must be between 1 and 30.", "NumDays"), }); } @@ -38,7 +38,7 @@ public class UsageTriggerValidationTests : GivenContext, IClassFixture { - new ValidationError("Num days must be between 1 and 30.", "NumDays") + new ValidationError("Num days must be between 1 and 30.", "NumDays"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs index a86e0899e..a68d49577 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleCommandMiddlewareTests.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure.Commands; namespace Squidex.Domain.Apps.Entities.Rules.DomainObject; -public sealed class RuleCommandMiddlewareTests : HandlerTestBase +public class RuleCommandMiddlewareTests : HandlerTestBase { private readonly IDomainObjectFactory domainObjectFactory = A.Fake(); private readonly IRuleEnricher ruleEnricher = A.Fake(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs index 4b6a1bc4a..ae9d40b3f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/RuleDomainObjectTests.cs @@ -100,7 +100,7 @@ public class RuleDomainObjectTests : HandlerTestBase { var command = new UpdateRule { - IsEnabled = true + IsEnabled = true, }; await ExecuteCreateAsync(); @@ -128,7 +128,7 @@ public class RuleDomainObjectTests : HandlerTestBase { var command = new UpdateRule { - IsEnabled = false + IsEnabled = false, }; await ExecuteCreateAsync(); @@ -187,12 +187,12 @@ public class RuleDomainObjectTests : HandlerTestBase { Trigger = new ContentChangedTriggerV2 { - HandleAll = false + HandleAll = false, }, Action = new TestAction { - Value = 123 - } + Value = 123, + }, }; } @@ -203,12 +203,12 @@ public class RuleDomainObjectTests : HandlerTestBase Name = "NewName", Trigger = new ContentChangedTriggerV2 { - HandleAll = true + HandleAll = true, }, Action = new TestAction { - Value = 456 - } + Value = 456, + }, }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs index 5116a0d9c..2157fd97d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleEnricherTests.cs @@ -47,7 +47,7 @@ public class RuleEnricherTests : GivenContext var stats = new Dictionary { - [source.Id] = new RuleCounters(42, 17, 12) + [source.Id] = new RuleCounters(42, 17, 12), }; A.CallTo(() => ruleUsageTracker.GetTotalByAppAsync(AppId.Id, CancellationToken)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs index a7f835789..df5f34e37 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Queries/RuleQueryServiceTests.cs @@ -27,12 +27,12 @@ public class RuleQueryServiceTests : GivenContext { var original = new List { - new Rule() + new Rule(), }; var enriched = new List { - CreateRule() + CreateRule(), }; A.CallTo(() => rulesIndex.GetRulesAsync(AppId.Id, CancellationToken)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerWorkerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerWorkerTests.cs index e4d56fa6f..d7c1a1532 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerWorkerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerWorkerTests.cs @@ -36,7 +36,7 @@ public class RuleDequeuerWorkerTests Options.Create(new RulesOptions()), log) { - Clock = clock + Clock = clock, }; } @@ -45,14 +45,14 @@ public class RuleDequeuerWorkerTests { await sut.QueryAsync(); - A.CallTo(() => ruleEventRepository.QueryPendingAsync(A._, A>._, default)) + A.CallTo(() => ruleEventRepository.QueryPendingAsync(A._, default)) .MustHaveHappened(); } [Fact] public async Task Should_ignore_repository_exceptions_and_log() { - A.CallTo(() => ruleEventRepository.QueryPendingAsync(A._, A>._, default)) + A.CallTo(() => ruleEventRepository.QueryPendingAsync(A._, default)) .Throws(new InvalidOperationException()); await sut.QueryAsync(); @@ -170,7 +170,7 @@ public class RuleDequeuerWorkerTests ActionData = actionData, ActionName = actionName, Created = clock.GetCurrentInstant(), - RuleId = DomainId.NewGuid() + RuleId = DomainId.NewGuid(), }; A.CallTo(() => @event.Id).Returns(id); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs index a2d3aa3df..3d8acfb30 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -116,7 +116,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; RuleEventWrite[]? writes = null; @@ -147,7 +147,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; RuleEventWrite[]? writes = null; @@ -176,7 +176,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; SetupRules(@event, job, default); @@ -204,7 +204,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; SetupRules(@event, job, SkipReason.Failed); @@ -232,7 +232,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; SetupRules(@event, job, default); @@ -253,7 +253,7 @@ public class RuleEnqueuerTests : GivenContext AppId = AppId.Id, ActionData = string.Empty, ActionName = string.Empty, - Created = now + Created = now, }; SetupRules(@event, job, default); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs index d151c0c53..38d9bb176 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleQueueWriterTests.cs @@ -53,7 +53,7 @@ public class RuleQueueWriterTests : GivenContext SkipReason = reason, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = null, - Job = new RuleJob() + Job = new RuleJob(), }; var writes = await EnqueueAndFlushAsync(result); @@ -69,14 +69,14 @@ public class RuleQueueWriterTests : GivenContext SkipReason = SkipReason.Failed, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = null, - Job = new RuleJob() + Job = new RuleJob(), }; var writes = await EnqueueAndFlushAsync(result); Assert.Equal(new[] { - new RuleEventWrite(result.Job, null, null) + new RuleEventWrite(result.Job, null, null), }, writes); } @@ -88,14 +88,14 @@ public class RuleQueueWriterTests : GivenContext SkipReason = SkipReason.Failed, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = new InvalidOperationException(), - Job = new RuleJob() + Job = new RuleJob(), }; var writes = await EnqueueAndFlushAsync(result); Assert.Equal(new[] { - new RuleEventWrite(result.Job, null, result.EnrichmentError) + new RuleEventWrite(result.Job, null, result.EnrichmentError), }, writes); } @@ -107,14 +107,14 @@ public class RuleQueueWriterTests : GivenContext SkipReason = SkipReason.None, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = null, - Job = new RuleJob() + Job = new RuleJob(), }; var writes = await EnqueueAndFlushAsync(result); Assert.Equal(new[] { - new RuleEventWrite(result.Job, result.Job.Created, null) + new RuleEventWrite(result.Job, result.Job.Created, null), }, writes); } @@ -126,14 +126,14 @@ public class RuleQueueWriterTests : GivenContext SkipReason = SkipReason.Disabled, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = null, - Job = new RuleJob() + Job = new RuleJob(), }; var writes = await EnqueueAndFlushAsync(result); Assert.Equal(new[] { - new RuleEventWrite(result.Job, result.Job.Created, null) + new RuleEventWrite(result.Job, result.Job.Created, null), }, writes); } @@ -145,7 +145,7 @@ public class RuleQueueWriterTests : GivenContext SkipReason = SkipReason.Disabled, EnrichedEvent = new EnrichedManualEvent(), EnrichmentError = null, - Job = new RuleJob() + Job = new RuleJob(), }; for (var i = 0; i < 250; i++) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/UsageTracking/UsageTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/UsageTracking/UsageTriggerHandlerTests.cs index e2ea4dc21..4592653d2 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/UsageTracking/UsageTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/UsageTracking/UsageTriggerHandlerTests.cs @@ -86,7 +86,7 @@ public class UsageTriggerHandlerTests : GivenContext AppId = AppId, IncludeSkipped = false, IncludeStale = false, - Rule = CreateRule() with { Trigger = trigger } + Rule = CreateRule() with { Trigger = trigger }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/BackupSchemasTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/BackupSchemasTests.cs index 32e5b47f0..59ce54acb 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/BackupSchemasTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/BackupSchemasTests.cs @@ -44,22 +44,22 @@ public class BackupSchemasTests : GivenContext await sut.RestoreEventAsync(AppEvent(new SchemaCreated { - SchemaId = schemaId1 + SchemaId = schemaId1, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new SchemaCreated { - SchemaId = schemaId2 + SchemaId = schemaId2, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new SchemaCreated { - SchemaId = schemaId3 + SchemaId = schemaId3, }), context, CancellationToken); await sut.RestoreEventAsync(AppEvent(new SchemaDeleted { - SchemaId = schemaId3 + SchemaId = schemaId3, }), context, CancellationToken); var rebuildContents = new HashSet(); @@ -72,7 +72,7 @@ public class BackupSchemasTests : GivenContext Assert.Equal( [ DomainId.Combine(AppId, schemaId1.Id), - DomainId.Combine(AppId, schemaId2.Id) + DomainId.Combine(AppId, schemaId2.Id), ], rebuildContents); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ArrayFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ArrayFieldPropertiesTests.cs index 3b270b4b8..49a5935e8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ArrayFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ArrayFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class ArrayFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems") + new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/AssetsFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/AssetsFieldPropertiesTests.cs index 0bf273179..aac002191 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/AssetsFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/AssetsFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems") + new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems"), }); } @@ -47,7 +47,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max width must be greater or equal to min width.", "MinWidth", "MaxWidth") + new ValidationError("Max width must be greater or equal to min width.", "MinWidth", "MaxWidth"), }); } @@ -71,7 +71,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max height must be greater or equal to min height.", "MinHeight", "MaxHeight") + new ValidationError("Max height must be greater or equal to min height.", "MinHeight", "MaxHeight"), }); } @@ -95,7 +95,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max size must be greater than min size.", "MinSize", "MaxSize") + new ValidationError("Max size must be greater than min size.", "MinSize", "MaxSize"), }); } @@ -109,7 +109,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight") + new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight"), }); } @@ -123,7 +123,7 @@ public class AssetsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight") + new ValidationError("If aspect width or aspect height is used both must be defined.", "AspectWidth", "AspectHeight"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/BooleanFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/BooleanFieldPropertiesTests.cs index 4bd260790..45a4da95e 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/BooleanFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/BooleanFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class BooleanFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ComponentsFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ComponentsFieldPropertiesTests.cs index 18ed38e70..1e7359cb7 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ComponentsFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ComponentsFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class ComponentsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems") + new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs index 53da8d571..8eb3a7995 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/DateTimeFieldPropertiesTests.cs @@ -22,7 +22,7 @@ public class DateTimeFieldPropertiesTests : IClassFixture { MinValue = FutureDays(10), MaxValue = FutureDays(20), - DefaultValue = FutureDays(15) + DefaultValue = FutureDays(15), }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -40,7 +40,7 @@ public class DateTimeFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max value must be greater than min value.", "MinValue", "MaxValue") + new ValidationError("Max value must be greater than min value.", "MinValue", "MaxValue"), }); } @@ -54,7 +54,7 @@ public class DateTimeFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } @@ -68,7 +68,7 @@ public class DateTimeFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Calculated default value is not a valid value.", "CalculatedDefaultValue") + new ValidationError("Calculated default value is not a valid value.", "CalculatedDefaultValue"), }); } @@ -82,7 +82,7 @@ public class DateTimeFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Calculated default value and default value cannot be used together.", "CalculatedDefaultValue", "DefaultValue") + new ValidationError("Calculated default value and default value cannot be used together.", "CalculatedDefaultValue", "DefaultValue"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/GeolocationFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/GeolocationFieldPropertiesTests.cs index a926d79c3..2eff9ebf0 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/GeolocationFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/GeolocationFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class GeolocationFieldPropertiesTests : IClassFixture { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/NumberFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/NumberFieldPropertiesTests.cs index 471bd4891..5824ebb63 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/NumberFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/NumberFieldPropertiesTests.cs @@ -21,7 +21,7 @@ public class NumberFieldPropertiesTests : IClassFixture { MinValue = 0, MaxValue = 100, - DefaultValue = 5 + DefaultValue = 5, }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -39,7 +39,7 @@ public class NumberFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max value must be greater than min value.", "MinValue", "MaxValue") + new ValidationError("Max value must be greater than min value.", "MinValue", "MaxValue"), }); } @@ -53,7 +53,7 @@ public class NumberFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Radio buttons or dropdown list need allowed values.", "AllowedValues") + new ValidationError("Radio buttons or dropdown list need allowed values.", "AllowedValues"), }); } @@ -67,7 +67,7 @@ public class NumberFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } @@ -82,7 +82,7 @@ public class NumberFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Inline editing is not allowed for Radio editor.", "InlineEditable", "Editor") + new ValidationError("Inline editing is not allowed for Radio editor.", "InlineEditable", "Editor"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs index bf0fb5124..7810462ac 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/ReferencesFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class ReferencesFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems") + new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems"), }); } @@ -37,7 +37,7 @@ public class ReferencesFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } @@ -51,7 +51,7 @@ public class ReferencesFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Can only resolve references when MaxItems is 1.", "ResolveReference", "MaxItems") + new ValidationError("Can only resolve references when MaxItems is 1.", "ResolveReference", "MaxItems"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/RichTextFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/RichTextFieldPropertiesTests.cs index beb5dfaae..6c00cca1b 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/RichTextFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/RichTextFieldPropertiesTests.cs @@ -33,7 +33,7 @@ public class RichTextFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max length must be greater or equal to min length.", "MinLength", "MaxLength") + new ValidationError("Max length must be greater or equal to min length.", "MinLength", "MaxLength"), }); } @@ -57,7 +57,7 @@ public class RichTextFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max characters must be greater or equal to min characters.", "MinCharacters", "MaxCharacters") + new ValidationError("Max characters must be greater or equal to min characters.", "MinCharacters", "MaxCharacters"), }); } @@ -81,7 +81,7 @@ public class RichTextFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max words must be greater or equal to min words.", "MinWords", "MaxWords") + new ValidationError("Max words must be greater or equal to min words.", "MinWords", "MaxWords"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/StringFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/StringFieldPropertiesTests.cs index c325f6d62..81f09d3cc 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/StringFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/StringFieldPropertiesTests.cs @@ -24,7 +24,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max length must be greater or equal to min length.", "MinLength", "MaxLength") + new ValidationError("Max length must be greater or equal to min length.", "MinLength", "MaxLength"), }); } @@ -48,7 +48,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max characters must be greater or equal to min characters.", "MinCharacters", "MaxCharacters") + new ValidationError("Max characters must be greater or equal to min characters.", "MinCharacters", "MaxCharacters"), }); } @@ -72,7 +72,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max words must be greater or equal to min words.", "MinWords", "MaxWords") + new ValidationError("Max words must be greater or equal to min words.", "MinWords", "MaxWords"), }); } @@ -96,7 +96,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Radio buttons or dropdown list need allowed values.", "AllowedValues") + new ValidationError("Radio buttons or dropdown list need allowed values.", "AllowedValues"), }); } @@ -110,7 +110,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } @@ -124,7 +124,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Content type is not a valid value.", "ContentType") + new ValidationError("Content type is not a valid value.", "ContentType"), }); } @@ -138,7 +138,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Pattern is not a valid value.", "Pattern") + new ValidationError("Pattern is not a valid value.", "Pattern"), }); } @@ -156,7 +156,7 @@ public class StringFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Inline editing is only allowed for dropdowns, slugs and input fields.", "InlineEditable", "Editor") + new ValidationError("Inline editing is only allowed for dropdowns, slugs and input fields.", "InlineEditable", "Editor"), }); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/TagsFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/TagsFieldPropertiesTests.cs index 44f7ff6da..5103968f8 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/TagsFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/TagsFieldPropertiesTests.cs @@ -23,7 +23,7 @@ public class TagsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems") + new ValidationError("Max items must be greater or equal to min items.", "MinItems", "MaxItems"), }); } @@ -47,7 +47,7 @@ public class TagsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Checkboxes or dropdown list need allowed values.", "AllowedValues") + new ValidationError("Checkboxes or dropdown list need allowed values.", "AllowedValues"), }); } @@ -61,7 +61,7 @@ public class TagsFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/UIFieldPropertiesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/UIFieldPropertiesTests.cs index 23ce04ebf..273155a92 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/UIFieldPropertiesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/FieldProperties/UIFieldPropertiesTests.cs @@ -33,7 +33,7 @@ public class UIFieldPropertiesTests : IClassFixture errors.Should().BeEquivalentTo( new List { - new ValidationError("Editor is not a valid value.", "Editor") + new ValidationError("Editor is not a valid value.", "Editor"), }); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs index 79d87136a..1d1f152b4 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/Guards/GuardSchemaTests.cs @@ -48,10 +48,10 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "invalid name", Properties = new StringFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -70,10 +70,10 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = null!, - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -92,10 +92,10 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 }, - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -115,10 +115,10 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = new StringFieldProperties(), - Partitioning = "INVALID" + Partitioning = "INVALID", }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -137,16 +137,16 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = new StringFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, new UpsertSchemaField { Name = "field1", Properties = new StringFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -171,12 +171,12 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "invalid name", - Properties = new StringFieldProperties() + Properties = new StringFieldProperties(), }, - ] + ], }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -201,12 +201,12 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "nested1", - Properties = null! + Properties = null!, }, - ] + ], }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -231,12 +231,12 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "nested1", - Properties = new ArrayFieldProperties() + Properties = new ArrayFieldProperties(), }, - ] + ], }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -261,12 +261,12 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "nested1", - Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } + Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 }, }, - ] + ], }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -292,17 +292,17 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "nested1", - Properties = new StringFieldProperties() + Properties = new StringFieldProperties(), }, new UpsertSchemaNestedField { Name = "nested1", - Properties = new StringFieldProperties() + Properties = new StringFieldProperties(), }, - ] + ], }, ], - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -323,12 +323,12 @@ public class GuardSchemaTests : GivenContext, IClassFixture Properties = new UIFieldProperties(), IsHidden = true, IsDisabled = true, - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], FieldsInLists = FieldNames.Create("data.field1"), FieldsInReferences = FieldNames.Create("data.field1"), - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -353,18 +353,18 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = new StringFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, new UpsertSchemaField { Name = "field4", Properties = new UIFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], FieldsInLists = FieldNames.Create(null!, null!, "data.field3", "data.field1", "data.field1", "data.field4"), FieldsInReferences = null, - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -391,18 +391,18 @@ public class GuardSchemaTests : GivenContext, IClassFixture { Name = "field1", Properties = new StringFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, new UpsertSchemaField { Name = "field4", Properties = new UIFieldProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, ], FieldsInLists = null, FieldsInReferences = FieldNames.Create(null!, null!, "data.field3", "data.field1", "data.field1", "data.field4"), - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -425,7 +425,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture { FieldsInLists = null, FieldsInReferences = FieldNames.Create("id"), - Name = "new-schema" + Name = "new-schema", }); ValidationAssert.Throws(() => GuardSchema.CanCreate(command), @@ -446,13 +446,13 @@ public class GuardSchemaTests : GivenContext, IClassFixture Properties = new StringFieldProperties(), IsHidden = true, IsDisabled = true, - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, new UpsertSchemaField { Name = "field2", Properties = ValidProperties(), - Partitioning = Partitioning.Invariant.Key + Partitioning = Partitioning.Invariant.Key, }, new UpsertSchemaField { @@ -464,19 +464,19 @@ public class GuardSchemaTests : GivenContext, IClassFixture new UpsertSchemaNestedField { Name = "nested1", - Properties = ValidProperties() + Properties = ValidProperties(), }, new UpsertSchemaNestedField { Name = "nested2", - Properties = ValidProperties() + Properties = ValidProperties(), }, - ] + ], }, ], FieldsInLists = FieldNames.Create("data.field1", "id"), FieldsInReferences = FieldNames.Create("data.field1"), - Name = "new-schema" + Name = "new-schema", }); GuardSchema.CanCreate(command); @@ -488,7 +488,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture var command = new ConfigureUIFields { FieldsInLists = FieldNames.Create(null!, null!, "data.field3", "data.field1", "data.field1", "data.field4"), - FieldsInReferences = null + FieldsInReferences = null, }; ValidationAssert.Throws(() => GuardSchema.CanConfigureUIFields(command, Schema), @@ -510,7 +510,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture var command = new ConfigureUIFields { FieldsInLists = null, - FieldsInReferences = FieldNames.Create(null!, null!, "data.field3", "data.field1", "data.field1", "data.field4") + FieldsInReferences = FieldNames.Create(null!, null!, "data.field3", "data.field1", "data.field1", "data.field4"), }; ValidationAssert.Throws(() => GuardSchema.CanConfigureUIFields(command, Schema), @@ -532,7 +532,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture var command = new ConfigureUIFields { FieldsInLists = null, - FieldsInReferences = FieldNames.Create("meta.id") + FieldsInReferences = FieldNames.Create("meta.id"), }; ValidationAssert.Throws(() => GuardSchema.CanConfigureUIFields(command, Schema), @@ -546,7 +546,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture var command = new ConfigureUIFields { FieldsInLists = FieldNames.Create("data.field1", "id"), - FieldsInReferences = FieldNames.Create("data.field2") + FieldsInReferences = FieldNames.Create("data.field2"), }; GuardSchema.CanConfigureUIFields(command, Schema); @@ -560,8 +560,8 @@ public class GuardSchemaTests : GivenContext, IClassFixture FieldRules = [ new FieldRuleCommand { Field = "field", Action = (FieldRuleAction)5 }, - new FieldRuleCommand() - ] + new FieldRuleCommand(), + ], }; ValidationAssert.Throws(() => GuardSchema.CanConfigureFieldRules(command), @@ -579,8 +579,8 @@ public class GuardSchemaTests : GivenContext, IClassFixture FieldRules = [ new FieldRuleCommand { Field = "field1", Action = FieldRuleAction.Disable, Condition = "a == b" }, - new FieldRuleCommand { Field = "field2" } - ] + new FieldRuleCommand { Field = "field2" }, + ], }; GuardSchema.CanConfigureFieldRules(command); @@ -591,7 +591,7 @@ public class GuardSchemaTests : GivenContext, IClassFixture { var command = new ConfigureFieldRules { - FieldRules = null + FieldRules = null, }; GuardSchema.CanConfigureFieldRules(command); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs index 3ef6a4e03..212e57c64 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/DomainObject/SchemaDomainObjectTests.cs @@ -52,7 +52,7 @@ public class SchemaDomainObjectTests : HandlerTestBase Name = SchemaId.Name, SchemaId = SchemaId.Id, Scripts = null!, - Properties = new SchemaProperties() + Properties = new SchemaProperties(), }; var actual = await PublishAsync(sut, command); @@ -80,10 +80,10 @@ public class SchemaDomainObjectTests : HandlerTestBase Nested = [ new UpsertSchemaNestedField { Name = "nested1", Properties = ValidProperties() }, - new UpsertSchemaNestedField { Name = "nested2", Properties = ValidProperties() } - ] + new UpsertSchemaNestedField { Name = "nested2", Properties = ValidProperties() }, + ], }, - ] + ], }; var actual = await PublishAsync(sut, command); @@ -96,7 +96,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { var command = new UpdateSchema { - Properties = new SchemaProperties { Label = "My Properties" } + Properties = new SchemaProperties { Label = "My Properties" }, }; await ExecuteCreateAsync(); @@ -113,8 +113,8 @@ public class SchemaDomainObjectTests : HandlerTestBase { Scripts = new SchemaScripts { - Query = "" - } + Query = "", + }, }; await ExecuteCreateAsync(); @@ -131,8 +131,8 @@ public class SchemaDomainObjectTests : HandlerTestBase { FieldRules = [ - new FieldRuleCommand { Field = "field1" } - ] + new FieldRuleCommand { Field = "field1" }, + ], }; await ExecuteCreateAsync(); @@ -147,7 +147,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { var command = new ConfigureUIFields { - FieldsInLists = FieldNames.Create($"data.{fieldName}") + FieldsInLists = FieldNames.Create($"data.{fieldName}"), }; await ExecuteCreateAsync(); @@ -163,7 +163,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { var command = new ConfigureUIFields { - FieldsInReferences = FieldNames.Create($"data.{fieldName}") + FieldsInReferences = FieldNames.Create($"data.{fieldName}"), }; await ExecuteCreateAsync(); @@ -218,8 +218,8 @@ public class SchemaDomainObjectTests : HandlerTestBase { PreviewUrls = new Dictionary { - ["Web"] = "web-url" - }.ToReadonlyDictionary() + ["Web"] = "web-url", + }.ToReadonlyDictionary(), }; await ExecuteCreateAsync(); @@ -258,7 +258,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new ReorderFields { ParentFieldId = null, - FieldIds = [2L, 1L] + FieldIds = [2L, 1L], }; await ExecuteCreateAsync(); @@ -276,7 +276,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new ReorderFields { ParentFieldId = 1, - FieldIds = [3L, 2L] + FieldIds = [3L, 2L], }; await ExecuteCreateAsync(); @@ -296,7 +296,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { ParentFieldId = null, Name = fieldName, - Properties = ValidProperties() + Properties = ValidProperties(), }; await ExecuteCreateAsync(); @@ -313,7 +313,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { ParentFieldId = 1, Name = fieldName, - Properties = ValidProperties() + Properties = ValidProperties(), }; await ExecuteCreateAsync(); @@ -331,7 +331,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { ParentFieldId = null, FieldId = 1, - Properties = new StringFieldProperties() + Properties = new StringFieldProperties(), }; await ExecuteCreateAsync(); @@ -349,7 +349,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { ParentFieldId = 1, FieldId = 2, - Properties = new StringFieldProperties() + Properties = new StringFieldProperties(), }; await ExecuteCreateAsync(); @@ -367,7 +367,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new LockField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -384,7 +384,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new LockField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -402,7 +402,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new HideField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -419,7 +419,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new HideField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -437,7 +437,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new ShowField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -455,7 +455,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new ShowField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -474,7 +474,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new DisableField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -491,7 +491,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new DisableField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -509,7 +509,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new EnableField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -527,7 +527,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new EnableField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -546,7 +546,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new DeleteField { ParentFieldId = null, - FieldId = 1 + FieldId = 1, }; await ExecuteCreateAsync(); @@ -563,7 +563,7 @@ public class SchemaDomainObjectTests : HandlerTestBase var command = new DeleteField { ParentFieldId = 1, - FieldId = 2 + FieldId = 2, }; await ExecuteCreateAsync(); @@ -580,7 +580,7 @@ public class SchemaDomainObjectTests : HandlerTestBase { var command = new SynchronizeSchema { - Category = "My-Category" + Category = "My-Category", }; await ExecuteCreateAsync(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs index 7c7d54bd6..61f2600e2 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaChangedTriggerHandlerTests.cs @@ -137,7 +137,7 @@ public class SchemaChangedTriggerHandlerTests : GivenContext { var trigger = new SchemaChangedTrigger { - Condition = condition + Condition = condition, }; action(Context(trigger)); @@ -163,7 +163,7 @@ public class SchemaChangedTriggerHandlerTests : GivenContext AppId = AppId, IncludeSkipped = true, IncludeStale = true, - Rule = CreateRule() with { Trigger = trigger } + Rule = CreateRule() with { Trigger = trigger }, }; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs index e3d5d1d48..259ee5a29 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs @@ -29,22 +29,22 @@ public class SchemaCommandsTests IsLocked = true, Properties = new StringFieldProperties { - IsRequired = true + IsRequired = true, }, - Partitioning = "language" + Partitioning = "language", }, ], - Category = "myCategory" + Category = "myCategory", }; var field = Fields.String(1, "myString", Partitioning.Language, new StringFieldProperties { - IsRequired = true + IsRequired = true, }) with { IsDisabled = true, IsHidden = true, - IsLocked = true + IsLocked = true, }; var expected = new Schema @@ -55,7 +55,7 @@ public class SchemaCommandsTests FieldsInReferences = FieldNames.Create(), Scripts = new SchemaScripts(), PreviewUrls = ReadonlyDictionary.Empty(), - Category = "myCategory" + Category = "myCategory", }; expected = expected.AddField(field); @@ -72,7 +72,7 @@ public class SchemaCommandsTests { Properties = new SchemaProperties { - Hints = "MyHints" + Hints = "MyHints", }, IsPublished = true, Fields = @@ -85,9 +85,9 @@ public class SchemaCommandsTests IsLocked = true, Properties = new StringFieldProperties { - IsRequired = true + IsRequired = true, }, - Partitioning = "language" + Partitioning = "language", }, ], FieldsInLists = FieldNames.Create( @@ -100,23 +100,23 @@ public class SchemaCommandsTests ), Scripts = new SchemaScripts { - Change = "change-script" + Change = "change-script", }, PreviewUrls = new Dictionary { - ["mobile"] = "http://mobile" + ["mobile"] = "http://mobile", }.ToReadonlyDictionary(), - Category = "myCategory" + Category = "myCategory", }; var field = Fields.String(1, "myString", Partitioning.Language, new StringFieldProperties { - IsRequired = true + IsRequired = true, }) with { IsDisabled = true, IsHidden = true, - IsLocked = true + IsLocked = true, }; var expected = new Schema @@ -124,7 +124,7 @@ public class SchemaCommandsTests Name = "my-schema", Properties = new SchemaProperties { - Hints = "MyHints" + Hints = "MyHints", }, IsPublished = true, FieldsInLists = FieldNames.Create( @@ -137,13 +137,13 @@ public class SchemaCommandsTests ), Scripts = new SchemaScripts { - Change = "change-script" + Change = "change-script", }, PreviewUrls = new Dictionary { - ["mobile"] = "http://mobile" + ["mobile"] = "http://mobile", }.ToReadonlyDictionary(), - Category = "myCategory" + Category = "myCategory", }; expected = expected.AddField(field); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaPermanentDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaPermanentDeleterTests.cs index c61ce1251..7bbed625c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaPermanentDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaPermanentDeleterTests.cs @@ -82,7 +82,7 @@ public class SchemaPermanentDeleterTests : GivenContext { AppId = AppId, SchemaId = SchemaId, - Permanent = false + Permanent = false, })); A.CallTo(() => deleter1.DeleteSchemaAsync(App, Schema, default)) @@ -102,7 +102,7 @@ public class SchemaPermanentDeleterTests : GivenContext { AppId = AppId, SchemaId = SchemaId, - Permanent = true + Permanent = true, })); A.CallTo(() => deleter1.DeleteSchemaAsync(App, Schema, default)) @@ -121,7 +121,7 @@ public class SchemaPermanentDeleterTests : GivenContext { AppId = AppId, SchemaId = SchemaId, - Permanent = false + Permanent = false, })); A.CallTo(() => deleter1.DeleteSchemaAsync(App, Schema, default)) diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasChatToolTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasChatToolTests.cs index 34750d050..17909d514 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasChatToolTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemasChatToolTests.cs @@ -28,7 +28,7 @@ public class SchemasChatToolTests : GivenContext { var chatContext = new AppChatContext { - BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppSchemasRead, App.Name).Id) + BaseContext = CreateContext(PermissionIds.ForApp(PermissionIds.AppSchemasRead, App.Name).Id), }; var tool = await sut.GetToolsAsync(chatContext, CancellationToken).FirstOrDefaultAsync(CancellationToken); @@ -53,7 +53,7 @@ public class SchemasChatToolTests : GivenContext { var chatContext = new AppChatContext { - BaseContext = FrontendContext + BaseContext = FrontendContext, }; var tool = await sut.GetToolsAsync(chatContext, CancellationToken).FirstOrDefaultAsync(CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index fd9e0e3e7..a9ef7a7b6 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -34,7 +34,7 @@ - + diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagServiceTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagServiceTests.cs index 947b2b0ea..a241ed839 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagServiceTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Tags/TagServiceTests.cs @@ -51,7 +51,7 @@ public class TagServiceTests : GivenContext await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag1"]] = 1, - [ids["tag2"]] = 1 + [ids["tag2"]] = 1, }, CancellationToken); // Clear is called by the event consumer to fill the counts again, therefore we do not delete other things. @@ -62,7 +62,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { ["tag1"] = 0, - ["tag2"] = 0 + ["tag2"] = 0, }, allTags); A.CallTo(() => state.Persistence.DeleteAsync(CancellationToken)) @@ -87,7 +87,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { - ["tag_1"] = 0 + ["tag_1"] = 0, }, allTags); } @@ -113,7 +113,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { - ["tag_2"] = 0 + ["tag_2"] = 0, }, allTags); } @@ -137,7 +137,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { - ["tag_0"] = 0 + ["tag_0"] = 0, }, allTags); } @@ -149,7 +149,7 @@ public class TagServiceTests : GivenContext await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag1"]] = 1, - [ids["tag2"]] = 2 + [ids["tag2"]] = 2, }, CancellationToken); await sut.RenameTagAsync(AppId.Id, group, "tag2", "tag1", CancellationToken); @@ -158,7 +158,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { - ["tag1"] = 3 + ["tag1"] = 3, }, allTags); } @@ -170,9 +170,9 @@ public class TagServiceTests : GivenContext Tags = new Dictionary { ["id1"] = new Tag { Name = "tag1", Count = 10 }, - ["id2"] = new Tag { Name = "tag1", Count = 20 } + ["id2"] = new Tag { Name = "tag1", Count = 20 }, }, - Alias = null! + Alias = null!, }; await sut.RebuildTagsAsync(AppId.Id, group, tags, CancellationToken); @@ -181,7 +181,7 @@ public class TagServiceTests : GivenContext Assert.Equal(new Dictionary { - ["tag1"] = 30 + ["tag1"] = 30, }, allTags); } @@ -195,9 +195,9 @@ public class TagServiceTests : GivenContext ["id1"] = new Tag { Name = "tag1 ", Count = 10 }, ["id2"] = new Tag { Name = "tag2,", Count = 20 }, ["id3"] = new Tag { Name = " tag3,", Count = 30 }, - ["id4"] = new Tag { Name = ",tag4,", Count = 40 } + ["id4"] = new Tag { Name = ",tag4,", Count = 40 }, }, - Alias = null! + Alias = null!, }; await sut.RebuildTagsAsync(AppId.Id, group, tags, CancellationToken); @@ -209,7 +209,7 @@ public class TagServiceTests : GivenContext ["tag1"] = 10, ["tag2"] = 20, ["tag3"] = 30, - ["tag4"] = 40 + ["tag4"] = 40, }, allTags); } @@ -222,9 +222,9 @@ public class TagServiceTests : GivenContext { ["id1"] = new Tag { Name = "tag1", Count = 1 }, ["id2"] = new Tag { Name = "tag2", Count = 2 }, - ["id3"] = new Tag { Name = "tag3", Count = 6 } + ["id3"] = new Tag { Name = "tag3", Count = 6 }, }, - Alias = null! + Alias = null!, }; await sut.RebuildTagsAsync(AppId.Id, group, tags, CancellationToken); @@ -235,7 +235,7 @@ public class TagServiceTests : GivenContext { ["tag1"] = 1, ["tag2"] = 2, - ["tag3"] = 6 + ["tag3"] = 6, }, allTags); var export = await sut.GetExportableTagsAsync(AppId.Id, group, CancellationToken); @@ -251,9 +251,9 @@ public class TagServiceTests : GivenContext { Alias = new Dictionary { - ["id1"] = "id2" + ["id1"] = "id2", }, - Tags = null! + Tags = null!, }; await sut.RebuildTagsAsync(AppId.Id, group, tags, CancellationToken); @@ -276,7 +276,7 @@ public class TagServiceTests : GivenContext { ["tag1"] = 0, ["tag2"] = 0, - ["tag3"] = 0 + ["tag3"] = 0, }, allTags); } @@ -288,13 +288,13 @@ public class TagServiceTests : GivenContext await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag1"]] = 1, - [ids["tag2"]] = 1 + [ids["tag2"]] = 1, }, CancellationToken); await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag2"]] = 1, - [ids["tag3"]] = 1 + [ids["tag3"]] = 1, }, CancellationToken); var allTags = await sut.GetTagsAsync(AppId.Id, group, CancellationToken); @@ -303,7 +303,7 @@ public class TagServiceTests : GivenContext { ["tag1"] = 1, ["tag2"] = 2, - ["tag3"] = 1 + ["tag3"] = 1, }, allTags); } @@ -315,13 +315,13 @@ public class TagServiceTests : GivenContext await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag1"]] = 1, - [ids["tag2"]] = 1 + [ids["tag2"]] = 1, }, CancellationToken); await sut.UpdateAsync(AppId.Id, group, new Dictionary { [ids["tag2"]] = -2, - [ids["tag3"]] = -2 + [ids["tag3"]] = -2, }, CancellationToken); var allTags = await sut.GetTagsAsync(AppId.Id, group, CancellationToken); @@ -330,7 +330,7 @@ public class TagServiceTests : GivenContext { ["tag1"] = 1, ["tag2"] = 0, - ["tag3"] = 0 + ["tag3"] = 0, }, allTags); } @@ -341,7 +341,7 @@ public class TagServiceTests : GivenContext await sut.UpdateAsync(AppId.Id, group, new Dictionary { ["id1"] = 1, - ["id2"] = 1 + ["id2"] = 1, }, CancellationToken); var allTags = await sut.GetTagsAsync(AppId.Id, group, CancellationToken); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/Guards/GuardTeamContributorsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/Guards/GuardTeamContributorsTests.cs index f49360bbe..6bd76363f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/Guards/GuardTeamContributorsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/Guards/GuardTeamContributorsTests.cs @@ -72,7 +72,7 @@ public class GuardTeamContributorsTests : GivenContext, IClassFixture GuardTeamContributors.CanRemove(command, Team), @@ -192,7 +192,7 @@ public class GuardTeamContributorsTests : GivenContext, IClassFixture DisplayName = "Squidex", Authority = "https://identity.squidex.io", ClientId = "clientId", - ClientSecret = "clientSecret" + ClientSecret = "clientSecret", }; } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/TeamDomainObjectTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/TeamDomainObjectTests.cs index c26210fe3..efab3f9be 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/TeamDomainObjectTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Teams/DomainObject/TeamDomainObjectTests.cs @@ -64,7 +64,7 @@ public class TeamDomainObjectTests : HandlerTestBase DisplayName = "Squidex", Authority = "https://identity.squidex.io", ClientId = "clientId", - ClientSecret = "clientSecret" + ClientSecret = "clientSecret", }; var serviceProvider = diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/GivenContext.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/GivenContext.cs index 981889d22..fb9a01418 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/GivenContext.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/GivenContext.cs @@ -21,7 +21,7 @@ using ContentFieldData = Squidex.Domain.Apps.Core.Contents.ContentFieldData; namespace Squidex.Domain.Apps.Entities.TestHelpers; -public abstract class GivenContext +public class GivenContext { private readonly CancellationTokenSource cts = new CancellationTokenSource(); private IAppProvider? appProvider; @@ -75,7 +75,7 @@ public abstract class GivenContext public CancellationToken CancellationToken => cts.Token; - protected GivenContext() + public GivenContext() { Team = new Team { diff --git a/backend/tests/Squidex.Domain.Users.Tests/DefaultKeyStoreTests.cs b/backend/tests/Squidex.Domain.Users.Tests/DefaultKeyStoreTests.cs index e2c990949..dfe4e60a2 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/DefaultKeyStoreTests.cs +++ b/backend/tests/Squidex.Domain.Users.Tests/DefaultKeyStoreTests.cs @@ -84,12 +84,12 @@ public class DefaultKeyStoreTests { var key = new RsaSecurityKey(RSA.Create(2048)) { - KeyId = CryptoRandom.CreateUniqueId(16) + KeyId = CryptoRandom.CreateUniqueId(16), }; return new DefaultKeyStore.State { - Parameters = key.Rsa.ExportParameters(includePrivateParameters: true) + Parameters = key.Rsa.ExportParameters(includePrivateParameters: true), }; } } diff --git a/backend/tests/Squidex.Domain.Users.Tests/DefaultUserServiceTests.cs b/backend/tests/Squidex.Domain.Users.Tests/DefaultUserServiceTests.cs index 9551304cd..c28b4e00d 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/DefaultUserServiceTests.cs +++ b/backend/tests/Squidex.Domain.Users.Tests/DefaultUserServiceTests.cs @@ -169,7 +169,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Email = identity.Email! + Email = identity.Email!, }; SetupCreation(identity, 1); @@ -199,7 +199,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Consent = true + Consent = true, }; SetupCreation(identity, 1); @@ -217,7 +217,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Consent = true + Consent = true, }; SetupCreation(identity, 0); @@ -235,7 +235,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Consent = true + Consent = true, }; SetupCreation(identity, 0); @@ -253,7 +253,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Consent = true + Consent = true, }; SetupCreation(identity, 1); @@ -271,7 +271,7 @@ public class DefaultUserServiceTests var values = new UserValues { - Password = "password" + Password = "password", }; SetupCreation(identity, 1); @@ -287,7 +287,7 @@ public class DefaultUserServiceTests { var update = new UserValues { - Email = "new@email.com" + Email = "new@email.com", }; var identity = CreateIdentity(found: false); @@ -326,7 +326,7 @@ public class DefaultUserServiceTests { var update = new UserValues { - Password = "password" + Password = "password", }; var identity = CreateIdentity(found: true); @@ -348,7 +348,7 @@ public class DefaultUserServiceTests { var update = new UserValues { - Email = "new@email.com" + Email = "new@email.com", }; var identity = CreateIdentity(found: true); @@ -367,7 +367,7 @@ public class DefaultUserServiceTests { var update = new UserValues { - Consent = true + Consent = true, }; var identity = CreateIdentity(found: true); @@ -386,7 +386,7 @@ public class DefaultUserServiceTests { var update = new UserValues { - ConsentForEmails = true + ConsentForEmails = true, }; var identity = CreateIdentity(found: true); @@ -610,7 +610,7 @@ public class DefaultUserServiceTests return new IdentityUser { Id = id, - Email = $"{id}@email.com" + Email = $"{id}@email.com", }; } } diff --git a/backend/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs b/backend/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs index 0cfb48316..29c20ac09 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs +++ b/backend/tests/Squidex.Domain.Users.Tests/DefaultXmlRepositoryTests.cs @@ -11,7 +11,7 @@ using Squidex.Infrastructure.States; namespace Squidex.Domain.Users; -public sealed class DefaultXmlRepositoryTests +public class DefaultXmlRepositoryTests { private readonly ISnapshotStore store = A.Fake>(); private readonly DefaultXmlRepository sut; @@ -29,12 +29,12 @@ public sealed class DefaultXmlRepositoryTests { new SnapshotResult(default, new DefaultXmlRepository.State { - Xml = new XElement("xml").ToString() + Xml = new XElement("xml").ToString(), }, 0L), new SnapshotResult(default, new DefaultXmlRepository.State { - Xml = new XElement("xml").ToString() - }, 0L) + Xml = new XElement("xml").ToString(), + }, 0L), }.ToAsyncEnumerable()); var xml = sut.GetAllElements(); diff --git a/backend/tests/Squidex.Domain.Users.Tests/SquidexClaimExtensionsTests.cs b/backend/tests/Squidex.Domain.Users.Tests/SquidexClaimExtensionsTests.cs index 882df1c60..f6d7c82c0 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/SquidexClaimExtensionsTests.cs +++ b/backend/tests/Squidex.Domain.Users.Tests/SquidexClaimExtensionsTests.cs @@ -27,7 +27,7 @@ public class SquidexClaimExtensionsTests Assert.Equal(new[] { ("key1", "value1"), - ("key2", "value2") + ("key2", "value2"), }, result.ToArray()); } @@ -46,7 +46,7 @@ public class SquidexClaimExtensionsTests Assert.Equal(new[] { ("key1", "value1"), - ("key2", "value2") + ("key2", "value2"), }, result.ToArray()); } @@ -65,7 +65,7 @@ public class SquidexClaimExtensionsTests Assert.Equal(new[] { ("key1", JsonValue.Create("value1")), - ("key2", JsonValue.Create("value2")) + ("key2", JsonValue.Create("value2")), }, result.ToArray()); } @@ -85,7 +85,7 @@ public class SquidexClaimExtensionsTests Assert.Equal(new[] { ("key1", JsonValue.Create("value1")), - ("key2", JsonValue.Create("value2")) + ("key2", JsonValue.Create("value2")), }, result.ToArray()); } diff --git a/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs b/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs index 0561db952..868840bb0 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs @@ -38,7 +38,7 @@ public class CollectionExtensionsTests var source = new List<(int Value, int Other)> { (5, 5), - (4, 4) + (4, 4), }; var index = source.IndexOf(x => x.Other == 4); @@ -52,7 +52,7 @@ public class CollectionExtensionsTests var source = new List<(int Value, int Other)> { (5, 5), - (4, 4) + (4, 4), }; var index = source.IndexOf(x => x.Other == 2); @@ -171,12 +171,12 @@ public class CollectionExtensionsTests var lhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; var rhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; Assert.True(lhs.EqualsDictionary(rhs)); @@ -188,11 +188,11 @@ public class CollectionExtensionsTests var lhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; var rhs = new Dictionary { - [1] = 1 + [1] = 1, }; Assert.False(lhs.EqualsDictionary(rhs)); @@ -204,12 +204,12 @@ public class CollectionExtensionsTests var lhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; var rhs = new Dictionary { [1] = 1, - [3] = 3 + [3] = 3, }; Assert.False(lhs.EqualsDictionary(rhs)); @@ -221,12 +221,12 @@ public class CollectionExtensionsTests var lhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; var rhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; Assert.Equal(lhs.DictionaryHashCode(), rhs.DictionaryHashCode()); @@ -238,12 +238,12 @@ public class CollectionExtensionsTests var lhs = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }; var rhs = new Dictionary { [1] = 1, - [3] = 3 + [3] = 3, }; Assert.NotEqual(lhs.DictionaryHashCode(), rhs.DictionaryHashCode()); @@ -255,12 +255,12 @@ public class CollectionExtensionsTests var lhs = new List { 1, - 2 + 2, }; var rhs = new List { 1, - 2 + 2, }; Assert.True(lhs.EqualsList(rhs)); @@ -272,11 +272,11 @@ public class CollectionExtensionsTests var lhs = new List { 1, - 2 + 2, }; var rhs = new List { - 1 + 1, }; Assert.False(lhs.EqualsList(rhs)); @@ -288,12 +288,12 @@ public class CollectionExtensionsTests var lhs = new List { 1, - 2 + 2, }; var rhs = new List { 1, - 3 + 3, }; Assert.False(lhs.EqualsList(rhs)); @@ -305,12 +305,12 @@ public class CollectionExtensionsTests var lhs = new List { 1, - 2 + 2, }; var rhs = new List { 2, - 1 + 1, }; Assert.False(lhs.EqualsList(rhs)); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyDictionaryTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyDictionaryTests.cs index a09dd08e5..459eca756 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyDictionaryTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyDictionaryTests.cs @@ -44,28 +44,28 @@ public class ReadonlyDictionaryTests { var obj1a = new Dictionary { - [1] = 1 + [1] = 1, }.ToReadonlyDictionary(); var obj1b = new Dictionary { - [1] = 1 + [1] = 1, }.ToReadonlyDictionary(); var dictionaryOtherValue = new Dictionary { - [1] = 2 + [1] = 2, }.ToReadonlyDictionary(); var dictionaryOtherKey = new Dictionary { - [2] = 1 + [2] = 1, }.ToReadonlyDictionary(); var dictionaryOtherCount = new Dictionary { [1] = 1, - [2] = 2 + [2] = 2, }.ToReadonlyDictionary(); Assert.Equal(obj1a, obj1b); @@ -92,7 +92,7 @@ public class ReadonlyDictionaryTests { [11] = 1, [12] = 2, - [13] = 3 + [13] = 3, }.ToReadonlyDictionary(); var serialized = sut.SerializeAndDeserializeJson(); @@ -107,7 +107,7 @@ public class ReadonlyDictionaryTests { [11] = 1, [12] = 2, - [13] = 3 + [13] = 3, }); var serialized = sut.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyListTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyListTests.cs index 92836573c..afaff5db4 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyListTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Collections/ReadonlyListTests.cs @@ -68,7 +68,7 @@ public class ReadonlyListTests { 1, 2, - 3 + 3, }.ToReadonlyList(); var serialized = sut.SerializeAndDeserializeJson(); @@ -83,7 +83,7 @@ public class ReadonlyListTests { 1, 2, - 3 + 3, }); var serialized = sut.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/CustomCommandMiddlewareRunnerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/CustomCommandMiddlewareRunnerTests.cs index 3019db807..fa1ad8e63 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/CustomCommandMiddlewareRunnerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/CustomCommandMiddlewareRunnerTests.cs @@ -47,7 +47,7 @@ public class CustomCommandMiddlewareRunnerTests [ new CustomMiddleware(10), new CustomMiddleware(12), - new CustomMiddleware(14) + new CustomMiddleware(14), ]); var isNextCalled = false; diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/DefaultDomainObjectCacheTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/DefaultDomainObjectCacheTests.cs index fd4bc0296..f6c283418 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/DefaultDomainObjectCacheTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/DefaultDomainObjectCacheTests.cs @@ -36,7 +36,7 @@ public class DefaultDomainObjectCacheTests { var options = Options.Create(new DomainObjectCacheOptions { - CacheDuration = default + CacheDuration = default, }); var sut2 = new DefaultDomainObjectCache(cache, serializer, distributedCache, options); @@ -51,7 +51,7 @@ public class DefaultDomainObjectCacheTests { var options = Options.Create(new DomainObjectCacheOptions { - CacheDuration = TimeSpan.FromMinutes(-10) + CacheDuration = TimeSpan.FromMinutes(-10), }); var sut2 = new DefaultDomainObjectCache(cache, serializer, distributedCache, options); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Commands/EnrichWithTimestampCommandMiddlewareTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Commands/EnrichWithTimestampCommandMiddlewareTests.cs index af4b4793e..a598e779a 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Commands/EnrichWithTimestampCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Commands/EnrichWithTimestampCommandMiddlewareTests.cs @@ -23,7 +23,7 @@ public class EnrichWithTimestampCommandMiddlewareTests sut = new EnrichWithTimestampCommandMiddleware { - Clock = clock + Clock = clock, }; } diff --git a/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs b/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs index 562e60ba8..b79333265 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs @@ -170,7 +170,7 @@ public class DomainIdTests { Id0 = DomainId.NewGuid(), Id1 = NamedId.Of(DomainId.NewGuid(), "1"), - Id2 = NamedId.Of(DomainId.NewGuid(), "2") + Id2 = NamedId.Of(DomainId.NewGuid(), "2"), }; var serialized = obj.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerManagerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerManagerTests.cs index 2a2f2848c..59959fc2f 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerManagerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerManagerTests.cs @@ -47,18 +47,18 @@ public class EventConsumerManagerTests new SnapshotResult(DomainId.Create(consumerName1), new EventConsumerState { - Position = "1" + Position = "1", }, 1), new SnapshotResult(DomainId.Create(consumerName2), new EventConsumerState { - Position = "2" + Position = "2", }, 2), new SnapshotResult(DomainId.Create("oldConsumer"), new EventConsumerState { - Position = "2" - }, 2) + Position = "2", + }, 2), }.ToAsyncEnumerable()); var actual = await sut.GetConsumersAsync(default); @@ -67,7 +67,7 @@ public class EventConsumerManagerTests new List { new EventConsumerInfo { Name = consumerName1, Position = "1" }, - new EventConsumerInfo { Name = consumerName2, Position = "2" } + new EventConsumerInfo { Name = consumerName2, Position = "2" }, }); } @@ -84,9 +84,9 @@ public class EventConsumerManagerTests { Snapshot = new EventConsumerState { - Position = "42" + Position = "42", }, - Version = 0 + Version = 0, }; var response = await sut.StartAsync(consumerName1, default); @@ -104,9 +104,9 @@ public class EventConsumerManagerTests { Snapshot = new EventConsumerState { - Position = "42" + Position = "42", }, - Version = 0 + Version = 0, }; var response = await sut.StopAsync(consumerName1, default); @@ -124,9 +124,9 @@ public class EventConsumerManagerTests { Snapshot = new EventConsumerState { - Position = "42" + Position = "42", }, - Version = 0 + Version = 0, }; var response = await sut.ResetAsync(consumerName1, default); diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs index a0a36476d..eab20cc7d 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerProcessorTests.cs @@ -55,8 +55,8 @@ public class EventConsumerProcessorTests { Snapshot = new EventConsumerState { - Position = initialPosition - } + Position = initialPosition, + }, }; A.CallTo(() => eventStore.CreateSubscription(A>._, A._, A._)) diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerStateTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerStateTests.cs index e632d2012..d6e5a7fab 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerStateTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumerStateTests.cs @@ -19,7 +19,7 @@ public class EventConsumerStateTests Count = 1, IsStopped = true, Error = "Error", - Position = "Position" + Position = "Position", }; var serialized = state.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumersHealthCheckTests.cs b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumersHealthCheckTests.cs index 423f6da08..ac5b6e8b5 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumersHealthCheckTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/EventSourcing/Consume/EventConsumersHealthCheckTests.cs @@ -40,17 +40,17 @@ public class EventConsumersHealthCheckTests { consumers.Add(new EventConsumerInfo { - Name = "Consumer1" + Name = "Consumer1", }); consumers.Add(new EventConsumerInfo { - Name = "Consumer2" + Name = "Consumer2", }); consumers.Add(new EventConsumerInfo { - Name = "Consumer2" + Name = "Consumer2", }); var status = await sut.CheckHealthAsync(null!, ct); @@ -64,19 +64,19 @@ public class EventConsumersHealthCheckTests consumers.Add(new EventConsumerInfo { Name = "Consumer1", - Error = "Failed1" + Error = "Failed1", }); consumers.Add(new EventConsumerInfo { Name = "Consumer2", - Error = "Failed2" + Error = "Failed2", }); consumers.Add(new EventConsumerInfo { Name = "Consumer3", - Error = "Failed3" + Error = "Failed3", }); var status = await sut.CheckHealthAsync(null!, ct); @@ -90,19 +90,19 @@ public class EventConsumersHealthCheckTests consumers.Add(new EventConsumerInfo { Name = "Consumer1", - Error = "Failed1" + Error = "Failed1", }); consumers.Add(new EventConsumerInfo { Name = "Consumer2", - IsStopped = true + IsStopped = true, }); consumers.Add(new EventConsumerInfo { Name = "Consumer3", - IsStopped = false + IsStopped = false, }); var status = await sut.CheckHealthAsync(null!, ct); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs index 9d1a6b30f..e11a6a831 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Json/ClaimsPrincipalConverterTests.cs @@ -20,15 +20,15 @@ public class ClaimsPrincipalConverterTests new ClaimsIdentity( [ new Claim("email", "me@email.com"), - new Claim("username", "me@email.com") + new Claim("username", "me@email.com"), ], "Cookie"), new ClaimsIdentity( [ new Claim("user_id", "12345"), - new Claim("login", "me") + new Claim("login", "me"), ], - "Google") + "Google"), ]); var serialized = value.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs index 7be55e4fc..a2b1afa9b 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs @@ -124,7 +124,7 @@ public class JsonObjectTests new JsonValue((string?)null), JsonValue.Create((string?)null), JsonValue.Create((object?)null), - default + default, }; foreach (var json in jsons) @@ -148,7 +148,7 @@ public class JsonObjectTests new JsonValue(true), JsonValue.Create(true), JsonValue.Create((object?)true), - true + true, }; foreach (var json in jsons) @@ -173,7 +173,7 @@ public class JsonObjectTests JsonValue.Create(12.5), JsonValue.Create((object?)12.5), JsonValue.Create((object?)12.5f), - 12.5 + 12.5, }; foreach (var json in jsons) @@ -198,7 +198,7 @@ public class JsonObjectTests JsonValue.Create(12), JsonValue.Create((object?)12L), JsonValue.Create((object?)12), - 12 + 12, }; foreach (var json in jsons) @@ -222,7 +222,7 @@ public class JsonObjectTests new JsonValue("text"), JsonValue.Create("text"), JsonValue.Create((object?)"text"), - "text" + "text", }; foreach (var json in jsons) @@ -247,7 +247,7 @@ public class JsonObjectTests { JsonValue.Create(instant), JsonValue.Create((object?)instant), - instant + instant, }; foreach (var json in jsons) @@ -272,7 +272,7 @@ public class JsonObjectTests { JsonValue.Create(id), JsonValue.Create((object?)id), - id + id, }; var actual = id.ToString(); @@ -303,7 +303,7 @@ public class JsonObjectTests JsonValue.Create(input), JsonValue.Create((object?)input), JsonValue.Create(new object[] { 1, 2 }), - input + input, }; var actual = new JsonArray { 1, 2 }; @@ -332,7 +332,7 @@ public class JsonObjectTests JsonValue.Create(input), JsonValue.Create((object?)input), JsonValue.Create(input.ToDictionary(x => x.Key, x => x.Value.Value)), - input + input, }; var actual = JsonValue.Object().Add("1", 1).Add("2", 2); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Json/System/PolymorphicConverterTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Json/System/PolymorphicConverterTests.cs index c5acc6efa..a816a2bf6 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Json/System/PolymorphicConverterTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Json/System/PolymorphicConverterTests.cs @@ -32,7 +32,7 @@ public class PolymorphicConverterTests Base source = new A { - PropertyA = 42 + PropertyA = 42, }; var serialized = serializer.Deserialize(serializer.Serialize(source)); @@ -49,7 +49,7 @@ public class PolymorphicConverterTests { ["$baseType"] = "A", ["propertyA"] = 42, - ["propertyOther"] = 44 + ["propertyOther"] = 44, }; var serialized = serializer.Deserialize(serializer.Serialize(source)); @@ -66,7 +66,7 @@ public class PolymorphicConverterTests { ["$type"] = "A", ["propertyA"] = 42, - ["propertyOther"] = 44 + ["propertyOther"] = 44, }; var serialized = serializer.Deserialize(serializer.Serialize(source)); @@ -83,7 +83,7 @@ public class PolymorphicConverterTests { ["propertyB"] = 42, ["propertyOther"] = 44, - ["$baseType"] = "B" + ["$baseType"] = "B", }; var serialized = serializer.Deserialize(serializer.Serialize(source)); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Json/System/ReadOnlyCollectionTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Json/System/ReadOnlyCollectionTests.cs index 8e7a0b1ce..7c4387a7b 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Json/System/ReadOnlyCollectionTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Json/System/ReadOnlyCollectionTests.cs @@ -24,8 +24,8 @@ public class ReadOnlyCollectionTests Values = new Dictionary { [2] = 4, - [3] = 9 - } + [3] = 9, + }, }; var serialized = source.SerializeAndDeserializeJson(); @@ -41,8 +41,8 @@ public class ReadOnlyCollectionTests Values = new List { 2, - 3 - } + 3, + }, }; var serialized = source.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/LanguagesInitializerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/LanguagesInitializerTests.cs index 5a4a3194b..b463772bc 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/LanguagesInitializerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/LanguagesInitializerTests.cs @@ -9,14 +9,14 @@ using Microsoft.Extensions.Options; namespace Squidex.Infrastructure; -public sealed class LanguagesInitializerTests +public class LanguagesInitializerTests { [Fact] public async Task Should_add_custom_languages() { var options = Options.Create(new LanguagesOptions { - ["en-NO"] = "English (Norwegian)" + ["en-NO"] = "English (Norwegian)", }); var sut = new LanguagesInitializer(options); @@ -31,7 +31,7 @@ public sealed class LanguagesInitializerTests { var options = Options.Create(new LanguagesOptions { - ["en-Error"] = null! + ["en-Error"] = null!, }); var sut = new LanguagesInitializer(options); @@ -46,7 +46,7 @@ public sealed class LanguagesInitializerTests { var options = Options.Create(new LanguagesOptions { - ["de"] = "German (Germany)" + ["de"] = "German (Germany)", }); var sut = new LanguagesInitializer(options); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Log/BackgroundRequestLogStoreTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Log/BackgroundRequestLogStoreTests.cs index da47f7c1a..23a0d4af5 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Log/BackgroundRequestLogStoreTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Log/BackgroundRequestLogStoreTests.cs @@ -8,6 +8,7 @@ using System.Globalization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using NodaTime; namespace Squidex.Infrastructure.Log; @@ -66,13 +67,13 @@ public class BackgroundRequestLogStoreTests { var key = "my-key"; - var dateFrom = DateTime.Today; - var dateTo = dateFrom.AddDays(4); + var timeFrom = SystemClock.Instance.GetCurrentInstant(); + var timeTo = timeFrom.Plus(Duration.FromDays(4)); - A.CallTo(() => requestLogRepository.QueryAllAsync(key, dateFrom, dateTo, ct)) + A.CallTo(() => requestLogRepository.QueryAllAsync(key, timeFrom, timeTo, ct)) .Returns(AsyncEnumerable.Repeat(new Request { Key = key }, 1)); - var actuals = await sut.QueryAllAsync(key, dateFrom, dateTo, ct).ToListAsync(ct); + var actuals = await sut.QueryAllAsync(key, timeFrom, timeTo, ct).ToListAsync(ct); Assert.NotEmpty(actuals); } @@ -84,14 +85,14 @@ public class BackgroundRequestLogStoreTests var key = "my-key"; - var dateFrom = DateTime.Today; - var dateTo = dateFrom.AddDays(4); + var timeFrom = SystemClock.Instance.GetCurrentInstant(); + var timeTo = timeFrom.Plus(Duration.FromDays(4)); - var actuals = await sut.QueryAllAsync(key, dateFrom, dateTo, ct).ToListAsync(ct); + var actuals = await sut.QueryAllAsync(key, timeFrom, timeTo, ct).ToListAsync(ct); Assert.Empty(actuals); - A.CallTo(() => requestLogRepository.QueryAllAsync(key, dateFrom, dateTo, A._)) + A.CallTo(() => requestLogRepository.QueryAllAsync(key, timeFrom, timeTo, A._)) .MustNotHaveHappened(); } diff --git a/backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs b/backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs index 0e767775f..dd665ab04 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs @@ -135,12 +135,12 @@ public class NamedIdTests { var value = new { - value = new { id = 42, name = "my-name" } + value = new { id = 42, name = "my-name" }, }; var expected = new Wrapper { - Value = NamedId.Of(42L, "my-name") + Value = NamedId.Of(42L, "my-name"), }; var serialized = value.SerializeAndDeserializeJson(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Net/IPAddressComparerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Net/IPAddressComparerTests.cs index 51dba7062..3c9e9b5d7 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Net/IPAddressComparerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Net/IPAddressComparerTests.cs @@ -21,7 +21,7 @@ public class IPAddressComparerTests IPAddress.Parse("127.0.0.255"), IPAddress.Parse("129.0.0.1"), IPAddress.Parse("127.0.0.1"), - IPAddress.Parse("127.0.0.200") + IPAddress.Parse("127.0.0.200"), }; var sorted = source.Order(IPAddressComparer.Instance); @@ -33,7 +33,7 @@ public class IPAddressComparerTests IPAddress.Parse("127.0.0.200"), IPAddress.Parse("127.0.0.255"), IPAddress.Parse("129.0.0.1"), - IPAddress.IPv6Loopback + IPAddress.IPv6Loopback, }; Assert.Equal(expected, sorted); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Queries/FilterSchemaTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Queries/FilterSchemaTests.cs index 06afd2bf8..c33b98989 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Queries/FilterSchemaTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Queries/FilterSchemaTests.cs @@ -26,12 +26,12 @@ public class FilterSchemaTests { Fields = new[] { - new FilterField(FilterSchema.Number, "nested3") - }.ToReadonlyList() - }, "nested2") - }.ToReadonlyList() - }, "nested1") - }.ToReadonlyList() + new FilterField(FilterSchema.Number, "nested3"), + }.ToReadonlyList(), + }, "nested2"), + }.ToReadonlyList(), + }, "nested1"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) @@ -40,8 +40,8 @@ public class FilterSchemaTests { new FilterField(new FilterSchema(FilterSchemaType.Object), "nested1"), new FilterField(new FilterSchema(FilterSchemaType.Object), "nested1.nested2"), - new FilterField(new FilterSchema(FilterSchemaType.Number), "nested1.nested2.nested3") - }.ToReadonlyList() + new FilterField(new FilterSchema(FilterSchemaType.Number), "nested1.nested2.nested3"), + }.ToReadonlyList(), }; var actual = schema.Flatten(); @@ -59,16 +59,16 @@ public class FilterSchemaTests new FilterField(FilterSchema.Number, "property1"), new FilterField(FilterSchema.String, "property1"), new FilterField(FilterSchema.String, "property2"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) { Fields = new[] { - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var actual = schema.Flatten(); @@ -85,8 +85,8 @@ public class FilterSchemaTests { new FilterField(FilterSchema.String, "property1", "Description1"), new FilterField(FilterSchema.String, "property2", "Description2"), - new FilterField(FilterSchema.String, "property2", "Description3") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2", "Description3"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) @@ -94,8 +94,8 @@ public class FilterSchemaTests Fields = new[] { new FilterField(FilterSchema.String, "property1", "Description1"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var actual = schema.Flatten(); @@ -111,16 +111,16 @@ public class FilterSchemaTests Fields = new[] { new FilterField(new FilterSchema(FilterSchemaType.Object), "property1"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) { Fields = new[] { - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var actual = schema.Flatten(predicate: x => x.Type != FilterSchemaType.Object); @@ -136,16 +136,16 @@ public class FilterSchemaTests Fields = new[] { new FilterField(new FilterSchema(FilterSchemaType.Object), "property1"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) { Fields = new[] { - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var actual = new QueryModel { Schema = schema }.Flatten().Schema; @@ -161,8 +161,8 @@ public class FilterSchemaTests Fields = new[] { new FilterField(new FilterSchema(FilterSchemaType.Object), "property1"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var expected = new FilterSchema(FilterSchemaType.Object) @@ -170,8 +170,8 @@ public class FilterSchemaTests Fields = new[] { new FilterField(new FilterSchema(FilterSchemaType.Object), "property1"), - new FilterField(FilterSchema.String, "property2") - }.ToReadonlyList() + new FilterField(FilterSchema.String, "property2"), + }.ToReadonlyList(), }; var actual = new QueryModel { Schema = schema }.Flatten(onlyWithOperators: false).Schema; diff --git a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs index 21b2b2852..010d165da 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromJsonTests.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure.Validation; namespace Squidex.Infrastructure.Queries; -public sealed class QueryFromJsonTests +public class QueryFromJsonTests { private static readonly (string Name, string Operator, string Output)[] AllOps = [ @@ -27,7 +27,7 @@ public sealed class QueryFromJsonTests ("LessThanOrEqual", "le", "$FIELD <= $VALUE"), ("LessThan", "lt", "$FIELD < $VALUE"), ("NotEquals", "ne", "$FIELD != $VALUE"), - ("StartsWith", "startswith", "startsWith($FIELD, $VALUE)") + ("StartsWith", "startswith", "startsWith($FIELD, $VALUE)"), ]; private static readonly QueryModel Model = new QueryModel(); @@ -36,7 +36,7 @@ public sealed class QueryFromJsonTests { var nestedSchema = new FilterSchema(FilterSchemaType.Object) { - Fields = ReadonlyList.Create(new FilterField(FilterSchema.String, "property")) + Fields = ReadonlyList.Create(new FilterField(FilterSchema.String, "property")), }; var fields = new List @@ -58,12 +58,12 @@ public sealed class QueryFromJsonTests new FilterField(FilterSchema.String, "union"), new FilterField(FilterSchema.StringArray, "stringArray"), new FilterField(FilterSchema.StringArray, "stringArrayNullable", IsNullable: true), - new FilterField(FilterSchema.String, "nested2.value") + new FilterField(FilterSchema.String, "nested2.value"), }; var schema = new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; Model = new QueryModel { Schema = schema }; @@ -706,7 +706,7 @@ public sealed class QueryFromJsonTests { var fields = new[] { - $"{field}" + $"{field}", }; var data = new TheoryData(); @@ -733,7 +733,7 @@ public sealed class QueryFromJsonTests { $"{field}", $"json.{field}", - $"json.nested.{field}" + $"json.nested.{field}", }; var data = new TheoryData(); @@ -760,7 +760,7 @@ public sealed class QueryFromJsonTests { $"{field}", $"json.{field}", - $"json.nested.{field}" + $"json.nested.{field}", }; var data = new TheoryData(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs index 9bf3274c3..001337575 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryFromODataTests.cs @@ -38,12 +38,12 @@ public class QueryFromODataTests new FilterField(FilterSchema.Number, "incomeMioNullable", IsNullable: true), new FilterField(FilterSchema.GeoObject, "geo"), new FilterField(FilterSchema.GeoObject, "geoNullable", IsNullable: true), - new FilterField(FilterSchema.Any, "properties") + new FilterField(FilterSchema.Any, "properties"), }; var filterSchema = new FilterSchema(FilterSchemaType.Object) { - Fields = fields.ToReadonlyList() + Fields = fields.ToReadonlyList(), }; var queryModel = new QueryModel { Schema = filterSchema }; diff --git a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryJsonTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryJsonTests.cs index 94ba74e5a..cb90c562e 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryJsonTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryJsonTests.cs @@ -29,7 +29,7 @@ public class QueryJsonTests { path = "property", op, - value = 12 + value = 12, }; var filter = SerializeAndDeserialize(json); @@ -95,8 +95,8 @@ public class QueryJsonTests and = new[] { new { path = "property", op = "ge", value = 10 }, - new { path = "property", op = "lt", value = 20 } - } + new { path = "property", op = "lt", value = 20 }, + }, }; var filter = SerializeAndDeserialize(json); @@ -112,8 +112,8 @@ public class QueryJsonTests or = new[] { new { path = "property", op = "ge", value = 10 }, - new { path = "property", op = "lt", value = 20 } - } + new { path = "property", op = "lt", value = 20 }, + }, }; var filter = SerializeAndDeserialize(json); @@ -126,7 +126,7 @@ public class QueryJsonTests { var json = new { - not = new { path = "property", op = "ge", value = 10 } + not = new { path = "property", op = "ge", value = 10 }, }; var filter = SerializeAndDeserialize(json); @@ -166,9 +166,9 @@ public class QueryJsonTests and = new[] { new { path = "property", op = "ge", value = 10 }, - new { path = "property", op = "lt", value = 20 } + new { path = "property", op = "lt", value = 20 }, }, - additional = 1 + additional = 1, }; SerializeAndDeserialize(json); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryTests.cs index 188aed6b8..32d0ec020 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Queries/QueryTests.cs @@ -18,8 +18,8 @@ public class QueryTests [ new SortNode("field1", SortOrder.Ascending), new SortNode("field1", SortOrder.Ascending), - new SortNode("field2", SortOrder.Ascending) - ] + new SortNode("field2", SortOrder.Ascending), + ], }; var fields = query.GetAllFields(); @@ -27,7 +27,7 @@ public class QueryTests var expected = new HashSet { "field1", - "field2" + "field2", }; Assert.Equal(expected, fields); @@ -44,7 +44,7 @@ public class QueryTests ClrFilter.Eq("field1", 1)), ClrFilter.Or( ClrFilter.Eq("field2", 2), - ClrFilter.Eq("field2", 4))) + ClrFilter.Eq("field2", 4))), }; var fields = query.GetAllFields(); @@ -52,7 +52,7 @@ public class QueryTests var expected = new HashSet { "field1", - "field2" + "field2", }; Assert.Equal(expected, fields); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs index 5ad402499..4eac60d4f 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleMapperTests.cs @@ -68,7 +68,7 @@ public class SimpleMapperTests var obj1 = new Class1 { P1 = 6, - P2 = 8 + P2 = 8, }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -82,7 +82,7 @@ public class SimpleMapperTests var obj1 = new Class1 { P1 = 6, - P2 = 8 + P2 = 8, }; var obj2 = SimpleMapper.Map(obj1, new Class1()); @@ -95,7 +95,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = 8 + P2 = 8, }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -108,7 +108,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = 8 + P2 = 8, }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -121,7 +121,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = new ValueType(8) + P2 = new ValueType(8), }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -134,7 +134,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = 8 + P2 = 8, }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -147,7 +147,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = new ValueType(8) + P2 = new ValueType(8), }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -160,7 +160,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = null + P2 = null, }; var obj2 = SimpleMapper.Map(obj1, new Class1()); @@ -173,7 +173,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = RefToken.User("2") + P2 = RefToken.User("2"), }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -186,7 +186,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P2 = long.MaxValue + P2 = long.MaxValue, }; var obj2 = SimpleMapper.Map(obj1, new Class2()); @@ -199,7 +199,7 @@ public class SimpleMapperTests { var obj1 = new Class1 { - P1 = 10 + P1 = 10, }; var obj2 = SimpleMapper.Map(obj1, new Readonly()); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs index 95652983d..bb1867264 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionSetTests.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.TestHelpers; namespace Squidex.Infrastructure.Security; -public sealed class PermissionSetTests +public class PermissionSetTests { [Fact] public void Should_provide_collection_features() @@ -19,7 +19,7 @@ public sealed class PermissionSetTests { new Permission("c"), new Permission("b"), - new Permission("a") + new Permission("a"), }; var sut = new PermissionSet(source); diff --git a/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs index 088c71666..cb8621e83 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Security/PermissionTests.cs @@ -155,7 +155,7 @@ public class PermissionTests { new Permission("c"), new Permission("b"), - new Permission("a") + new Permission("a"), }; var sorted = source.Order().ToList(); diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/IndexDefinitionTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/IndexDefinitionTests.cs index b7219e885..d910621df 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/IndexDefinitionTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/IndexDefinitionTests.cs @@ -24,7 +24,7 @@ public class IndexDefinitionTests { var definition = new IndexDefinition { - new IndexField("field1", SortOrder.Ascending) + new IndexField("field1", SortOrder.Ascending), }; Assert.Equal("field1_asc", definition.ToName()); @@ -35,7 +35,7 @@ public class IndexDefinitionTests { var definition = new IndexDefinition { - new IndexField("field1", SortOrder.Descending) + new IndexField("field1", SortOrder.Descending), }; Assert.Equal("field1_desc", definition.ToName()); @@ -47,7 +47,7 @@ public class IndexDefinitionTests var definition = new IndexDefinition { new IndexField("field1", SortOrder.Ascending), - new IndexField("field2", SortOrder.Descending) + new IndexField("field2", SortOrder.Descending), }; Assert.Equal("field1_asc_field2_desc", definition.ToName()); diff --git a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs index 09bd14d6b..884497e0b 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/States/PersistenceBatchTests.cs @@ -44,7 +44,7 @@ public class PersistenceBatchTests SetupEventStore(new Dictionary> { [key1] = [event1_1, event1_2], - [key2] = [event2_1, event2_2] + [key2] = [event2_1, event2_2], }); await bulk.LoadAsync([key1, key2]); diff --git a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs index 57111b1c1..ecb6faaef 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/TestHelpers/MyDomainState.cs @@ -49,7 +49,7 @@ public sealed class MultipleByTwiceEvent : IEvent, IMigratedStateEvent where T : class, new() A.CallTo(() => snapshotStore.ReadAllAsync(A._)) .ReturnsLazily(() => new List> { - new SnapshotResult(id, Snapshot, Version, true) + new SnapshotResult(id, Snapshot, Version, true), }.ToAsyncEnumerable()); A.CallTo(() => PersistenceFactory.WithEventSourcing(A._, id, A._)) diff --git a/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs index 73511fe6b..11d32fee1 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/ApiUsageTrackerTests.cs @@ -50,7 +50,7 @@ public class ApiUsageTrackerTests { [ApiUsageTracker.CounterTotalBytes] = 1024, [ApiUsageTracker.CounterTotalCalls] = 4, - [ApiUsageTracker.CounterTotalElapsedMs] = 120 + [ApiUsageTracker.CounterTotalElapsedMs] = 120, }); } @@ -59,7 +59,7 @@ public class ApiUsageTrackerTests { var counters = new Counters { - [ApiUsageTracker.CounterTotalCalls] = 4 + [ApiUsageTracker.CounterTotalCalls] = 4, }; A.CallTo(() => usageTracker.GetForMonthAsync($"{key}_API", date, category, ct)) @@ -75,7 +75,7 @@ public class ApiUsageTrackerTests { var counters = new Counters { - [ApiUsageTracker.CounterTotalBytes] = 14 + [ApiUsageTracker.CounterTotalBytes] = 14, }; A.CallTo(() => usageTracker.GetForMonthAsync($"{key}_API", date, category, ct)) @@ -100,7 +100,7 @@ public class ApiUsageTrackerTests (dateFrom.AddDays(1), Counters(4, 100, 2048)), (dateFrom.AddDays(2), Counters(0, 0, 0)), (dateFrom.AddDays(3), Counters(2, 60, 1024)), - (dateFrom.AddDays(4), Counters(3, 30, 512)) + (dateFrom.AddDays(4), Counters(3, 30, 512)), ], ["*"] = [ @@ -108,14 +108,14 @@ public class ApiUsageTrackerTests (dateFrom.AddDays(1), Counters(0, 0, 0)), (dateFrom.AddDays(2), Counters(5, 90, 16)), (dateFrom.AddDays(3), Counters(0, 0, 0)), - (dateFrom.AddDays(4), Counters(0, 0, 0)) - ] + (dateFrom.AddDays(4), Counters(0, 0, 0)), + ], }; var forMonth = new Counters { [ApiUsageTracker.CounterTotalCalls] = 120, - [ApiUsageTracker.CounterTotalBytes] = 400 + [ApiUsageTracker.CounterTotalBytes] = 400, }; A.CallTo(() => usageTracker.GetForMonthAsync($"{key}_API", dateFrom, null, ct)) @@ -134,7 +134,7 @@ public class ApiUsageTrackerTests new ApiStats(dateFrom.AddDays(1), 4, 25, 2048), new ApiStats(dateFrom.AddDays(2), 0, 0, 0), new ApiStats(dateFrom.AddDays(3), 2, 30, 1024), - new ApiStats(dateFrom.AddDays(4), 3, 10, 512) + new ApiStats(dateFrom.AddDays(4), 3, 10, 512), ], ["*"] = [ @@ -142,8 +142,8 @@ public class ApiUsageTrackerTests new ApiStats(dateFrom.AddDays(1), 0, 0, 0), new ApiStats(dateFrom.AddDays(2), 5, 18, 16), new ApiStats(dateFrom.AddDays(3), 0, 0, 0), - new ApiStats(dateFrom.AddDays(4), 0, 0, 0) - ] + new ApiStats(dateFrom.AddDays(4), 0, 0, 0), + ], }); summary.Should().BeEquivalentTo(new ApiStatsSummary(20, 15, 3728, 120, 400)); @@ -155,7 +155,7 @@ public class ApiUsageTrackerTests { [ApiUsageTracker.CounterTotalBytes] = bytes, [ApiUsageTracker.CounterTotalCalls] = calls, - [ApiUsageTracker.CounterTotalElapsedMs] = elapsed + [ApiUsageTracker.CounterTotalElapsedMs] = elapsed, }; } } diff --git a/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs b/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs index 18d8d6332..5104592ac 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/UsageTracking/BackgroundUsageTrackerTests.cs @@ -92,7 +92,7 @@ public class BackgroundUsageTrackerTests new StoredUsage("category1", date.AddDays(1), Counters(a: 10, b: 15)), new StoredUsage("category1", date.AddDays(3), Counters(a: 13, b: 18)), new StoredUsage("category2", date.AddDays(5), Counters(a: 15)), - new StoredUsage("category2", date.AddDays(7), Counters(b: 22)) + new StoredUsage("category2", date.AddDays(7), Counters(b: 22)), }; A.CallTo(() => usageStore.QueryAsync(key, dateFrom, dateTo, ct)) @@ -118,7 +118,7 @@ public class BackgroundUsageTrackerTests new StoredUsage("category1", date.AddDays(1), Counters(a: 10, b: 15)), new StoredUsage("category1", date.AddDays(3), Counters(a: 13, b: 18)), new StoredUsage("category2", date.AddDays(5), Counters(a: 15)), - new StoredUsage("category2", date.AddDays(7), Counters(b: 22)) + new StoredUsage("category2", date.AddDays(7), Counters(b: 22)), }; A.CallTo(() => usageStore.QueryAsync(key, dateFrom, dateTo, ct)) @@ -152,8 +152,8 @@ public class BackgroundUsageTrackerTests (dateFrom.AddDays(1), new Counters()), (dateFrom.AddDays(2), new Counters()), (dateFrom.AddDays(3), new Counters()), - (dateFrom.AddDays(4), new Counters()) - ] + (dateFrom.AddDays(4), new Counters()), + ], }; actual.Should().BeEquivalentTo(expected); @@ -171,7 +171,7 @@ public class BackgroundUsageTrackerTests new StoredUsage("my-category", dateFrom.AddDays(3), Counters(a: 13, b: 18)), new StoredUsage("my-category", dateFrom.AddDays(4), Counters(a: 15, b: 20)), new StoredUsage(null, dateFrom.AddDays(0), Counters(a: 17, b: 22)), - new StoredUsage(null, dateFrom.AddDays(2), Counters(a: 11, b: 14)) + new StoredUsage(null, dateFrom.AddDays(2), Counters(a: 11, b: 14)), }; A.CallTo(() => usageStore.QueryAsync(key, dateFrom, dateTo, ct)) @@ -187,7 +187,7 @@ public class BackgroundUsageTrackerTests (dateFrom.AddDays(1), Counters(a: 10, b: 15)), (dateFrom.AddDays(2), Counters()), (dateFrom.AddDays(3), Counters(a: 13, b: 18)), - (dateFrom.AddDays(4), Counters(a: 15, b: 20)) + (dateFrom.AddDays(4), Counters(a: 15, b: 20)), ], ["*"] = [ @@ -195,8 +195,8 @@ public class BackgroundUsageTrackerTests (dateFrom.AddDays(1), Counters()), (dateFrom.AddDays(2), Counters(a: 11, b: 14)), (dateFrom.AddDays(3), Counters()), - (dateFrom.AddDays(4), Counters()) - ] + (dateFrom.AddDays(4), Counters()), + ], }; actual.Should().BeEquivalentTo(expected); @@ -236,7 +236,7 @@ public class BackgroundUsageTrackerTests new UsageUpdate(date, key1, "my-category", Counters(a: 1.0, b: 1000)), new UsageUpdate(date, key2, "my-category", Counters(a: 1.5, b: 5000)), new UsageUpdate(date, key3, "my-category", Counters(a: 0.4, b: 9000)), - new UsageUpdate(date, key3, "*", Counters(1, 8000)) + new UsageUpdate(date, key3, "*", Counters(1, 8000)), }, o => o.ComparingByMembers()); A.CallTo(() => usageStore.TrackUsagesAsync(A._, A._)) diff --git a/backend/tests/Squidex.Infrastructure.Tests/ValidationExceptionTests.cs b/backend/tests/Squidex.Infrastructure.Tests/ValidationExceptionTests.cs index 615473647..e8851e271 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/ValidationExceptionTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/ValidationExceptionTests.cs @@ -33,7 +33,7 @@ public class ValidationExceptionTests var errors = new List { new ValidationError("Error1"), - new ValidationError("Error2") + new ValidationError("Error2"), }; var ex = new ValidationException(errors); diff --git a/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs b/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs index 123656ef4..fafec671a 100644 --- a/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs +++ b/backend/tests/Squidex.Web.Tests/ApiExceptionFilterAttributeTests.cs @@ -32,7 +32,7 @@ public class ApiExceptionFilterAttributeTests new ValidationError("Error2", "property0"), new ValidationError("Error3", "property1", "property2"), new ValidationError("Error4", "Property3.Property4"), - new ValidationError("Error5", "Property5[0].Property6") + new ValidationError("Error5", "Property5[0].Property6"), }; var ex = new ValidationException(errors); @@ -52,7 +52,7 @@ public class ApiExceptionFilterAttributeTests "property0: Error2", "property1, property2: Error3", "Property3.Property4: Error4", - "Property5[0].Property6: Error5" + "Property5[0].Property6: Error5", }, ((ErrorDto)actual.Value!).Details); A.CallTo(log) @@ -239,7 +239,7 @@ public class ApiExceptionFilterAttributeTests return new ExceptionContext(actionContext, new List()) { - Exception = exception + Exception = exception, }; } @@ -252,12 +252,12 @@ public class ApiExceptionFilterAttributeTests var httpContext = new DefaultHttpContext { - RequestServices = services + RequestServices = services, }; var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor { - FilterDescriptors = new List() + FilterDescriptors = new List(), }); return actionContext; diff --git a/backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs b/backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs index e1176d6f0..b5fc4d232 100644 --- a/backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs +++ b/backend/tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs @@ -32,7 +32,7 @@ public class ApiPermissionAttributeTests : GivenContext { var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor { - FilterDescriptors = new List() + FilterDescriptors = new List(), }); actionExecutingContext = new ActionExecutingContext(actionContext, new List(), new Dictionary(), this); diff --git a/backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithContentIdCommandMiddlewareTests.cs b/backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithContentIdCommandMiddlewareTests.cs index ae16bd455..356cba830 100644 --- a/backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithContentIdCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithContentIdCommandMiddlewareTests.cs @@ -26,7 +26,7 @@ public class EnrichWithContentIdCommandMiddlewareTests : GivenContext { var command = new UpdateContent { - ContentId = DomainId.Create("_schemaId_") + ContentId = DomainId.Create("_schemaId_"), }; await HandleAsync(command); @@ -39,7 +39,7 @@ public class EnrichWithContentIdCommandMiddlewareTests : GivenContext { var command = new CreateContent { - ContentId = DomainId.Create("_SchemaId_") + ContentId = DomainId.Create("_SchemaId_"), }; await HandleAsync(command); @@ -52,7 +52,7 @@ public class EnrichWithContentIdCommandMiddlewareTests : GivenContext { var command = new UpdateContent { - ContentId = DomainId.Create("{custom}") + ContentId = DomainId.Create("{custom}"), }; await HandleAsync(command); diff --git a/backend/tests/Squidex.Web.Tests/ExposedValuesTests.cs b/backend/tests/Squidex.Web.Tests/ExposedValuesTests.cs index 9a69a915a..5a74de861 100644 --- a/backend/tests/Squidex.Web.Tests/ExposedValuesTests.cs +++ b/backend/tests/Squidex.Web.Tests/ExposedValuesTests.cs @@ -18,7 +18,7 @@ public class ExposedValuesTests { ["name1"] = "config1", ["name2"] = "config2", - ["name3"] = "config3" + ["name3"] = "config3", }; var configuration = A.Fake(); @@ -56,7 +56,7 @@ public class ExposedValuesTests { var values = new ExposedValues { - ["name1"] = "value1" + ["name1"] = "value1", }; Assert.Equal("name1: value1", values.ToString()); @@ -68,7 +68,7 @@ public class ExposedValuesTests var values = new ExposedValues { ["name1"] = "value1", - ["name2"] = "value2" + ["name2"] = "value2", }; Assert.Equal("name1: value1, name2: value2", values.ToString()); diff --git a/backend/tests/Squidex.Web.Tests/IgnoreHashFileProviderTests.cs b/backend/tests/Squidex.Web.Tests/IgnoreHashFileProviderTests.cs index 76f45d5cc..e752f6b30 100644 --- a/backend/tests/Squidex.Web.Tests/IgnoreHashFileProviderTests.cs +++ b/backend/tests/Squidex.Web.Tests/IgnoreHashFileProviderTests.cs @@ -43,9 +43,9 @@ public class IgnoreHashFileProviderTests (string.Empty, new[] { - fileHashed + fileHashed, } - ) + ), }; var sut = CreateSut(directories); @@ -78,15 +78,15 @@ public class IgnoreHashFileProviderTests (string.Empty, new[] { - directory + directory, } ), (directory.Name, new[] { - fileHashed + fileHashed, } - ) + ), }; var sut = CreateSut(directories); @@ -113,9 +113,9 @@ public class IgnoreHashFileProviderTests (string.Empty, new[] { - fileNormal + fileNormal, } - ) + ), }; var sut = CreateSut(directories); @@ -142,9 +142,9 @@ public class IgnoreHashFileProviderTests (string.Empty, new[] { - fileNormal + fileNormal, } - ) + ), }; var sut = CreateSut(directories); diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs index df216c7a5..072e6941c 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs @@ -36,7 +36,7 @@ public class AppResolverTests : GivenContext { actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor { - EndpointMetadata = new List() + EndpointMetadata = new List(), }); actionExecutingContext = new ActionExecutingContext(actionContext, new List(), new Dictionary(), this); @@ -131,7 +131,7 @@ public class AppResolverTests : GivenContext App = App with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader), }; A.CallTo(() => AppProvider.GetAppAsync(AppId.Name, true, httpContext.RequestAborted)) @@ -158,7 +158,7 @@ public class AppResolverTests : GivenContext App = App with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader), }; A.CallTo(() => AppProvider.GetAppAsync(AppId.Name, false, httpContext.RequestAborted)) @@ -184,7 +184,7 @@ public class AppResolverTests : GivenContext App = App with { - Clients = AppClients.Empty.Add(Client.Identifier, Role.Reader) + Clients = AppClients.Empty.Add(Client.Identifier, Role.Reader), }; A.CallTo(() => AppProvider.GetAppAsync(AppId.Name, true, httpContext.RequestAborted)) @@ -204,7 +204,7 @@ public class AppResolverTests : GivenContext App = App with { - Clients = AppClients.Empty.Add(Client.Identifier, Role.Reader).Update(Client.Identifier, allowAnonymous: true) + Clients = AppClients.Empty.Add(Client.Identifier, Role.Reader).Update(Client.Identifier, allowAnonymous: true), }; A.CallTo(() => AppProvider.GetAppAsync(AppId.Name, true, httpContext.RequestAborted)) @@ -264,7 +264,7 @@ public class AppResolverTests : GivenContext App = App with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader), }; A.CallTo(() => AppProvider.GetAppAsync(AppId.Name, false, httpContext.RequestAborted)) diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs index 98dd4fe97..93ed9269a 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/CachingFilterTests.cs @@ -38,7 +38,7 @@ public class CachingFilterTests executingContext = new ActionExecutingContext(actionContext, actionFilters, new Dictionary(), this); executedContext = new ActionExecutedContext(actionContext, actionFilters, this) { - Result = new OkResult() + Result = new OkResult(), }; sut = new CachingFilter(cachingManager); @@ -70,7 +70,7 @@ public class CachingFilterTests { executingContext.ActionDescriptor.EndpointMetadata = new List { - new IgnoreCacheFilterAttribute() + new IgnoreCacheFilterAttribute(), }; httpContext.Request.Method = HttpMethods.Get; diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs index 05a9d2284..d2fcd3642 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/SchemaResolverTests.cs @@ -35,7 +35,7 @@ public class SchemaResolverTests : GivenContext { actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor { - EndpointMetadata = new List() + EndpointMetadata = new List(), }); actionExecutingContext = new ActionExecutingContext(actionContext, new List(), new Dictionary(), this); diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/TeamResolverTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/TeamResolverTests.cs index 87a088945..1b73c50ca 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/TeamResolverTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/TeamResolverTests.cs @@ -37,7 +37,7 @@ public class TeamResolverTests : GivenContext { actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor { - EndpointMetadata = new List() + EndpointMetadata = new List(), }); actionExecutingContext = new ActionExecutingContext(actionContext, new List(), new Dictionary(), this); @@ -132,7 +132,7 @@ public class TeamResolverTests : GivenContext Team = Team with { - Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader) + Contributors = Contributors.Empty.Assign(User.Identifier, Role.Reader), }; A.CallTo(() => AppProvider.GetTeamAsync(TeamId, httpContext.RequestAborted)) diff --git a/backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs b/backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs index ee41830c0..53ec68195 100644 --- a/backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs +++ b/backend/tests/Squidex.Web.Tests/Pipeline/UsageMiddlewareTests.cs @@ -41,7 +41,7 @@ public class UsageMiddlewareTests : GivenContext sut = new UsageMiddleware(usageLog, usageGate) { - Clock = clock + Clock = clock, }; } diff --git a/backend/tests/Squidex.Web.Tests/Scripting/HttpRequestJintExtensionTests.cs b/backend/tests/Squidex.Web.Tests/Scripting/HttpRequestJintExtensionTests.cs index 9a6e16a6e..85750468c 100644 --- a/backend/tests/Squidex.Web.Tests/Scripting/HttpRequestJintExtensionTests.cs +++ b/backend/tests/Squidex.Web.Tests/Scripting/HttpRequestJintExtensionTests.cs @@ -21,14 +21,14 @@ public class HttpRequestJintExtensionTests { var extensions = new IJintExtension[] { - new HttpRequestJintExtension(httpContextAccessor) + new HttpRequestJintExtension(httpContextAccessor), }; sut = new JintScriptEngine(new MemoryCache(Options.Create(new MemoryCacheOptions())), Options.Create(new JintScriptOptions { TimeoutScript = TimeSpan.FromSeconds(2), - TimeoutExecution = TimeSpan.FromSeconds(10) + TimeoutExecution = TimeSpan.FromSeconds(10), }), extensions); } diff --git a/backend/tests/Squidex.Web.Tests/UrlDecodeRouteParamsAttributeTests.cs b/backend/tests/Squidex.Web.Tests/UrlDecodeRouteParamsAttributeTests.cs index 65d56a208..3316d81f7 100644 --- a/backend/tests/Squidex.Web.Tests/UrlDecodeRouteParamsAttributeTests.cs +++ b/backend/tests/Squidex.Web.Tests/UrlDecodeRouteParamsAttributeTests.cs @@ -22,12 +22,12 @@ public class UrlDecodeRouteParamsAttributeTests var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor { - FilterDescriptors = new List() + FilterDescriptors = new List(), }); var actionExecutingContext = new ActionExecutingContext(actionContext, new List(), new Dictionary { - ["key"] = "path%2Fto%2Fsomething" + ["key"] = "path%2Fto%2Fsomething", }, null!); sut.OnActionExecuting(actionExecutingContext); diff --git a/frontend/src/app/features/content/shared/forms/assets-editor.component.html b/frontend/src/app/features/content/shared/forms/assets-editor.component.html index c28cc9854..859c042b4 100644 --- a/frontend/src/app/features/content/shared/forms/assets-editor.component.html +++ b/frontend/src/app/features/content/shared/forms/assets-editor.component.html @@ -1,16 +1,11 @@
-
- -
-
+
- {{ "contents.assetsSelect" | sqxTranslate }} + {{ "contents.assetsUpload" | sqxTranslate }}
+
+ +
@if (hasChatBot) {
diff --git a/tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs b/tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs index 8f6bdfcdb..ca870900a 100644 --- a/tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs +++ b/tools/TestSuite/TestSuite.ApiTests/ContentCollationTests.cs @@ -34,6 +34,7 @@ public class ContentCollationTests(CreatedAppFixture fixture) : IClassFixture } [Fact] + [Trait("Category", "MongoOnly")] public async Task Should_search_content() { // STEP 1: Create content. diff --git a/tools/TestSuite/TestSuite.ApiTests/Verify/GraphQLTests.Should_query_assets.verified.txt b/tools/TestSuite/TestSuite.ApiTests/Verify/GraphQLTests.Should_query_assets.verified.txt index 3a1bf6fde..a47c0baef 100644 --- a/tools/TestSuite/TestSuite.ApiTests/Verify/GraphQLTests.Should_query_assets.verified.txt +++ b/tools/TestSuite/TestSuite.ApiTests/Verify/GraphQLTests.Should_query_assets.verified.txt @@ -34,11 +34,6 @@ metadataText: 600x600px, 19 kB, metadataPixelWidth: 600, metadataUnknown: null, - metadata: { - pixelWidth: 600, - pixelHeight: 600, - description: PNG File - }, slug: logo-squared.png } } \ No newline at end of file diff --git a/tools/TestSuite/docker-compose-base.yml b/tools/TestSuite/docker-compose-base.yml index 4da68b290..0cb034b0c 100644 --- a/tools/TestSuite/docker-compose-base.yml +++ b/tools/TestSuite/docker-compose-base.yml @@ -5,7 +5,7 @@ services: environment: - ASPNETCORE_URLS=http://+:5000 - CLUSTERING__RANDOMNAME=true - - EVENTSTORE__MONGODB__CONFIGURATION=mongodb://mongo + - EVENTSTORE__MONGODB__CONFIGURATION=mongodb://db_mongo - GRAPHQL__CACHEDURATION=00:00:00 - IDENTITY__ADMINCLIENTID=root - IDENTITY__ADMINCLIENTSECRET=xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0= @@ -16,7 +16,7 @@ services: - RULES__RULESCACHEDURATION=00:00:00 - SCRIPTING__TIMEOUTEXECUTION=00:00:10 - SCRIPTING__TIMEOUTSCRIPT=00:00:10 - - STORE__MONGODB__CONFIGURATION=mongodb://mongo + - STORE__MONGODB__CONFIGURATION=mongodb://db_mongo - TEMPLATES__LOCALURL=http://localhost:5000 - UI__HIDENEWS=true - UI__HIDEONBOARDING=true diff --git a/tools/TestSuite/docker-compose.yml b/tools/TestSuite/docker-compose.yml index 66a9d4a22..2e959c108 100644 --- a/tools/TestSuite/docker-compose.yml +++ b/tools/TestSuite/docker-compose.yml @@ -1,58 +1,106 @@ services: - mongo: - image: mongo:latest + db_mongo: + image: mongo ports: - "27019:27017" networks: - internal + db_postgres: + image: "postgres" + ports: + - "5432:5432" + environment: + - POSTGRES_USER=admin + - POSTGRES_PASSWORD=secret + - POSTGRES_DB=squidex + networks: + - internal + healthcheck: + test: ["CMD-SHELL", "pg_isready -U admin"] + interval: 10s + retries: 10 + + db_mysql: + image: mysql + ports: + - "3306:3306" + environment: + - MYSQL_DATABASE=squidex + - MYSQL_USER=admin + - MYSQL_PASSWORD=secret + - MYSQL_ROOT_PASSWORD=secret + command: --log-bin-trust-function-creators=1 --local-infile=1 + volumes: + - mysql_data:/var/lib/mysql + networks: + - internal + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "admin", "--password=secret"] + interval: 10s + retries: 10 + + db_sqlserver: + image: squidex/mssql-dev + ports: + - "1433:1433" + environment: + - SA_PASSWORD=1q2w3e!R + - ACCEPT_EULA=Y + networks: + - internal + healthcheck: + test: ["CMD-SHELL", "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 1q2w3e!R -Q 'SELECT 1'"] + interval: 10s + retries: 10 + # Base configuration - squidex1: + squidex_mongo1: extends: file: docker-compose-base.yml service: squidex_base environment: - - EVENTSTORE__MONGODB__DATABASE=squidex1 - - STORE__MONGODB__CONTENTDATABASE=squidex1_content - - STORE__MONGODB__DATABASE=squidex1 + - EVENTSTORE__MONGODB__DATABASE=squidex_mongo1 + - STORE__MONGODB__CONTENTDATABASE=squidex_mongo1_content + - STORE__MONGODB__DATABASE=squidex_mongo1 - URLS__BASEURL=http://localhost:8080 depends_on: - - mongo + - db_mongo # Hosted on path and separate worker - squidex2: + squidex_mongo2: extends: file: docker-compose-base.yml service: squidex_base environment: - CLUSTERING__WORKER=false - - EVENTSTORE__MONGODB__DATABASE=squidex2 - - STORE__MONGODB__CONTENTDATABASE=squidex2_content - - STORE__MONGODB__DATABASE=squidex2 + - EVENTSTORE__MONGODB__DATABASE=squidex_mongo2 + - STORE__MONGODB__CONTENTDATABASE=squidex_mongo2_content + - STORE__MONGODB__DATABASE=squidex_mongo2 - STORE__MONGODB__TEXTHARDCOUNT=20 - URLS__BASEPATH=squidex/ - URLS__BASEURL=http://localhost:8081/squidex/ depends_on: - - mongo + - db_mongo # Hosted on path and separate worker - squidex2_worker: + squidex_mongo2_worker: extends: file: docker-compose-base.yml service: squidex_base environment: - CLUSTERING__WORKER=true - - EVENTSTORE__MONGODB__DATABASE=squidex2 - - STORE__MONGODB__CONTENTDATABASE=squidex2_content - - STORE__MONGODB__DATABASE=squidex2 + - EVENTSTORE__MONGODB__DATABASE=squidex_mongo2 + - STORE__MONGODB__CONTENTDATABASE=squidex_mongo2_content + - STORE__MONGODB__DATABASE=squidex_mongo2 - STORE__MONGODB__TEXTHARDCOUNT=20 - URLS__BASEPATH=squidex/ - URLS__BASEURL=http://localhost:8081/squidex/ depends_on: - - mongo + - db_mongo # Separate datbases - squidex3: + squidex_mongo3: extends: file: docker-compose-base.yml service: squidex_base @@ -64,14 +112,58 @@ services: - STORE__MONGODB__DATABASE=squidex3 - URLS__BASEURL=http://localhost:8082 depends_on: - - mongo + - db_mongo + + squidex_postgres: + extends: + file: docker-compose-base.yml + service: squidex_base + environment: + - EVENTSTORE__TYPE=Sql + - STORE__TYPE=Sql + - STORE__SQL__CONNECTIONSTRING=Server=db_postgres;Port=5432;Database=squidex;Pooling=true;Username=admin;Password=secret;SSL Mode=Disable; + - STORE__SQL__PROVIDER=Postgres + - MESSAGING__TYPE=Sql + - URLS__BASEURL=http://localhost:8083 + depends_on: + db_postgres: + condition: service_healthy + + squidex_mysql: + extends: + file: docker-compose-base.yml + service: squidex_base + environment: + - EVENTSTORE__TYPE=Sql + - STORE__TYPE=Sql + - STORE__SQL__CONNECTIONSTRING=Server=db_mysql;Port=3306;Database=squidex;User=admin;Password=secret;AllowLoadLocalInfile=true; + - STORE__SQL__PROVIDER=MySql + - STORE__SQL__VERSION=9.2.0-mysql + - MESSAGING__TYPE=Sql + - URLS__BASEURL=http://localhost:8084 + depends_on: + db_mysql: + condition: service_healthy + + squidex_sqlserver: + extends: + file: docker-compose-base.yml + service: squidex_base + environment: + - EVENTSTORE__TYPE=Sql + - STORE__TYPE=Sql + - STORE__SQL__CONNECTIONSTRING=Server=db_sqlserver;Database=squidex;User=sa;Password=1q2w3e!R;TrustServerCertificate=True; + - STORE__SQL__PROVIDER=SqlServer + - MESSAGING__TYPE=Sql + - URLS__BASEURL=http://localhost:8085 + depends_on: + db_sqlserver: + condition: service_healthy resizer: image: squidex/resizer:1.3.0 networks: - internal - depends_on: - - mongo webhookcatcher: image: tarampampam/webhook-tester:2 @@ -81,48 +173,93 @@ services: networks: - internal - squidex_proxy1: + squidex_mongo_proxy1: image: squidex/caddy-proxy:2.6.2 ports: - "8080:8080" environment: - SITE_ADDRESS=http://localhost:8080 - SITE_PATH=* - - SITE_SERVER="squidex1:5000" + - SITE_SERVER="squidex_mongo1:5000" depends_on: - - squidex1 + - squidex_mongo1 networks: - internal restart: unless-stopped - squidex_proxy2: + squidex_mongo_proxy2: image: squidex/caddy-proxy-path:2.6.2 ports: - "8081:8081" environment: - SITE_ADDRESS=http://localhost:8081 - SITE_PATH=/squidex/* - - SITE_SERVER="squidex2:5000" + - SITE_SERVER="squidex_mongo2:5000" depends_on: - - squidex2 + - squidex_mongo2 networks: - internal restart: unless-stopped - squidex_proxy3: + squidex_mongo_proxy3: image: squidex/caddy-proxy-path:2.6.2 ports: - "8082:8082" environment: - SITE_ADDRESS=http://localhost:8082 - SITE_PATH=* - - SITE_SERVER="squidex3:5000" + - SITE_SERVER="squidex_mongo3:5000" + depends_on: + - squidex_mongo3 + networks: + - internal + restart: unless-stopped + + squidex_postgres_proxy: + image: squidex/caddy-proxy-path:2.6.2 + ports: + - "8083:8083" + environment: + - SITE_ADDRESS=http://localhost:8083 + - SITE_PATH=* + - SITE_SERVER="squidex_postgres:5000" depends_on: - - squidex2 + - squidex_postgres + networks: + - internal + restart: unless-stopped + + squidex_mysql_proxy: + image: squidex/caddy-proxy-path:2.6.2 + ports: + - "8084:8084" + environment: + - SITE_ADDRESS=http://localhost:8084 + - SITE_PATH=* + - SITE_SERVER="squidex_mysql:5000" + depends_on: + - squidex_mysql + networks: + - internal + restart: unless-stopped + + squidex_sqlserver_proxy: + image: squidex/caddy-proxy-path:2.6.2 + ports: + - "8085:8085" + environment: + - SITE_ADDRESS=http://localhost:8085 + - SITE_PATH=* + - SITE_SERVER="squidex_sqlserver:5000" + depends_on: + - squidex_sqlserver networks: - internal restart: unless-stopped networks: internal: - driver: bridge \ No newline at end of file + driver: bridge + +volumes: + mysql_data: \ No newline at end of file diff --git a/tools/TestSuite/local-proxy/docker-compose.yml b/tools/TestSuite/local-proxy/docker-compose.yml index 309a3490c..0ed782046 100644 --- a/tools/TestSuite/local-proxy/docker-compose.yml +++ b/tools/TestSuite/local-proxy/docker-compose.yml @@ -8,10 +8,4 @@ services: - SITE_ADDRESS=http://localhost:8081 - SITE_PATH=/squidex/* - SITE_SERVER="http://172.21.0.1:5000" - networks: - - internal - restart: unless-stopped - -networks: - internal: - driver: host \ No newline at end of file + restart: unless-stopped \ No newline at end of file diff --git a/tools/TestSuite/mysql/init.sql b/tools/TestSuite/mysql/init.sql new file mode 100644 index 000000000..3c6360fad --- /dev/null +++ b/tools/TestSuite/mysql/init.sql @@ -0,0 +1 @@ +SET GLOBAL local_infile = true; \ No newline at end of file diff --git a/tools/TestSuite/sql-server/Dockerfile b/tools/TestSuite/sql-server/Dockerfile new file mode 100644 index 000000000..431583cb7 --- /dev/null +++ b/tools/TestSuite/sql-server/Dockerfile @@ -0,0 +1,29 @@ +FROM mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04 + +# Switch to root to perform installation +USER root + +ENV ACCEPT_EULA=Y + +# Install prerequisites, add Microsoft repository, and install mssql-tools and unixodbc-dev +RUN apt-get update && \ + apt-get install -y curl gnupg2 && \ + curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - && \ + curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list -o /etc/apt/sources.list.d/mssql-release.list && \ + apt-get update && \ + # Install mssql-tools (which includes sqlcmd) and its dependencies + apt-get install -y mssql-tools unixodbc-dev && \ + # Clean up APT caches to reduce image size + rm -rf /var/lib/apt/lists/* + +# Add the SQL Server tools to the PATH environment variable +ENV PATH="${PATH}:/opt/mssql-tools/bin" + +# Revert to the default user for security +USER mssql + +# Expose the SQL Server port +EXPOSE 1433 + +# Start SQL Server when the container launches +CMD ["/opt/mssql/bin/sqlservr"] \ No newline at end of file