From 389b0dadaa2bbe12f626ac050b74fc3597311dcc Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Mon, 10 May 2021 14:40:37 +0200 Subject: [PATCH] Refactoring/simplify immutability (#693) * Simplify immutability. * More cleanup. * Code cleanup. * Fix. * More simplifications. * Just a spelling fix. --- .../Actions/Algolia/AlgoliaAction.cs | 2 +- .../Actions/AzureQueue/AzureQueueAction.cs | 4 +- .../Actions/Comment/CommentAction.cs | 2 +- .../CreateContent/CreateContentAction.cs | 2 +- .../Actions/Discourse/DiscourseAction.cs | 2 +- .../ElasticSearch/ElasticSearchAction.cs | 2 +- .../Actions/Email/EmailAction.cs | 2 +- .../Actions/Fastly/FastlyAction.cs | 2 +- .../Actions/Kafka/KafkaAction.cs | 2 +- .../Actions/Medium/MediumAction.cs | 2 +- .../Notification/NotificationAction.cs | 2 +- .../Actions/Prerender/PrerenderAction.cs | 2 +- .../Actions/Slack/SlackAction.cs | 2 +- .../Actions/Twitter/TweetAction.cs | 2 +- .../Actions/Webhook/WebhookAction.cs | 2 +- .../Migrations/OldEvents/AppPatternAdded.cs | 4 +- .../Migrations/OldEvents/AppPatternUpdated.cs | 4 +- .../src/Migrations/OldEvents/SchemaCreated.cs | 42 +-- .../Migrations/OldEvents/ScriptsConfigured.cs | 10 +- .../OldTriggers/AssetChangedTrigger.cs | 2 +- .../OldTriggers/ContentChangedTrigger.cs | 21 +- .../ContentChangedTriggerSchema.cs | 3 +- .../Apps/AppClients.cs | 28 +- .../Apps/AppContributors.cs | 16 +- .../Apps/AppSettings.cs | 8 +- .../Apps/Json/AppClientsSurrogate.cs | 33 --- .../Apps/Json/AppContributorsSurrogate.cs | 33 --- .../Apps/Json/LanguageConfigSurrogate.cs | 3 +- .../Apps/LanguageConfig.cs | 23 +- .../Apps/LanguagesConfig.cs | 3 +- .../Apps/Role.cs | 4 +- .../Apps/Roles.cs | 24 +- .../Contents/Json/WorkflowStepSurrogate.cs | 3 +- .../Contents/Json/WorkflowsSurrogate.cs | 28 -- .../Contents/NoUpdate.cs | 26 +- .../Contents/Workflow.cs | 49 ++-- .../Contents/WorkflowCondition.cs | 17 +- .../Contents/WorkflowStep.cs | 34 +-- .../Contents/WorkflowTransition.cs | 26 +- .../Contents/Workflows.cs | 34 ++- .../FodyWeavers.xml | 6 - .../FodyWeavers.xsd | 27 -- .../Freezable.cs | 39 --- .../Squidex.Domain.Apps.Core.Model/Named.cs | 2 +- .../Rules/Rule.cs | 69 ++--- .../Rules/RuleAction.cs | 7 +- .../Rules/RuleTrigger.cs | 7 +- .../Rules/Triggers/AssetChangedTriggerV2.cs | 4 +- .../Rules/Triggers/CommentTrigger.cs | 4 +- .../Triggers/ContentChangedTriggerSchemaV2.cs | 6 +- .../Rules/Triggers/ContentChangedTriggerV2.cs | 21 +- .../Rules/Triggers/ManualTrigger.cs | 2 +- .../Rules/Triggers/SchemaChangedTrigger.cs | 4 +- .../Rules/Triggers/UsageTrigger.cs | 6 +- .../Schemas/ArrayField.cs | 25 +- .../Schemas/ArrayFieldProperties.cs | 7 +- .../Schemas/AssetsFieldProperties.cs | 44 ++- .../Schemas/BooleanFieldProperties.cs | 11 +- .../Schemas/DateTimeFieldProperties.cs | 15 +- .../Schemas/FieldBase.cs} | 23 +- .../Schemas/FieldCollection.cs | 31 +- .../Schemas/FieldNames.cs | 18 +- .../Schemas/FieldProperties.cs | 19 +- .../Schemas/FieldRule.cs | 19 +- .../Schemas/FieldRules.cs | 18 +- .../Schemas/GeolocationFieldProperties.cs | 5 +- .../Schemas/JsonFieldProperties.cs | 3 +- .../Schemas/LocalizedValue.cs | 18 +- .../Schemas/NamedElementPropertiesBase.cs | 8 +- .../Schemas/NestedField.cs | 79 ++--- .../Schemas/NestedField{T}.cs | 27 +- .../Schemas/NumberFieldProperties.cs | 21 +- .../Schemas/ReferencesFieldProperties.cs | 36 ++- .../Schemas/RootField.cs | 87 +++--- .../Schemas/RootField{T}.cs | 25 +- .../Schemas/Schema.cs | 20 +- .../Schemas/SchemaProperties.cs | 19 +- .../Schemas/SchemaScripts.cs | 18 +- .../Schemas/StringFieldProperties.cs | 37 ++- .../Schemas/TagsFieldProperties.cs | 19 +- .../Schemas/UIFieldProperties.cs | 5 +- .../Squidex.Domain.Apps.Core.Model.csproj | 6 - .../DefaultValues/DefaultValueFactory.cs | 3 +- .../SchemaSynchronizer.cs | 10 +- .../GenerateJsonSchema/JsonTypeVisitor.cs | 2 +- .../Apps/DomainObject/AppDomainObject.cs | 10 +- .../Templates/Builders/AssetFieldBuilder.cs | 10 +- .../Templates/Builders/BooleanFieldBuilder.cs | 6 +- .../Builders/DateTimeFieldBuilder.cs | 6 +- .../Apps/Templates/Builders/FieldBuilder.cs | 17 +- .../Builders/ReferencesFieldBuilder.cs | 6 +- .../Apps/Templates/Builders/SchemaBuilder.cs | 10 +- .../Templates/Builders/StringFieldBuilder.cs | 39 ++- .../Templates/Builders/TagsFieldBuilder.cs | 7 +- .../Schemas/Commands/FieldRuleCommand.cs | 5 +- .../Schemas/Commands/IUpsertCommand.cs | 42 +-- .../Schemas/Commands/UpsertSchemaFieldBase.cs | 2 +- .../Schemas/SchemaCreatedFieldBase.cs | 2 +- .../Squidex.Infrastructure/Cloneable{T}.cs | 19 -- .../CollectionExtensions.cs | 129 +++++++-- .../Collections/ImmutableDictionary.cs | 45 ++- .../ImmutableDictionary{TKey,TValue}.cs | 87 ++---- .../Collections/ImmutableList.cs | 49 ++++ .../Collections/ImmutableList{T}.cs | 43 +++ .../Collections/ReadOnlyCollection.cs | 36 --- .../Squidex.Infrastructure/FodyWeavers.xml | 3 - .../Squidex.Infrastructure/FodyWeavers.xsd | 26 -- .../src/Squidex.Infrastructure/IFreezable.cs | 14 - .../Newtonsoft/ConverterContractResolver.cs | 15 + .../Queries/OData/PropertyPathVisitor.cs | 4 +- .../Queries/PropertyPath.cs | 28 +- .../Reflection/Equality/ArrayComparer.cs | 40 --- .../Reflection/Equality/CollectionComparer.cs | 69 ----- .../Equality/DeepEqualityComparer.cs | 32 --- .../Reflection/Equality/DefaultComparer.cs | 36 --- .../DictionaryComparer{TKey,TValue}.cs | 37 --- .../Reflection/Equality/IDeepComparer.cs | 14 - .../Reflection/Equality/NoopComparer.cs | 17 -- .../Reflection/Equality/ObjectComparer.cs | 50 ---- .../Reflection/Equality/SetComparer.cs | 35 --- .../Reflection/SimpleEquals.cs | 128 --------- .../Security/PermissionSet.cs | 44 +-- .../Apps/Models/UpdateAppSettingsDto.cs | 4 +- .../Apps/Models/UpdateWorkflowDto.cs | 6 +- .../Apps/Models/WorkflowStepDto.cs | 3 +- .../Triggers/ContentChangedRuleTriggerDto.cs | 2 +- .../Schemas/Models/FieldPropertiesDto.cs | 4 +- .../Models/Fields/AssetsFieldPropertiesDto.cs | 8 +- .../Models/Fields/NumberFieldPropertiesDto.cs | 4 +- .../Fields/ReferencesFieldPropertiesDto.cs | 8 +- .../Models/Fields/StringFieldPropertiesDto.cs | 4 +- .../Models/Fields/TagsFieldPropertiesDto.cs | 8 +- .../Schemas/Models/SchemaPropertiesDto.cs | 4 +- .../Schemas/Models/UpdateSchemaDto.cs | 4 +- .../src/Squidex/Config/Domain/AppsServices.cs | 2 +- .../Config/Domain/SerializationServices.cs | 3 - .../Model/Apps/AppClientJsonTests.cs | 3 +- .../Model/Apps/AppClientsTests.cs | 15 +- .../Model/Apps/AppContributorsJsonTests.cs | 3 +- .../Model/Apps/AppImageTests.cs | 3 +- .../Model/Apps/AppPlanTests.cs | 3 +- .../Model/Apps/LanguagesConfigTests.cs | 15 +- .../Model/Apps/RolesTests.cs | 2 +- .../Model/Contents/ContentFieldDataTests.cs | 3 +- .../Model/Contents/WorkflowJsonTests.cs | 20 +- .../Model/Contents/WorkflowTests.cs | 10 +- .../Model/Contents/WorkflowsJsonTests.cs | 3 +- .../Model/Rules/RuleTests.cs | 15 +- .../Model/Schemas/FieldCompareTests.cs | 22 +- .../Model/Schemas/SchemaFieldTests.cs | 10 - .../DefaultValues/DefaultValuesTests.cs | 42 +-- .../SchemaSynchronizerTests.cs | 6 +- .../HandleRules/RuleElementRegistryTests.cs | 4 +- .../HandleRules/RuleServiceTests.cs | 6 +- .../ValidateContent/NumberFieldTests.cs | 2 +- .../ValidateContent/StringFieldTests.cs | 2 +- .../ValidateContent/TagsFieldTests.cs | 2 +- .../Validators/AssetsValidatorTests.cs | 2 +- .../TestHelpers/TestUtils.cs | 52 +--- .../Apps/DomainObject/Guards/GuardAppTests.cs | 12 +- .../Guards/GuardAppWorkflowTests.cs | 17 +- .../ContentChangedTriggerHandlerTests.cs | 21 +- .../DefaultWorkflowsValidatorTests.cs | 11 +- .../Contents/DynamicContentWorkflowTests.cs | 19 +- .../DomainObject/Guards/GuardRuleTests.cs | 8 +- .../Triggers/ContentChangedTriggerTests.cs | 8 +- .../DomainObject/RuleDomainObjectTests.cs | 4 +- .../Rules/RuleEnqueuerTests.cs | 4 +- .../NumberFieldPropertiesTests.cs | 4 +- .../StringFieldPropertiesTests.cs | 4 +- .../DomainObject/Guards/GuardSchemaTests.cs | 24 +- .../DomainObject/SchemaDomainObjectTests.cs | 6 +- .../Schemas/SchemaCommandsTests.cs | 8 +- .../CollectionExtensionsTests.cs | 96 +++++-- .../Collections/ImmutableDictionaryTests.cs | 68 +++++ .../Collections/ImmutableListTests.cs | 62 ++++ .../DomainIdTests.cs | 12 +- .../Json/Objects/JsonObjectTests.cs | 73 ++--- .../Reflection/SimpleEqualsTests.cs | 270 ------------------ 179 files changed, 1362 insertions(+), 2232 deletions(-) delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd delete mode 100644 backend/src/Squidex.Domain.Apps.Core.Model/Freezable.cs rename backend/src/{Squidex.Infrastructure/Cloneable.cs => Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs} (52%) delete mode 100644 backend/src/Squidex.Infrastructure/Cloneable{T}.cs create mode 100644 backend/src/Squidex.Infrastructure/Collections/ImmutableList.cs create mode 100644 backend/src/Squidex.Infrastructure/Collections/ImmutableList{T}.cs delete mode 100644 backend/src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs delete mode 100644 backend/src/Squidex.Infrastructure/FodyWeavers.xml delete mode 100644 backend/src/Squidex.Infrastructure/FodyWeavers.xsd delete mode 100644 backend/src/Squidex.Infrastructure/IFreezable.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/ArrayComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/CollectionComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/DeepEqualityComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/DefaultComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/DictionaryComparer{TKey,TValue}.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/IDeepComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/NoopComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/ObjectComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/Equality/SetComparer.cs delete mode 100644 backend/src/Squidex.Infrastructure/Reflection/SimpleEquals.cs create mode 100644 backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableDictionaryTests.cs create mode 100644 backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs delete mode 100644 backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleEqualsTests.cs diff --git a/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs b/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs index 261470ee6..a45752c60 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Algolia/AlgoliaAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Algolia Display = "Populate Algolia index", Description = "Populate a full text search index in Algolia.", ReadMore = "https://www.algolia.com/")] - public sealed class AlgoliaAction : RuleAction + public sealed record AlgoliaAction : RuleAction { [LocalizedRequired] [Display(Name = "Application Id", Description = "The application ID.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs b/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs index bf87414dd..af80dec42 100644 --- a/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/AzureQueue/AzureQueueAction.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -21,7 +21,7 @@ namespace Squidex.Extensions.Actions.AzureQueue Display = "Send to Azure Queue", Description = "Send an event to azure queue storage.", ReadMore = "https://azure.microsoft.com/en-us/services/storage/queues/")] - public sealed class AzureQueueAction : RuleAction + public sealed record AzureQueueAction : RuleAction { [LocalizedRequired] [Display(Name = "Connection", Description = "The connection string to the storage account.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs b/backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs index e8443a4e6..e24bfc7a5 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Comment/CommentAction.cs @@ -18,7 +18,7 @@ namespace Squidex.Extensions.Actions.Comment IconColor = "#3389ff", Display = "Create comment", Description = "Create a comment for a content event.")] - public sealed class CommentAction : RuleAction + public sealed record CommentAction : RuleAction { [LocalizedRequired] [Display(Name = "Text", Description = "The comment text.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentAction.cs b/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentAction.cs index 2689a30aa..f2a1fc41e 100644 --- a/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/CreateContent/CreateContentAction.cs @@ -18,7 +18,7 @@ namespace Squidex.Extensions.Actions.CreateContent IconColor = "#3389ff", Display = "Create content", Description = "Create a a new content item for any schema.")] - public sealed class CreateContentAction : RuleAction + public sealed record CreateContentAction : RuleAction { [LocalizedRequired] [Display(Name = "Data", Description = "The content data.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs b/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs index d9b210472..79a7c90bf 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Discourse/DiscourseAction.cs @@ -20,7 +20,7 @@ namespace Squidex.Extensions.Actions.Discourse Display = "Post to discourse", Description = "Create a post or topic at discourse.", ReadMore = "https://www.discourse.org/")] - public sealed class DiscourseAction : RuleAction + public sealed record DiscourseAction : RuleAction { [AbsoluteUrl] [LocalizedRequired] diff --git a/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs b/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs index c0fc24bd0..deca80d1a 100644 --- a/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchAction.cs @@ -20,7 +20,7 @@ namespace Squidex.Extensions.Actions.ElasticSearch Display = "Populate Elasticsearch index", Description = "Populate a full text search index in ElasticSearch.", ReadMore = "https://www.elastic.co/")] - public sealed class ElasticSearchAction : RuleAction + public sealed record ElasticSearchAction : RuleAction { [AbsoluteUrl] [LocalizedRequired] diff --git a/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs b/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs index 1e68dc41f..431a53d3c 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Email/EmailAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Email Display = "Send an email", Description = "Send an email with a custom SMTP server.", ReadMore = "https://en.wikipedia.org/wiki/Email")] - public sealed class EmailAction : RuleAction + public sealed record EmailAction : RuleAction { [LocalizedRequired] [Display(Name = "Server Host", Description = "The IP address or host to the SMTP server.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs b/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs index 048a0ab98..979bd2154 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Fastly/FastlyAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Fastly Display = "Purge fastly cache", Description = "Remove entries from the fastly CDN cache.", ReadMore = "https://www.fastly.com/")] - public sealed class FastlyAction : RuleAction + public sealed record FastlyAction : RuleAction { [LocalizedRequired] [Display(Name = "Api Key", Description = "The API key to grant access to Squidex.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs index 9b29d3334..52752dfe9 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Kafka/KafkaAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Kafka Display = "Push to kafka", Description = "Connect to Kafka stream and push data to that stream.", ReadMore = "https://kafka.apache.org/quickstart")] - public sealed class KafkaAction : RuleAction + public sealed record KafkaAction : RuleAction { [LocalizedRequired] [Display(Name = "Topic Name", Description = "The name of the topic.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs b/backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs index 5673ecf42..6e6fd09c1 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Medium/MediumAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Medium Display = "Post to Medium", Description = "Create a new story or post at medium.", ReadMore = "https://medium.com/")] - public sealed class MediumAction : RuleAction + public sealed record MediumAction : RuleAction { [LocalizedRequired] [Display(Name = "Access Token", Description = "The self issued access token.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs b/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs index 04769ac28..5201584f1 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Notification/NotificationAction.cs @@ -18,7 +18,7 @@ namespace Squidex.Extensions.Actions.Notification IconColor = "#3389ff", Display = "Send a notification", Description = "Send an integrated notification to a user.")] - public sealed class NotificationAction : RuleAction + public sealed record NotificationAction : RuleAction { [LocalizedRequired] [Display(Name = "User", Description = "The user id or email.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs b/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs index 6c7290615..171a6c817 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Prerender/PrerenderAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Prerender Display = "Recache URL", Description = "Prerender a javascript website for bots.", ReadMore = "https://prerender.io")] - public sealed class PrerenderAction : RuleAction + public sealed record PrerenderAction : RuleAction { [LocalizedRequired] [Display(Name = "Token", Description = "The prerender token from your account.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs b/backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs index 56471cc4c..bc0b2f12f 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Slack/SlackAction.cs @@ -20,7 +20,7 @@ namespace Squidex.Extensions.Actions.Slack Display = "Send to Slack", Description = "Create a status update to a slack channel.", ReadMore = "https://slack.com")] - public sealed class SlackAction : RuleAction + public sealed record SlackAction : RuleAction { [AbsoluteUrl] [LocalizedRequired] diff --git a/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs b/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs index 00e6c1cee..98b784da2 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Twitter/TweetAction.cs @@ -19,7 +19,7 @@ namespace Squidex.Extensions.Actions.Twitter Display = "Tweet", Description = "Tweet an update with your twitter account.", ReadMore = "https://twitter.com")] - public sealed class TweetAction : RuleAction + public sealed record TweetAction : RuleAction { [LocalizedRequired] [Display(Name = "Access Token", Description = " The generated access token.")] diff --git a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs index 8780aad7d..7916defce 100644 --- a/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs +++ b/backend/extensions/Squidex.Extensions/Actions/Webhook/WebhookAction.cs @@ -20,7 +20,7 @@ namespace Squidex.Extensions.Actions.Webhook Display = "Send webhook", Description = "Invoke HTTP endpoints on a target system.", ReadMore = "https://en.wikipedia.org/wiki/Webhook")] - public sealed class WebhookAction : RuleAction + public sealed record WebhookAction : RuleAction { [LocalizedRequired] [Display(Name = "Url", Description = "The url to the webhook.")] diff --git a/backend/src/Migrations/OldEvents/AppPatternAdded.cs b/backend/src/Migrations/OldEvents/AppPatternAdded.cs index f7d2069ba..bbebb7b7a 100644 --- a/backend/src/Migrations/OldEvents/AppPatternAdded.cs +++ b/backend/src/Migrations/OldEvents/AppPatternAdded.cs @@ -36,13 +36,13 @@ namespace Migrations.OldEvents { var newSettings = new AppSettings { - Patterns = new List(state.Settings.Patterns.Where(x => x.Name != Name || x.Regex != Pattern)) + Patterns = ImmutableList.ToImmutableList(new List(state.Settings.Patterns.Where(x => x.Name != Name || x.Regex != Pattern)) { new Pattern(Name, Pattern) { Message = Message } - }.ToReadOnlyCollection(), + }), Editors = state.Settings.Editors }; diff --git a/backend/src/Migrations/OldEvents/AppPatternUpdated.cs b/backend/src/Migrations/OldEvents/AppPatternUpdated.cs index 56b68eb69..d9dd0e172 100644 --- a/backend/src/Migrations/OldEvents/AppPatternUpdated.cs +++ b/backend/src/Migrations/OldEvents/AppPatternUpdated.cs @@ -36,13 +36,13 @@ namespace Migrations.OldEvents { var newSettings = new AppSettings { - Patterns = new List(state.Settings.Patterns.Where(x => x.Name != Name || x.Regex != Pattern)) + Patterns = ImmutableList.ToImmutableList(new List(state.Settings.Patterns.Where(x => x.Name != Name || x.Regex != Pattern)) { new Pattern(Name, Pattern) { Message = Message } - }.ToReadOnlyCollection(), + }), Editors = state.Settings.Editors }; diff --git a/backend/src/Migrations/OldEvents/SchemaCreated.cs b/backend/src/Migrations/OldEvents/SchemaCreated.cs index d88c71f56..1c6be72a9 100644 --- a/backend/src/Migrations/OldEvents/SchemaCreated.cs +++ b/backend/src/Migrations/OldEvents/SchemaCreated.cs @@ -50,7 +50,11 @@ namespace Migrations.OldEvents var partitioning = Partitioning.FromString(eventField.Partitioning); - var field = eventField.Properties.CreateRootField(totalFields, eventField.Name, partitioning); + var field = + eventField.Properties.CreateRootField( + totalFields, + eventField.Name, partitioning, + eventField); if (field is ArrayField arrayField && eventField.Nested?.Length > 0) { @@ -58,22 +62,11 @@ namespace Migrations.OldEvents { totalFields++; - var nestedField = nestedEventField.Properties.CreateNestedField(totalFields, nestedEventField.Name); - - if (nestedEventField.IsHidden) - { - nestedField = nestedField.Hide(); - } - - if (nestedEventField.IsDisabled) - { - nestedField = nestedField.Disable(); - } - - if (nestedEventField.IsLocked) - { - nestedField = nestedField.Lock(); - } + var nestedField = + nestedEventField.Properties.CreateNestedField( + totalFields, + nestedEventField.Name, + nestedEventField); arrayField = arrayField.AddField(nestedField); } @@ -81,21 +74,6 @@ namespace Migrations.OldEvents field = arrayField; } - if (eventField.IsHidden) - { - field = field.Hide(); - } - - if (eventField.IsDisabled) - { - field = field.Disable(); - } - - if (eventField.IsLocked) - { - field = field.Lock(); - } - schema = schema.AddField(field); } } diff --git a/backend/src/Migrations/OldEvents/ScriptsConfigured.cs b/backend/src/Migrations/OldEvents/ScriptsConfigured.cs index 9913f35d3..343c4e2d2 100644 --- a/backend/src/Migrations/OldEvents/ScriptsConfigured.cs +++ b/backend/src/Migrations/OldEvents/ScriptsConfigured.cs @@ -35,27 +35,27 @@ namespace Migrations.OldEvents if (!string.IsNullOrWhiteSpace(ScriptQuery)) { - scripts.Query = ScriptQuery; + scripts = scripts with { Query = ScriptQuery }; } if (!string.IsNullOrWhiteSpace(ScriptCreate)) { - scripts.Create = ScriptCreate; + scripts = scripts with { Create = ScriptCreate }; } if (!string.IsNullOrWhiteSpace(ScriptUpdate)) { - scripts.Update = ScriptUpdate; + scripts = scripts with { Update = ScriptUpdate }; } if (!string.IsNullOrWhiteSpace(ScriptDelete)) { - scripts.Delete = ScriptDelete; + scripts = scripts with { Delete = ScriptDelete }; } if (!string.IsNullOrWhiteSpace(ScriptChange)) { - scripts.Change = ScriptChange; + scripts = scripts with { Change = ScriptChange }; } return SimpleMapper.Map(this, new SchemaScriptsConfigured { Scripts = scripts }); diff --git a/backend/src/Migrations/OldTriggers/AssetChangedTrigger.cs b/backend/src/Migrations/OldTriggers/AssetChangedTrigger.cs index 94d3e3037..7bb91c365 100644 --- a/backend/src/Migrations/OldTriggers/AssetChangedTrigger.cs +++ b/backend/src/Migrations/OldTriggers/AssetChangedTrigger.cs @@ -16,7 +16,7 @@ using Squidex.Infrastructure.Reflection; namespace Migrations.OldTriggers { [TypeName(nameof(AssetChangedTrigger))] - public sealed class AssetChangedTrigger : RuleTrigger, IMigrated + public sealed record AssetChangedTrigger : RuleTrigger, IMigrated { public bool SendCreate { get; set; } diff --git a/backend/src/Migrations/OldTriggers/ContentChangedTrigger.cs b/backend/src/Migrations/OldTriggers/ContentChangedTrigger.cs index 6fc953352..5f08eafc3 100644 --- a/backend/src/Migrations/OldTriggers/ContentChangedTrigger.cs +++ b/backend/src/Migrations/OldTriggers/ContentChangedTrigger.cs @@ -6,19 +6,19 @@ // ========================================================================== using System; -using System.Collections.ObjectModel; using System.Linq; using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Triggers; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.Reflection; namespace Migrations.OldTriggers { [TypeName(nameof(ContentChangedTrigger))] - public sealed class ContentChangedTrigger : RuleTrigger, IMigrated + public sealed record ContentChangedTrigger : RuleTrigger, IMigrated { - public ReadOnlyCollection Schemas { get; set; } + public ImmutableList Schemas { get; set; } public bool HandleAll { get; set; } @@ -27,22 +27,9 @@ namespace Migrations.OldTriggers throw new NotSupportedException(); } - public override void Freeze() - { - base.Freeze(); - - if (Schemas != null) - { - foreach (var schema in Schemas) - { - schema.Freeze(); - } - } - } - public RuleTrigger Migrate() { - var schemas = new ReadOnlyCollection(Schemas.Select(x => x.Migrate()).ToList()); + var schemas = Schemas.Select(x => x.Migrate()).ToImmutableList(); return new ContentChangedTriggerV2 { HandleAll = HandleAll, Schemas = schemas }; } diff --git a/backend/src/Migrations/OldTriggers/ContentChangedTriggerSchema.cs b/backend/src/Migrations/OldTriggers/ContentChangedTriggerSchema.cs index 562dbdb23..db0e52380 100644 --- a/backend/src/Migrations/OldTriggers/ContentChangedTriggerSchema.cs +++ b/backend/src/Migrations/OldTriggers/ContentChangedTriggerSchema.cs @@ -7,14 +7,13 @@ using System; using System.Collections.Generic; -using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Infrastructure; namespace Migrations.OldTriggers { - public sealed class ContentChangedTriggerSchema : Freezable + public sealed class ContentChangedTriggerSchema { public Guid SchemaId { get; set; } 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 ed9829917..1128e2f63 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppClients.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Core.Apps { } - public AppClients(Dictionary inner) + public AppClients(IDictionary inner) : base(inner) { } @@ -30,7 +30,12 @@ namespace Squidex.Domain.Apps.Core.Apps { Guard.NotNullOrEmpty(id, nameof(id)); - return Without(id); + if (!this.TryRemove(id, out var updated)) + { + return this; + } + + return new AppClients(updated); } [Pure] @@ -39,17 +44,17 @@ namespace Squidex.Domain.Apps.Core.Apps Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNullOrEmpty(secret, nameof(secret)); - if (ContainsKey(id)) - { - return this; - } - var newClient = new AppClient(id, secret) { Role = role.Or(Role.Editor) }; - return With(id, newClient); + if (!this.TryAdd(id, newClient, out var updated)) + { + return this; + } + + return new AppClients(updated); } [Pure] @@ -90,7 +95,12 @@ namespace Squidex.Domain.Apps.Core.Apps newClient = newClient with { ApiTrafficLimit = apiTrafficLimit.Value }; } - return With(id, newClient); + if (!this.TrySet(id, newClient, out var updated)) + { + return this; + } + + return new AppClients(updated); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs index 5f4cf8eef..f1e0e74cd 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppContributors.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Core.Apps { } - public AppContributors(Dictionary inner) + public AppContributors(IDictionary inner) : base(inner) { } @@ -31,7 +31,12 @@ namespace Squidex.Domain.Apps.Core.Apps Guard.NotNullOrEmpty(contributorId, nameof(contributorId)); Guard.NotNullOrEmpty(role, nameof(role)); - return With(contributorId, role, EqualityComparer.Default); + if (!this.TrySet(contributorId, role, out var updated)) + { + return this; + } + + return new AppContributors(updated); } [Pure] @@ -39,7 +44,12 @@ namespace Squidex.Domain.Apps.Core.Apps { Guard.NotNullOrEmpty(contributorId, nameof(contributorId)); - return Without(contributorId); + if (!this.TryRemove(contributorId, out var updated)) + { + return this; + } + + return new AppContributors(updated); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppSettings.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppSettings.cs index a125eb317..5e8144472 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppSettings.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/AppSettings.cs @@ -5,19 +5,17 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Apps { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class AppSettings + public sealed record AppSettings { public static readonly AppSettings Empty = new AppSettings(); - public ReadOnlyCollection Patterns { get; init; } = ReadOnlyCollection.Empty(); + public ImmutableList Patterns { get; init; } = ImmutableList.Empty(); - public ReadOnlyCollection Editors { get; init; } = ReadOnlyCollection.Empty(); + public ImmutableList Editors { get; init; } = ImmutableList.Empty(); public bool HideScheduler { get; init; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs deleted file mode 100644 index ed2df1ffa..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppClientsSurrogate.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Core.Apps.Json -{ - public sealed class AppClientsSurrogate : Dictionary, ISurrogate - { - public void FromSource(AppClients source) - { - foreach (var (key, client) in source) - { - Add(key, client); - } - } - - public AppClients ToSource() - { - if (Count == 0) - { - return AppClients.Empty; - } - - return new AppClients(this); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs deleted file mode 100644 index f2e1fe509..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/AppContributorsSurrogate.cs +++ /dev/null @@ -1,33 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Core.Apps.Json -{ - public sealed class AppContributorsSurrogate : Dictionary, ISurrogate - { - public void FromSource(AppContributors source) - { - foreach (var (userId, role) in source) - { - Add(userId, role); - } - } - - public AppContributors ToSource() - { - if (Count == 0) - { - return AppContributors.Empty; - } - - return new AppContributors(this); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs index bb332c41b..0f49d5383 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Json/LanguageConfigSurrogate.cs @@ -7,6 +7,7 @@ using System.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Apps.Json { @@ -31,7 +32,7 @@ namespace Squidex.Domain.Apps.Core.Apps.Json } else { - return new LanguageConfig(IsOptional, Fallback); + return new LanguageConfig(IsOptional, ImmutableList.Create(Fallback)); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs index c82e537fe..e6a465833 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs @@ -5,39 +5,36 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; using System.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Apps { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class LanguageConfig + public sealed record LanguageConfig { public static readonly LanguageConfig Default = new LanguageConfig(); - private readonly Language[] fallbacks; - public bool IsOptional { get; } - public IEnumerable Fallbacks - { - get => fallbacks; - } + public ImmutableList Fallbacks { get; } = ImmutableList.Empty(); - public LanguageConfig(bool isOptional = false, params Language[]? fallbacks) + public LanguageConfig(bool isOptional = false, ImmutableList? fallbacks = null) { IsOptional = isOptional; - this.fallbacks = fallbacks ?? Array.Empty(); + if (fallbacks != null) + { + Fallbacks = fallbacks; + } } internal LanguageConfig Cleanup(string self, IReadOnlyDictionary allowed) { - if (fallbacks.Any(x => x.Iso2Code == self) || fallbacks.Any(x => !allowed.ContainsKey(x))) + if (Fallbacks.Any(x => x.Iso2Code == self) || Fallbacks.Any(x => !allowed.ContainsKey(x))) { - var cleaned = Fallbacks.Where(x => x.Iso2Code != self && allowed.ContainsKey(x.Iso2Code)).ToArray(); + var cleaned = Fallbacks.Where(x => x.Iso2Code != self && allowed.ContainsKey(x.Iso2Code)).ToImmutableList(); return new LanguageConfig(IsOptional, cleaned); } 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 d06b1a0ac..d92bf87d7 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Apps { @@ -66,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Apps var newLanguages = new Dictionary(languages) { - [language] = new LanguageConfig(isOptional, fallbacks) + [language] = new LanguageConfig(isOptional, ImmutableList.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 ea2477674..5ab791868 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Role.cs @@ -15,8 +15,7 @@ using Squidex.Infrastructure.Security; namespace Squidex.Domain.Apps.Core.Apps { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class Role : Named + public sealed record Role : Named { private static readonly HashSet ExtraPermissions = new HashSet { @@ -41,7 +40,6 @@ namespace Squidex.Domain.Apps.Core.Apps public JsonObject Properties { get; } - [IgnoreDuringEquals] public bool IsDefault { get => Roles.IsDefault(this); 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 5bc94d574..0200a0159 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/Roles.cs @@ -92,25 +92,30 @@ namespace Squidex.Domain.Apps.Core.Apps [Pure] public Roles Remove(string name) { - return Create(inner.Without(name)); + if (!inner.TryRemove(name, out var updated)) + { + return this; + } + + return Create(new ImmutableDictionary(updated)); } [Pure] public Roles Add(string name) { - if (inner.ContainsKey(name)) + if (IsDefault(name)) { return this; } - if (IsDefault(name)) + var newRole = Role.Create(name); + + if (!inner.TryAdd(name, newRole, out var updated)) { return this; } - var newRole = Role.Create(name); - - return Create(inner.With(name, newRole)); + return Create(new ImmutableDictionary(updated)); } [Pure] @@ -125,7 +130,12 @@ namespace Squidex.Domain.Apps.Core.Apps var newRole = role.Update(permissions, properties); - return Create(inner.With(name, newRole)); + if (!inner.TrySet(name, newRole, out var updated)) + { + return this; + } + + return Create(new ImmutableDictionary(updated)); } public static bool IsDefault(string role) diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs index e1f088044..3d26f8628 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowStepSurrogate.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Contents.Json @@ -49,7 +50,7 @@ namespace Squidex.Domain.Apps.Core.Contents.Json } var transitions = - Transitions?.ToDictionary( + Transitions?.ToImmutableDictionary( x => x.Key, x => x.Value.ToSource()); diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs deleted file mode 100644 index 45965738a..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowsSurrogate.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Core.Contents.Json -{ - public sealed class WorkflowsSurrogate : Dictionary, ISurrogate - { - public void FromSource(Workflows source) - { - foreach (var (key, workflow) in source) - { - Add(key, workflow); - } - } - - public Workflows ToSource() - { - return new Workflows(this); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/NoUpdate.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/NoUpdate.cs index 178559ec8..a8ea60a83 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/NoUpdate.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/NoUpdate.cs @@ -5,35 +5,27 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Contents { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class NoUpdate : WorkflowCondition + public sealed record NoUpdate : WorkflowCondition { - public static readonly NoUpdate Always = new NoUpdate(null, null); - - public NoUpdate(string? expression, ReadOnlyCollection? roles) - : base(expression, roles) - { - } + public static readonly NoUpdate Always = new NoUpdate(); public static NoUpdate When(string? expression, params string[]? roles) { if (roles?.Length > 0) { - return new NoUpdate(expression, ReadOnlyCollection.Create(roles)); + return new NoUpdate { Expression = expression, Roles = roles?.ToImmutableList() }; } - else if (!string.IsNullOrWhiteSpace(expression)) - { - return new NoUpdate(expression, null); - } - else + + if (!string.IsNullOrWhiteSpace(expression)) { - return Always; + return new NoUpdate { Expression = expression }; } + + return Always; } } -} \ No newline at end of file +} 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 aad9aacff..d0d6ec00d 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs @@ -6,38 +6,32 @@ // ========================================================================== using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; -#pragma warning disable IDE0051 // Remove unused private members - namespace Squidex.Domain.Apps.Core.Contents { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class Workflow : Named + public sealed record Workflow { private const string DefaultName = "Unnamed"; - public static readonly IReadOnlyDictionary EmptySteps = new Dictionary(); - public static readonly Workflow Default = CreateDefault(); public static readonly Workflow Empty = new Workflow(default, null); - [IgnoreDuringEquals] - public IReadOnlyDictionary Steps { get; } = EmptySteps; + public Status Initial { get; } + + public ImmutableDictionary Steps { get; } = ImmutableDictionary.Empty(); - public ReadOnlyCollection SchemaIds { get; } = ReadOnlyCollection.Empty(); + public ImmutableList SchemaIds { get; } = ImmutableList.Empty(); - public Status Initial { get; } + public string Name { get; } public Workflow( Status initial, - IReadOnlyDictionary? steps = null, - IReadOnlyList? schemaIds = null, + ImmutableDictionary? steps = null, + ImmutableList? schemaIds = null, string? name = null) - : base(name.Or(DefaultName)) { Initial = initial; @@ -48,21 +42,24 @@ namespace Squidex.Domain.Apps.Core.Contents if (schemaIds != null) { - SchemaIds = schemaIds.ToReadOnlyCollection(); + SchemaIds = schemaIds; } + + Name = name.Or(DefaultName); } public static Workflow CreateDefault(string? name = null) { return new Workflow( - Status.Draft, new Dictionary + Status.Draft, + new Dictionary { [Status.Archived] = new WorkflowStep( new Dictionary { [Status.Draft] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Archived, NoUpdate.Always), [Status.Draft] = new WorkflowStep( @@ -70,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Contents { [Status.Archived] = WorkflowTransition.Always, [Status.Published] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Draft), [Status.Published] = new WorkflowStep( @@ -78,9 +75,9 @@ namespace Squidex.Domain.Apps.Core.Contents { [Status.Archived] = WorkflowTransition.Always, [Status.Draft] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Published) - }, null, name); + }.ToImmutableDictionary(), null, name); } public IEnumerable<(Status Status, WorkflowStep Step, WorkflowTransition Transition)> GetTransitions(Status status) @@ -128,17 +125,5 @@ namespace Squidex.Domain.Apps.Core.Contents { return (Initial, Steps[Initial]); } - - [CustomEqualsInternal] - private bool CustomEquals(Workflow other) - { - return Steps.EqualsDictionary(other.Steps); - } - - [CustomGetHashCode] - private int CustomHashCode() - { - return Steps.DictionaryHashCode(); - } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowCondition.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowCondition.cs index c65d6c2f5..ff66a042e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowCondition.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowCondition.cs @@ -5,21 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Contents { - public abstract class WorkflowCondition + public abstract record WorkflowCondition { - public string? Expression { get; } + public string? Expression { get; init; } - public ReadOnlyCollection? Roles { get; } - - protected WorkflowCondition(string? expression, ReadOnlyCollection? roles) - { - Expression = expression; - - Roles = roles; - } + public ImmutableList? Roles { get; init; } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs index eec4f71f9..4bbecf56b 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs @@ -5,44 +5,28 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; -using Squidex.Infrastructure; - -#pragma warning disable IDE0051 // Remove unused private members +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Contents { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class WorkflowStep + public sealed record WorkflowStep { - private static readonly IReadOnlyDictionary EmptyTransitions = new Dictionary(); - - [IgnoreDuringEquals] - public IReadOnlyDictionary Transitions { get; } + public ImmutableDictionary Transitions { get; } = ImmutableDictionary.Empty(); public string? Color { get; } public NoUpdate? NoUpdate { get; } - public WorkflowStep(IReadOnlyDictionary? transitions = null, string? color = null, NoUpdate? noUpdate = null) + public WorkflowStep(ImmutableDictionary? transitions = null, string? color = null, NoUpdate? noUpdate = null) { - Transitions = transitions ?? EmptyTransitions; - Color = color; - NoUpdate = noUpdate; - } - - [CustomEqualsInternal] - private bool CustomEquals(WorkflowStep other) - { - return Transitions.EqualsDictionary(other.Transitions); - } + if (transitions != null) + { + Transitions = transitions; + } - [CustomGetHashCode] - private int CustomHashCode() - { - return Transitions.DictionaryHashCode(); + NoUpdate = noUpdate; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs index 3517d84d9..971423aa8 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs @@ -5,35 +5,27 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Contents { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class WorkflowTransition : WorkflowCondition + public sealed record WorkflowTransition : WorkflowCondition { - public static readonly WorkflowTransition Always = new WorkflowTransition(null, null); - - public WorkflowTransition(string? expression, ReadOnlyCollection? roles) - : base(expression, roles) - { - } + public static readonly WorkflowTransition Always = new WorkflowTransition(); public static WorkflowTransition When(string? expression, params string[]? roles) { if (roles?.Length > 0) { - return new WorkflowTransition(expression, ReadOnlyCollection.Create(roles)); + return new WorkflowTransition { Expression = expression, Roles = roles?.ToImmutableList() }; } - else if (!string.IsNullOrWhiteSpace(expression)) - { - return new WorkflowTransition(expression, null); - } - else + + if (!string.IsNullOrWhiteSpace(expression)) { - return Always; + return new WorkflowTransition { Expression = expression }; } + + return Always; } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs index f586b76d3..a256c0212 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs @@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Contents { } - public Workflows(Dictionary inner) + public Workflows(IDictionary inner) : base(inner) { } @@ -29,7 +29,12 @@ namespace Squidex.Domain.Apps.Core.Contents [Pure] public Workflows Remove(DomainId id) { - return Without(id); + if (!this.TryRemove(id, out var updated)) + { + return this; + } + + return new Workflows(updated); } [Pure] @@ -37,7 +42,12 @@ namespace Squidex.Domain.Apps.Core.Contents { Guard.NotNullOrEmpty(name, nameof(name)); - return With(workflowId, Workflow.CreateDefault(name)); + if (!this.TryAdd(workflowId, Workflow.CreateDefault(name), out var updated)) + { + return this; + } + + return new Workflows(updated); } [Pure] @@ -45,7 +55,12 @@ namespace Squidex.Domain.Apps.Core.Contents { Guard.NotNull(workflow, nameof(workflow)); - return With(default, workflow); + if (!this.TrySet(default, workflow, out var updated)) + { + return this; + } + + return new Workflows(updated); } [Pure] @@ -53,7 +68,12 @@ namespace Squidex.Domain.Apps.Core.Contents { Guard.NotNull(workflow, nameof(workflow)); - return With(id, workflow); + if (!this.TrySet(id, workflow, out var updated)) + { + return this; + } + + return new Workflows(updated); } [Pure] @@ -66,12 +86,12 @@ namespace Squidex.Domain.Apps.Core.Contents return Set(workflow); } - if (!ContainsKey(id)) + if (!this.TryUpdate(id, workflow, out var updated)) { return this; } - return With(id, workflow); + return new Workflows(updated); } public Workflow GetFirst() diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml b/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml deleted file mode 100644 index 736892395..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd b/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd deleted file mode 100644 index 6ccd762d1..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Freezable.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Freezable.cs deleted file mode 100644 index 5aa3fd350..000000000 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Freezable.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Reflection; - -namespace Squidex.Domain.Apps.Core -{ - [Equals(DoNotAddEquals = true, DoNotAddGetHashCode = true, DoNotAddEqualityOperators = true)] - public abstract class Freezable : IFreezable - { - private bool isFrozen; - - [IgnoreEquals] - [IgnoreDuringEquals] - public bool IsFrozen - { - get => isFrozen; - } - - protected void CheckIfFrozen() - { - if (isFrozen) - { - throw new InvalidOperationException("Object is frozen"); - } - } - - public virtual void Freeze() - { - isFrozen = true; - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Named.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Named.cs index ccaef1bc5..40b747388 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Named.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Named.cs @@ -9,7 +9,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core { - public abstract class Named + public abstract record Named { public string Name { get; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs index b08546a79..6a0c4659e 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Rule.cs @@ -11,81 +11,65 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Rules { - public sealed class Rule : Cloneable + public sealed class Rule { - private RuleTrigger trigger; - private RuleAction action; - private string name; - private bool isEnabled = true; + public string? Name { get; private set; } - public string Name - { - get => name; - } + public RuleTrigger Trigger { get; private set; } - public RuleTrigger Trigger - { - get => trigger; - } + public RuleAction Action { get; private set; } - public RuleAction Action - { - get => action; - } - - public bool IsEnabled - { - get => isEnabled; - } + public bool IsEnabled { get; private set; } = true; public Rule(RuleTrigger trigger, RuleAction action) { Guard.NotNull(trigger, nameof(trigger)); Guard.NotNull(action, nameof(action)); - SetTrigger(trigger); - SetAction(action); + Action = action; + + Trigger = trigger; } [Pure] public Rule Rename(string newName) { - if (string.Equals(name, newName)) + if (string.Equals(Name, newName)) { return this; } return Clone(clone => { - clone.name = newName; + clone.Name = newName; }); } [Pure] public Rule Enable() { - if (isEnabled) + if (IsEnabled) { return this; } return Clone(clone => { - clone.isEnabled = true; + clone.IsEnabled = true; }); } [Pure] public Rule Disable() { - if (!isEnabled) + if (!IsEnabled) { return this; } return Clone(clone => { - clone.isEnabled = false; + clone.IsEnabled = false; }); } @@ -94,19 +78,19 @@ namespace Squidex.Domain.Apps.Core.Rules { Guard.NotNull(newTrigger, nameof(newTrigger)); - if (newTrigger.GetType() != trigger.GetType()) + if (newTrigger.GetType() != Trigger.GetType()) { throw new ArgumentException("New trigger has another type.", nameof(newTrigger)); } - if (trigger.DeepEquals(newTrigger)) + if (Trigger.Equals(newTrigger)) { return this; } return Clone(clone => { - clone.SetTrigger(newTrigger); + clone.Trigger = newTrigger; }); } @@ -115,32 +99,29 @@ namespace Squidex.Domain.Apps.Core.Rules { Guard.NotNull(newAction, nameof(newAction)); - if (newAction.GetType() != action.GetType()) + if (newAction.GetType() != Action.GetType()) { throw new ArgumentException("New action has another type.", nameof(newAction)); } - if (action.DeepEquals(newAction)) + if (Action.Equals(newAction)) { return this; } return Clone(clone => { - clone.SetAction(newAction); + clone.Action = newAction; }); } - private void SetAction(RuleAction newAction) + private Rule Clone(Action updater) { - action = newAction; - action.Freeze(); - } + var clone = (Rule)MemberwiseClone(); - private void SetTrigger(RuleTrigger newTrigger) - { - trigger = newTrigger; - trigger.Freeze(); + updater(clone); + + return clone; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs index 28bdc7d62..a1501bcea 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleAction.cs @@ -13,7 +13,7 @@ using Squidex.Infrastructure.Validation; namespace Squidex.Domain.Apps.Core.Rules { - public abstract class RuleAction : Freezable + public abstract record RuleAction { public IEnumerable Validate() { @@ -41,10 +41,5 @@ namespace Squidex.Domain.Apps.Core.Rules { yield break; } - - public bool DeepEquals(RuleAction action) - { - return SimpleEquals.IsEquals(this, action); - } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs index 798ede56a..019e304f6 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/RuleTrigger.cs @@ -9,13 +9,8 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules { - public abstract class RuleTrigger : Freezable + public abstract record RuleTrigger { public abstract T Accept(IRuleTriggerVisitor visitor); - - public bool DeepEquals(RuleTrigger action) - { - return SimpleEquals.IsEquals(this, action); - } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs index 77084a828..d88ccb64b 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/AssetChangedTriggerV2.cs @@ -10,9 +10,9 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(AssetChangedTriggerV2))] - public sealed class AssetChangedTriggerV2 : RuleTrigger + public sealed record AssetChangedTriggerV2 : RuleTrigger { - public string Condition { get; set; } + public string Condition { get; init; } public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/CommentTrigger.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/CommentTrigger.cs index 84273235a..7f4908ed5 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/CommentTrigger.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/CommentTrigger.cs @@ -10,9 +10,9 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(CommentTrigger))] - public sealed class CommentTrigger : RuleTrigger + public sealed record CommentTrigger : RuleTrigger { - public string Condition { get; set; } + public string Condition { get; init; } public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs index cae53463a..bd904e3ba 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerSchemaV2.cs @@ -9,10 +9,10 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Rules.Triggers { - public sealed class ContentChangedTriggerSchemaV2 : Freezable + public sealed record ContentChangedTriggerSchemaV2 { - public DomainId SchemaId { get; set; } + public DomainId SchemaId { get; init; } - public string? Condition { get; set; } + public string? Condition { get; init; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs index c9cd35ead..1f1c9d0ed 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ContentChangedTriggerV2.cs @@ -5,34 +5,21 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(ContentChangedTriggerV2))] - public sealed class ContentChangedTriggerV2 : RuleTrigger + public sealed record ContentChangedTriggerV2 : RuleTrigger { - public ReadOnlyCollection? Schemas { get; set; } + public ImmutableList? Schemas { get; init; } - public bool HandleAll { get; set; } + public bool HandleAll { get; init; } public override T Accept(IRuleTriggerVisitor visitor) { return visitor.Visit(this); } - - public override void Freeze() - { - base.Freeze(); - - if (Schemas != null) - { - foreach (var schema in Schemas) - { - schema.Freeze(); - } - } - } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ManualTrigger.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ManualTrigger.cs index 4f7824ca8..fbb3778b2 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ManualTrigger.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/ManualTrigger.cs @@ -10,7 +10,7 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(ManualTrigger))] - public sealed class ManualTrigger : RuleTrigger + public sealed record ManualTrigger : RuleTrigger { public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/SchemaChangedTrigger.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/SchemaChangedTrigger.cs index ad37378e3..99126af67 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/SchemaChangedTrigger.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/SchemaChangedTrigger.cs @@ -10,9 +10,9 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(SchemaChangedTrigger))] - public sealed class SchemaChangedTrigger : RuleTrigger + public sealed record SchemaChangedTrigger : RuleTrigger { - public string Condition { get; set; } + public string Condition { get; init; } public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs index 509dcc5cf..6b30ca3e7 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Rules/Triggers/UsageTrigger.cs @@ -10,11 +10,11 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Rules.Triggers { [TypeName(nameof(UsageTrigger))] - public sealed class UsageTrigger : RuleTrigger + public sealed record UsageTrigger : RuleTrigger { - public int Limit { get; set; } + public int Limit { get; init; } - public int? NumDays { get; set; } + public int? NumDays { get; init; } public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs index 140f3e269..8b9cea229 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs @@ -14,27 +14,22 @@ namespace Squidex.Domain.Apps.Core.Schemas { public sealed class ArrayField : RootField, IArrayField { - private FieldCollection fields = FieldCollection.Empty; - public IReadOnlyList Fields { - get => fields.Ordered; + get => FieldCollection.Ordered; } public IReadOnlyDictionary FieldsById { - get => fields.ById; + get => FieldCollection.ById; } public IReadOnlyDictionary FieldsByName { - get => fields.ByName; + get => FieldCollection.ByName; } - public FieldCollection FieldCollection - { - get => fields; - } + public FieldCollection FieldCollection { get; private set; } = FieldCollection.Empty; public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties? properties = null, IFieldSettings? settings = null) : base(id, name, partitioning, properties, settings) @@ -44,9 +39,7 @@ namespace Squidex.Domain.Apps.Core.Schemas public ArrayField(long id, string name, Partitioning partitioning, NestedField[] fields, ArrayFieldProperties? properties = null, IFieldSettings? settings = null) : this(id, name, partitioning, properties, settings) { - Guard.NotNull(fields, nameof(fields)); - - this.fields = new FieldCollection(fields); + FieldCollection = new FieldCollection(fields); } [Pure] @@ -75,16 +68,16 @@ namespace Squidex.Domain.Apps.Core.Schemas private ArrayField Updatefields(Func, FieldCollection> updater) { - var newFields = updater(fields); + var newFields = updater(FieldCollection); - if (ReferenceEquals(newFields, fields)) + if (ReferenceEquals(newFields, FieldCollection)) { return this; } - return Clone(clone => + return (ArrayField)Clone(clone => { - clone.fields = newFields; + ((ArrayField)clone).FieldCollection = newFields; }); } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs index 48867f0ee..7256cb2d7 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayFieldProperties.cs @@ -9,12 +9,11 @@ using System; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class ArrayFieldProperties : FieldProperties + public sealed record ArrayFieldProperties : FieldProperties { - public int? MinItems { get; set; } + public int? MinItems { get; init; } - public int? MaxItems { get; set; } + public int? MaxItems { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs index d4e4e1246..6e62c6b91 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/AssetsFieldProperties.cs @@ -5,54 +5,52 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class AssetsFieldProperties : FieldProperties + public sealed record AssetsFieldProperties : FieldProperties { - public AssetPreviewMode PreviewMode { get; set; } + public AssetPreviewMode PreviewMode { get; init; } - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; init; } - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; init; } - public string? FolderId { get; set; } + public string? FolderId { get; init; } - public int? MinItems { get; set; } + public int? MinItems { get; init; } - public int? MaxItems { get; set; } + public int? MaxItems { get; init; } - public int? MinWidth { get; set; } + public int? MinWidth { get; init; } - public int? MaxWidth { get; set; } + public int? MaxWidth { get; init; } - public int? MinHeight { get; set; } + public int? MinHeight { get; init; } - public int? MaxHeight { get; set; } + public int? MaxHeight { get; init; } - public int? MinSize { get; set; } + public int? MinSize { get; init; } - public int? MaxSize { get; set; } + public int? MaxSize { get; init; } - public int? AspectWidth { get; set; } + public int? AspectWidth { get; init; } - public int? AspectHeight { get; set; } + public int? AspectHeight { get; init; } - public bool MustBeImage { get; set; } + public bool MustBeImage { get; init; } - public bool AllowDuplicates { get; set; } + public bool AllowDuplicates { get; init; } - public bool ResolveFirst { get; set; } + public bool ResolveFirst { get; init; } public bool ResolveImage { - get => ResolveFirst; - set => ResolveFirst = value; + init => ResolveFirst = value; } - public ReadOnlyCollection? AllowedExtensions { get; set; } + public ImmutableList? AllowedExtensions { get; set; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs index 9e9c3ad42..b8058c824 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/BooleanFieldProperties.cs @@ -7,16 +7,15 @@ namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class BooleanFieldProperties : FieldProperties + public sealed record BooleanFieldProperties : FieldProperties { - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue DefaultValues { get; init; } - public bool? DefaultValue { get; set; } + public bool? DefaultValue { get; init; } - public bool InlineEditable { get; set; } + public bool InlineEditable { get; init; } - public BooleanFieldEditor Editor { get; set; } + public BooleanFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs index 73ee1f875..e9dccf8ca 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/DateTimeFieldProperties.cs @@ -9,20 +9,19 @@ using NodaTime; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class DateTimeFieldProperties : FieldProperties + public sealed record DateTimeFieldProperties : FieldProperties { - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue DefaultValues { get; init; } - public Instant? DefaultValue { get; set; } + public Instant? DefaultValue { get; init; } - public Instant? MaxValue { get; set; } + public Instant? MaxValue { get; init; } - public Instant? MinValue { get; set; } + public Instant? MinValue { get; init; } - public DateTimeCalculatedDefaultValue? CalculatedDefaultValue { get; set; } + public DateTimeCalculatedDefaultValue? CalculatedDefaultValue { get; init; } - public DateTimeFieldEditor Editor { get; set; } + public DateTimeFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Infrastructure/Cloneable.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs similarity index 52% rename from backend/src/Squidex.Infrastructure/Cloneable.cs rename to backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs index 87f07a28c..13b8da28b 100644 --- a/backend/src/Squidex.Infrastructure/Cloneable.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldBase.cs @@ -5,25 +5,24 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; +using Squidex.Infrastructure; -namespace Squidex.Infrastructure +namespace Squidex.Domain.Apps.Core.Schemas { - public abstract class Cloneable + public abstract class FieldBase { - protected T Clone(Action updater) where T : Cloneable - { - var clone = (T)MemberwiseClone(); + public long Id { get; } - updater(clone); + public string Name { get; } - clone.OnCloned(); + protected FieldBase(long id, string name) + { + Guard.NotNullOrEmpty(name, nameof(name)); + Guard.GreaterThan(id, 0, nameof(id)); - return clone; - } + Id = id; - protected virtual void OnCloned() - { + Name = name; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs index b47d5cf8c..58acdcfc4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldCollection.cs @@ -13,14 +13,14 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class FieldCollection : Cloneable> where T : IField + public sealed class FieldCollection where T : IField { public static readonly FieldCollection Empty = new FieldCollection(); private static readonly Dictionary EmptyById = new Dictionary(); private static readonly Dictionary EmptyByString = new Dictionary(); - private T[] fieldsOrdered; + private readonly T[] fieldsOrdered; private Dictionary? fieldsById; private Dictionary? fieldsByName; @@ -81,10 +81,9 @@ namespace Squidex.Domain.Apps.Core.Schemas fieldsOrdered = fields; } - protected override void OnCloned() + private FieldCollection(IEnumerable fields) { - fieldsById = null; - fieldsByName = null; + fieldsOrdered = fields.ToArray(); } [Pure] @@ -95,10 +94,7 @@ namespace Squidex.Domain.Apps.Core.Schemas return this; } - return Clone(clone => - { - clone.fieldsOrdered = fieldsOrdered.Where(x => x.Id != fieldId).ToArray(); - }); + return new FieldCollection(fieldsOrdered.Where(x => x.Id != fieldId)); } [Pure] @@ -116,10 +112,7 @@ namespace Squidex.Domain.Apps.Core.Schemas return this; } - return Clone(clone => - { - clone.fieldsOrdered = fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id)).ToArray(); - }); + return new FieldCollection(fieldsOrdered.OrderBy(f => ids.IndexOf(f.Id))); } [Pure] @@ -137,10 +130,7 @@ namespace Squidex.Domain.Apps.Core.Schemas throw new ArgumentException($"A field with id {field.Id} already exists.", nameof(field)); } - return Clone(clone => - { - clone.fieldsOrdered = clone.fieldsOrdered.Union(Enumerable.Repeat(field, 1)).ToArray(); - }); + return new FieldCollection(fieldsOrdered.Union(Enumerable.Repeat(field, 1))); } [Pure] @@ -165,10 +155,7 @@ namespace Squidex.Domain.Apps.Core.Schemas throw new InvalidOperationException($"Field must be of type {typeof(T)}"); } - return Clone(clone => - { - clone.fieldsOrdered = clone.fieldsOrdered.Select(x => ReferenceEquals(x, field) ? newField : x).ToArray(); - }); + return new FieldCollection(fieldsOrdered.Select(x => ReferenceEquals(x, field) ? newField : x)); } } -} \ No newline at end of file +} 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 4bab0e67f..3bcc3d1bf 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldNames.cs @@ -6,25 +6,27 @@ // ========================================================================== using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class FieldNames : ReadOnlyCollection + public sealed class FieldNames : ImmutableList { - private static readonly List EmptyNames = new List(); + public static readonly FieldNames Empty = new FieldNames(new List()); - public static readonly FieldNames Empty = new FieldNames(EmptyNames); - - public FieldNames(params string[] fields) - : base(fields?.ToList() ?? EmptyNames) + public FieldNames() { } public FieldNames(IList list) - : base(list ?? EmptyNames) + : base(list) + { + } + + public static FieldNames Create(params string[] names) { + return new FieldNames(names.ToList()); } public FieldNames Add(string field) diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs index 52df05b0e..74484e766 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldProperties.cs @@ -5,24 +5,23 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public abstract class FieldProperties : NamedElementPropertiesBase + public abstract record FieldProperties : NamedElementPropertiesBase { - public bool IsRequired { get; set; } + public bool IsRequired { get; init; } - public bool IsRequiredOnPublish { get; set; } + public bool IsRequiredOnPublish { get; init; } - public bool IsHalfWidth { get; set; } + public bool IsHalfWidth { get; init; } - public string? Placeholder { get; set; } + public string? Placeholder { get; init; } - public string? EditorUrl { get; set; } + public string? EditorUrl { get; init; } - public ReadOnlyCollection? Tags { get; set; } + public ImmutableList? Tags { get; init; } public abstract T Accept(IFieldPropertiesVisitor visitor, TArgs args); @@ -32,4 +31,4 @@ namespace Squidex.Domain.Apps.Core.Schemas public abstract NestedField CreateNestedField(long id, string name, IFieldSettings? settings = null); } -} \ No newline at end of file +} 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 52e08eb43..f71cd9c12 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRule.cs @@ -9,16 +9,15 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class FieldRule + public sealed record FieldRule { public FieldRuleAction Action { get; } public string Field { get; } - public string? Condition { get; } + public string? Condition { get; init; } - public FieldRule(FieldRuleAction action, string field, string? condition) + public FieldRule(FieldRuleAction action, string field) { Guard.Enum(action, nameof(action)); Guard.NotNullOrEmpty(field, nameof(field)); @@ -26,18 +25,22 @@ namespace Squidex.Domain.Apps.Core.Schemas Action = action; Field = field; - - Condition = condition; } public static FieldRule Disable(string field, string? condition = null) { - return new FieldRule(FieldRuleAction.Disable, field, condition); + return new FieldRule(FieldRuleAction.Disable, field) + { + Condition = condition + }; } public static FieldRule Hide(string field, string? condition = null) { - return new FieldRule(FieldRuleAction.Hide, field, condition); + return new FieldRule(FieldRuleAction.Hide, field) + { + Condition = condition + }; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRules.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRules.cs index b7bae685d..539b9e757 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRules.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRules.cs @@ -6,25 +6,27 @@ // ========================================================================== using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class FieldRules : ReadOnlyCollection + public sealed class FieldRules : ImmutableList { - private static readonly List EmptyRules = new List(); + public static readonly FieldRules Empty = new FieldRules(new List()); - public static readonly FieldRules Empty = new FieldRules(EmptyRules); - - public FieldRules(params FieldRule[] fields) - : base(fields?.ToList() ?? EmptyRules) + public FieldRules() { } public FieldRules(IList list) - : base(list ?? EmptyRules) + : base(list) + { + } + + public static FieldRules Create(params FieldRule[] rules) { + return new FieldRules(rules.ToArray()); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs index 1fc0acb02..f0cf4dd3a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/GeolocationFieldProperties.cs @@ -7,10 +7,9 @@ namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class GeolocationFieldProperties : FieldProperties + public sealed record GeolocationFieldProperties : FieldProperties { - public GeolocationFieldEditor Editor { get; set; } + public GeolocationFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs index 7a9d0380c..95c24cf1a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/JsonFieldProperties.cs @@ -7,8 +7,7 @@ namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class JsonFieldProperties : FieldProperties + public sealed record JsonFieldProperties : FieldProperties { public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/LocalizedValue.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/LocalizedValue.cs index b6a8eda44..f6f5cbbcd 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/LocalizedValue.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/LocalizedValue.cs @@ -5,28 +5,20 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Reflection.Equality; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class LocalizedValue : Dictionary, IEquatable> + public sealed class LocalizedValue : ImmutableDictionary { - public override bool Equals(object? obj) + public LocalizedValue() { - return Equals(obj as LocalizedValue); } - public bool Equals(Dictionary? other) + public LocalizedValue(IDictionary inner) + : base(inner) { - return this.EqualsDictionary(other, EqualityComparer.Default, DeepEqualityComparer.Default); - } - - public override int GetHashCode() - { - return this.DictionaryHashCode(EqualityComparer.Default, DeepEqualityComparer.Default); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs index b3583e8f4..073b6f545 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NamedElementPropertiesBase.cs @@ -7,10 +7,10 @@ namespace Squidex.Domain.Apps.Core.Schemas { - public abstract class NamedElementPropertiesBase : Freezable + public abstract record NamedElementPropertiesBase { - public string? Label { get; set; } + public string? Label { get; init; } - public string? Hints { get; set; } + public string? Hints { get; init; } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs index 3f0a7b49f..ca199f81f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField.cs @@ -5,134 +5,113 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Diagnostics.Contracts; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - public abstract class NestedField : Cloneable, INestedField + public abstract class NestedField : FieldBase, INestedField { - private readonly long fieldId; - private readonly string fieldName; - private bool isDisabled; - private bool isHidden; - private bool isLocked; + public bool IsLocked { get; private set; } - public long Id - { - get => fieldId; - } - - public string Name - { - get => fieldName; - } - - public bool IsLocked - { - get => isLocked; - } - - public bool IsHidden - { - get => isHidden; - } + public bool IsHidden { get; private set; } - public bool IsDisabled - { - get => isDisabled; - } + public bool IsDisabled { get; private set; } public abstract FieldProperties RawProperties { get; } protected NestedField(long id, string name, IFieldSettings? settings = null) + : base(id, name) { - Guard.NotNullOrEmpty(name, nameof(name)); - Guard.GreaterThan(id, 0, nameof(id)); - - fieldId = id; - fieldName = name; - if (settings != null) { - isLocked = settings.IsLocked; - isHidden = settings.IsHidden; - isDisabled = settings.IsDisabled; + IsLocked = settings.IsLocked; + IsHidden = settings.IsHidden; + IsDisabled = settings.IsDisabled; } } [Pure] public NestedField Lock() { - if (isLocked) + if (IsLocked) { return this; } return Clone(clone => { - clone.isLocked = true; + clone.IsLocked = true; }); } [Pure] public NestedField Hide() { - if (isHidden) + if (IsHidden) { return this; } return Clone(clone => { - clone.isHidden = true; + clone.IsHidden = true; }); } [Pure] public NestedField Show() { - if (!isHidden) + if (!IsHidden) { return this; } return Clone(clone => { - clone.isHidden = false; + clone.IsHidden = false; }); } [Pure] public NestedField Disable() { - if (isDisabled) + if (IsDisabled) { return this; } return Clone(clone => { - clone.isDisabled = true; + clone.IsDisabled = true; }); } [Pure] public NestedField Enable() { - if (!isDisabled) + if (!IsDisabled) { return this; } return Clone(clone => { - clone.isDisabled = false; + clone.IsDisabled = false; }); } public abstract T Accept(IFieldVisitor visitor, TArgs args); public abstract NestedField Update(FieldProperties newProperties); + + protected NestedField Clone(Action updater) + { + var clone = (NestedField)MemberwiseClone(); + + updater(clone); + + return clone; + } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs index 0d266148d..30acf88a3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NestedField{T}.cs @@ -13,22 +13,17 @@ namespace Squidex.Domain.Apps.Core.Schemas { public class NestedField : NestedField, IField where T : FieldProperties, new() { - private T properties; - - public T Properties - { - get => properties; - } + public T Properties { get; private set; } public override FieldProperties RawProperties { - get => properties; + get => Properties; } public NestedField(long id, string name, T? properties = null, IFieldSettings? settings = null) : base(id, name, settings) { - SetProperties(properties ?? new T()); + Properties = properties ?? new T(); } [Pure] @@ -36,25 +31,17 @@ namespace Squidex.Domain.Apps.Core.Schemas { var typedProperties = ValidateProperties(newProperties); - typedProperties.Freeze(); - - if (properties.Equals(typedProperties)) + if (Properties.Equals(typedProperties)) { return this; } - return Clone>(clone => + return Clone(clone => { - clone.SetProperties(typedProperties); + ((NestedField)clone).Properties = typedProperties; }); } - private void SetProperties(T newProperties) - { - properties = newProperties; - properties.Freeze(); - } - private static T ValidateProperties(FieldProperties newProperties) { Guard.NotNull(newProperties, nameof(newProperties)); @@ -69,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Schemas public override TResult Accept(IFieldVisitor visitor, TArgs args) { - return properties.Accept(visitor, this, args); + return Properties.Accept(visitor, this, args); } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs index 3fe0c70d3..6d4d7b3ef 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/NumberFieldProperties.cs @@ -5,28 +5,27 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class NumberFieldProperties : FieldProperties + public sealed record NumberFieldProperties : FieldProperties { - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; init; } - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue DefaultValues { get; init; } - public double? DefaultValue { get; set; } + public double? DefaultValue { get; init; } - public double? MaxValue { get; set; } + public double? MaxValue { get; init; } - public double? MinValue { get; set; } + public double? MinValue { get; init; } - public bool IsUnique { get; set; } + public bool IsUnique { get; init; } - public bool InlineEditable { get; set; } + public bool InlineEditable { get; init; } - public NumberFieldEditor Editor { get; set; } + public NumberFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs index 314a680bd..50c029f89 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/ReferencesFieldProperties.cs @@ -5,52 +5,50 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class ReferencesFieldProperties : FieldProperties + public sealed record ReferencesFieldProperties : FieldProperties { - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; init; } - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; init; } - public int? MinItems { get; set; } + public int? MinItems { get; init; } - public int? MaxItems { get; set; } + public int? MaxItems { get; init; } - public bool ResolveReference { get; set; } + public bool ResolveReference { get; init; } - public bool AllowDuplicates { get; set; } + public bool AllowDuplicates { get; init; } - public bool MustBePublished { get; set; } + public bool MustBePublished { get; init; } - public ReferencesFieldEditor Editor { get; set; } + public ReferencesFieldEditor Editor { get; init; } public DomainId SchemaId { - get - { - return SchemaIds?.FirstOrDefault() ?? default; - } - set + init { if (value != default) { - SchemaIds = new ReadOnlyCollection(new List { value }); + SchemaIds = ImmutableList.Create(value); } else { SchemaIds = null; } } + get + { + return SchemaIds?.FirstOrDefault() ?? default; + } } - public ReadOnlyCollection? SchemaIds { get; set; } + public ImmutableList? SchemaIds { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs index e6321597a..0783f261f 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField.cs @@ -1,147 +1,124 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Diagnostics.Contracts; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - public abstract class RootField : Cloneable, IRootField + public abstract class RootField : FieldBase, IRootField { - private readonly long fieldId; - private readonly string fieldName; - private readonly Partitioning partitioning; - private bool isDisabled; - private bool isHidden; - private bool isLocked; - - public long Id - { - get => fieldId; - } + public Partitioning Partitioning { get; } - public string Name - { - get => fieldName; - } + public bool IsLocked { get; private set; } - public bool IsLocked - { - get => isLocked; - } + public bool IsHidden { get; private set; } - public bool IsHidden - { - get => isHidden; - } - - public bool IsDisabled - { - get => isDisabled; - } - - public Partitioning Partitioning - { - get => partitioning; - } + public bool IsDisabled { get; private set; } public abstract FieldProperties RawProperties { get; } protected RootField(long id, string name, Partitioning partitioning, IFieldSettings? settings = null) + : base(id, name) { - Guard.NotNullOrEmpty(name, nameof(name)); - Guard.GreaterThan(id, 0, nameof(id)); Guard.NotNull(partitioning, nameof(partitioning)); - fieldId = id; - fieldName = name; - - this.partitioning = partitioning; + Partitioning = partitioning; if (settings != null) { - isLocked = settings.IsLocked; - isHidden = settings.IsHidden; - isDisabled = settings.IsDisabled; + IsLocked = settings.IsLocked; + IsHidden = settings.IsHidden; + IsDisabled = settings.IsDisabled; } } [Pure] public RootField Lock() { - if (isLocked) + if (IsLocked) { return this; } return Clone(clone => { - clone.isLocked = true; + clone.IsLocked = true; }); } [Pure] public RootField Hide() { - if (isHidden) + if (IsHidden) { return this; } return Clone(clone => { - clone.isHidden = true; + clone.IsHidden = true; }); } [Pure] public RootField Show() { - if (!isHidden) + if (!IsHidden) { return this; } return Clone(clone => { - clone.isHidden = false; + clone.IsHidden = false; }); } [Pure] public RootField Disable() { - if (isDisabled) + if (IsDisabled) { return this; } return Clone(clone => { - clone.isDisabled = true; + clone.IsDisabled = true; }); } [Pure] public RootField Enable() { - if (!isDisabled) + if (!IsDisabled) { return this; } return Clone(clone => { - clone.isDisabled = false; + clone.IsDisabled = false; }); } public abstract T Accept(IFieldVisitor visitor, TArgs args); public abstract RootField Update(FieldProperties newProperties); + + protected RootField Clone(Action updater) + { + var clone = (RootField)MemberwiseClone(); + + updater(clone); + + return clone; + } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs index 7bd3d5c0c..5f80154bf 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/RootField{T}.cs @@ -13,22 +13,17 @@ namespace Squidex.Domain.Apps.Core.Schemas { public class RootField : RootField, IField where T : FieldProperties, new() { - private T properties; - - public T Properties - { - get => properties; - } + public T Properties { get; private set; } public override FieldProperties RawProperties { - get => properties; + get => Properties; } public RootField(long id, string name, Partitioning partitioning, T? properties = null, IFieldSettings? settings = null) : base(id, name, partitioning, settings) { - SetProperties(properties ?? new T()); + Properties = properties ?? new T(); } [Pure] @@ -36,23 +31,17 @@ namespace Squidex.Domain.Apps.Core.Schemas { var typedProperties = ValidateProperties(newProperties); - if (properties.Equals(typedProperties)) + if (Properties.Equals(typedProperties)) { return this; } - return Clone>(clone => + return Clone(clone => { - clone.SetProperties(typedProperties); + ((RootField)clone).Properties = typedProperties; }); } - private void SetProperties(T newProperties) - { - properties = newProperties; - properties.Freeze(); - } - private static T ValidateProperties(FieldProperties newProperties) { Guard.NotNull(newProperties, nameof(newProperties)); @@ -67,7 +56,7 @@ namespace Squidex.Domain.Apps.Core.Schemas public override TResult Accept(IFieldVisitor visitor, TArgs args) { - return properties.Accept(visitor, this, args); + return Properties.Accept(visitor, this, args); } } } 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 b7637d180..578222821 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -13,7 +13,7 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class Schema : Cloneable + public sealed class Schema { private static readonly Dictionary EmptyPreviewUrls = new Dictionary(); private readonly string name; @@ -105,7 +105,6 @@ namespace Squidex.Domain.Apps.Core.Schemas this.name = name; this.properties = properties ?? new SchemaProperties(); - this.properties.Freeze(); this.isSingleton = isSingleton; } @@ -133,7 +132,6 @@ namespace Squidex.Domain.Apps.Core.Schemas return Clone(clone => { clone.properties = newProperties; - clone.Properties.Freeze(); }); } @@ -150,7 +148,6 @@ namespace Squidex.Domain.Apps.Core.Schemas return Clone(clone => { clone.scripts = newScripts; - clone.scripts.Freeze(); }); } @@ -203,7 +200,7 @@ namespace Squidex.Domain.Apps.Core.Schemas { rules ??= FieldRules.Empty; - if (fieldRules.SetEquals(rules)) + if (fieldRules.Equals(rules)) { return this; } @@ -326,5 +323,14 @@ namespace Squidex.Domain.Apps.Core.Schemas clone.fields = newFields; }); } + + private Schema Clone(Action updater) + { + var clone = (Schema)MemberwiseClone(); + + updater(clone); + + return clone; + } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs index 428cc3c3d..95a93534d 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaProperties.cs @@ -1,25 +1,24 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class SchemaProperties : NamedElementPropertiesBase + public sealed record SchemaProperties : NamedElementPropertiesBase { - public ReadOnlyCollection? Tags { get; set; } + public ImmutableList? Tags { get; init; } - public string? ContentsSidebarUrl { get; set; } + public string? ContentsSidebarUrl { get; init; } - public string? ContentSidebarUrl { get; set; } + public string? ContentSidebarUrl { get; init; } - public string? ContentEditorUrl { get; set; } + public string? ContentEditorUrl { get; init; } - public bool ValidateOnPublish { get; set; } + public bool ValidateOnPublish { get; init; } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs index da57b5999..ba7a3989a 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs @@ -7,24 +7,18 @@ namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators =true)] - public sealed class SchemaScripts : Freezable + public sealed record SchemaScripts { public static readonly SchemaScripts Empty = new SchemaScripts(); - static SchemaScripts() - { - Empty.Freeze(); - } + public string Change { get; init; } - public string Change { get; set; } + public string Create { get; init; } - public string Create { get; set; } + public string Update { get; init; } - public string Update { get; set; } + public string Delete { get; init; } - public string Delete { get; set; } - - public string Query { get; set; } + public string Query { get; init; } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs index ba2f1f29b..c4bb2d1bc 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/StringFieldProperties.cs @@ -5,44 +5,43 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class StringFieldProperties : FieldProperties + public sealed record StringFieldProperties : FieldProperties { - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; init; } - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue DefaultValues { get; init; } - public string? DefaultValue { get; set; } + public string? DefaultValue { get; init; } - public string? Pattern { get; set; } + public string? Pattern { get; init; } - public string? PatternMessage { get; set; } + public string? PatternMessage { get; init; } - public string? FolderId { get; set; } + public string? FolderId { get; init; } - public int? MinLength { get; set; } + public int? MinLength { get; init; } - public int? MaxLength { get; set; } + public int? MaxLength { get; init; } - public int? MinCharacters { get; set; } + public int? MinCharacters { get; init; } - public int? MaxCharacters { get; set; } + public int? MaxCharacters { get; init; } - public int? MinWords { get; set; } + public int? MinWords { get; init; } - public int? MaxWords { get; set; } + public int? MaxWords { get; init; } - public bool IsUnique { get; set; } + public bool IsUnique { get; init; } - public bool InlineEditable { get; set; } + public bool InlineEditable { get; init; } - public StringContentType ContentType { get; set; } + public StringContentType ContentType { get; init; } - public StringFieldEditor Editor { get; set; } + public StringFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs index 1a0f9adff..a1424aef6 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/TagsFieldProperties.cs @@ -5,26 +5,25 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class TagsFieldProperties : FieldProperties + public sealed record TagsFieldProperties : FieldProperties { - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; init; } - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; init; } - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; init; } - public int? MinItems { get; set; } + public int? MinItems { get; init; } - public int? MaxItems { get; set; } + public int? MaxItems { get; init; } - public TagsFieldEditor Editor { get; set; } + public TagsFieldEditor Editor { get; init; } - public TagsFieldNormalization Normalization { get; set; } + public TagsFieldNormalization Normalization { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldProperties.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldProperties.cs index 54f41b5e3..21978aab4 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldProperties.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/UIFieldProperties.cs @@ -7,10 +7,9 @@ namespace Squidex.Domain.Apps.Core.Schemas { - [Equals(DoNotAddEqualityOperators = true)] - public sealed class UIFieldProperties : FieldProperties + public sealed record UIFieldProperties : FieldProperties { - public UIFieldEditor Editor { get; set; } + public UIFieldEditor Editor { get; init; } public override T Accept(IFieldPropertiesVisitor visitor, TArgs args) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj index 023245e83..4383c8bf5 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj @@ -9,12 +9,6 @@ True - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs index fd15f2700..4947153d8 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/DefaultValues/DefaultValueFactory.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using System.Globalization; using NodaTime; using Squidex.Domain.Apps.Core.Schemas; @@ -132,7 +133,7 @@ namespace Squidex.Domain.Apps.Core.DefaultValues return value; } - private static IJsonValue Array(string[]? values) + private static IJsonValue Array(IEnumerable? values) { if (values != null) { 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 0d1bfb681..03efdd554 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs @@ -65,17 +65,17 @@ namespace Squidex.Domain.Apps.Core.EventSynchronization yield return @event; } - if (!source.FieldsInLists.SequenceEqual(target.FieldsInLists)) + if (!source.FieldsInLists.Equals(target.FieldsInLists)) { yield return new SchemaUIFieldsConfigured { FieldsInLists = target.FieldsInLists }; } - if (!source.FieldsInReferences.SequenceEqual(target.FieldsInReferences)) + if (!source.FieldsInReferences.Equals(target.FieldsInReferences)) { yield return new SchemaUIFieldsConfigured { FieldsInReferences = target.FieldsInReferences }; } - if (!source.FieldRules.SetEquals(target.FieldRules)) + if (!source.FieldRules.Equals(target.FieldRules)) { yield return new SchemaFieldRulesConfigured { FieldRules = target.FieldRules }; } @@ -196,8 +196,8 @@ namespace Squidex.Domain.Apps.Core.EventSynchronization if (sourceIds.Count > 1) { - var sourceNames = sourceIds.Select(x => x.Name).ToList(); - var targetNames = target.Ordered.Select(x => x.Name).ToList(); + var sourceNames = sourceIds.Select(x => x.Name).ToHashSet(); + var targetNames = target.Ordered.Select(x => x.Name).ToHashSet(); if (sourceNames.SetEquals(targetNames) && !sourceNames.SequenceEqual(targetNames)) { diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs index 80d408bda..d67b55959 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/GenerateJsonSchema/JsonTypeVisitor.cs @@ -177,4 +177,4 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema return null; } } -} \ No newline at end of file +} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs index 210434a75..64e39e953 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/DomainObject/AppDomainObject.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) @@ -310,8 +310,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject var events = new List { - CreateInitalEvent(command.Name), - CreateInitialLanguage() + CreateInitalEvent(command.Name) }; if (command.Actor.IsUser) @@ -450,11 +449,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject return new AppCreated { Name = name }; } - private static AppLanguageAdded CreateInitialLanguage() - { - return new AppLanguageAdded { Language = Language.EN }; - } - private static AppContributorAssigned CreateInitialOwner(RefToken actor) { return new AppContributorAssigned { ContributorId = actor.Identifier, Role = Role.Owner }; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs index 2bc0b441f..0e01618c0 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs @@ -19,14 +19,20 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public AssetFieldBuilder MustBeImage() { - Properties().MustBeImage = true; + Properties(p => p with + { + MustBeImage = true + }); return this; } public AssetFieldBuilder RequireSingle() { - Properties().MaxItems = 2; + Properties(p => p with + { + MaxItems = 1 + }); return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs index 86ca35c61..d59672d76 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs @@ -19,7 +19,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public BooleanFieldBuilder AsToggle() { - Properties().Editor = BooleanFieldEditor.Toggle; + Properties(p => p with + { + Editor = BooleanFieldEditor.Toggle, + EditorUrl = null + }); return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs index ad2dbaf4c..14261dacc 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs @@ -19,7 +19,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public DateTimeFieldBuilder AsDateTime() { - Properties().Editor = DateTimeFieldEditor.DateTime; + Properties(p => p with + { + Editor = DateTimeFieldEditor.DateTime, + EditorUrl = null + }); return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs index ef0443b70..f2f67e710 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; @@ -16,11 +17,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders private readonly UpsertSchemaField field; private readonly CreateSchema schema; - protected T Properties() where T : FieldProperties - { - return (T)field.Properties; - } - protected FieldBuilder(UpsertSchemaField field, CreateSchema schema) { this.field = field; @@ -29,14 +25,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public FieldBuilder Label(string? label) { - field.Properties.Label = label; + field.Properties = field.Properties with { Label = label }; return this; } public FieldBuilder Hints(string? hints) { - field.Properties.Hints = hints; + field.Properties = field.Properties with { Hints = hints }; return this; } @@ -57,11 +53,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public FieldBuilder Required() { - field.Properties.IsRequired = true; + field.Properties = field.Properties with { IsRequired = true }; return this; } + protected void Properties(Func updater) where T : FieldProperties + { + field.Properties = updater((T)field.Properties); + } + public FieldBuilder ShowInList() { schema.FieldsInLists ??= new FieldNames(); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ReferencesFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ReferencesFieldBuilder.cs index f047a7ef3..541710e1e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ReferencesFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/ReferencesFieldBuilder.cs @@ -8,6 +8,7 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { @@ -20,7 +21,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public ReferencesFieldBuilder WithSchemaId(DomainId id) { - Properties().SchemaId = id; + Properties(p => p with + { + SchemaIds = ImmutableList.Create(id) + }); return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs index 65d05accb..177717e5e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs @@ -34,8 +34,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public SchemaBuilder WithLabel(string? label) { - command.Properties ??= new SchemaProperties(); - command.Properties.Label = label; + if (command.Properties == null) + { + command.Properties = new SchemaProperties { Label = label }; + } + else + { + command.Properties = command.Properties with { Label = label }; + } return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs index 578f88e5d..f227149b8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs @@ -20,45 +20,66 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public StringFieldBuilder AsTextArea() { - Properties().Editor = StringFieldEditor.TextArea; + Properties(p => p with + { + EditorUrl = null, + Editor = StringFieldEditor.TextArea + }); return this; } public StringFieldBuilder AsRichText() { - Properties().Editor = StringFieldEditor.RichText; + Properties(p => p with + { + EditorUrl = null, + Editor = StringFieldEditor.RichText + }); return this; } public StringFieldBuilder AsDropDown(params string[] values) { - Properties().AllowedValues = ReadOnlyCollection.Create(values); - Properties().Editor = StringFieldEditor.Dropdown; + Properties(p => p with + { + AllowedValues = ImmutableList.Create(values), + EditorUrl = null, + Editor = StringFieldEditor.Dropdown + }); return this; } public StringFieldBuilder Unique() { - Properties().IsUnique = true; + Properties(p => p with + { + IsUnique = true + }); return this; } public StringFieldBuilder Pattern(string pattern, string? message = null) { - Properties().Pattern = pattern; - Properties().PatternMessage = message; + Properties(p => p with + { + Pattern = pattern, + PatternMessage = message + }); return this; } public StringFieldBuilder Length(int maxLength, int minLength = 0) { - Properties().MaxLength = maxLength; - Properties().MinLength = minLength; + Properties(p => p with + { + MaxLength = maxLength, + MinLength = minLength + }); return this; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs index 59c99073c..bb20e708d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs @@ -5,9 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; +using Squidex.Infrastructure.Collections; namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { @@ -20,7 +20,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public TagsFieldBuilder WithAllowedValues(params string[] values) { - Properties().AllowedValues = new ReadOnlyCollection(values); + Properties(p => p with + { + AllowedValues = ImmutableList.Create(values) + }); return this; } 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 61deac01f..2cd06ba35 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldRuleCommand.cs @@ -19,7 +19,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands public FieldRule ToFieldRule() { - return new FieldRule(Action, Field, Condition); + return new FieldRule(Action, Field) + { + 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 3581417cc..5e730bf89 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/IUpsertCommand.cs @@ -82,7 +82,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands var partitioning = Partitioning.FromString(eventField.Partitioning); - var field = eventField.Properties.CreateRootField(totalFields, eventField.Name, partitioning); + var field = + eventField.Properties.CreateRootField( + totalFields, + eventField.Name, partitioning, + eventField); if (field is ArrayField arrayField && eventField.Nested?.Length > 0) { @@ -90,22 +94,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { totalFields++; - var nestedField = nestedEventField.Properties.CreateNestedField(totalFields, nestedEventField.Name); - - if (nestedEventField.IsHidden) - { - nestedField = nestedField.Hide(); - } - - if (nestedEventField.IsDisabled) - { - nestedField = nestedField.Disable(); - } - - if (nestedEventField.IsLocked) - { - nestedField = nestedField.Lock(); - } + var nestedField = + nestedEventField.Properties.CreateNestedField( + totalFields, + nestedEventField.Name, + nestedEventField); arrayField = arrayField.AddField(nestedField); } @@ -113,21 +106,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands field = arrayField; } - if (eventField.IsHidden) - { - field = field.Hide(); - } - - if (eventField.IsDisabled) - { - field = field.Disable(); - } - - if (eventField.IsLocked) - { - field = field.Lock(); - } - schema = schema.AddField(field); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs index 4b165a145..558eb8b37 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public abstract class UpsertSchemaFieldBase + public abstract class UpsertSchemaFieldBase : IFieldSettings { public string Name { get; set; } diff --git a/backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs b/backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs index 903c0c04b..da27beec5 100644 --- a/backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs +++ b/backend/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Events.Schemas { - public abstract class SchemaCreatedFieldBase + public abstract class SchemaCreatedFieldBase : IFieldSettings { public string Name { get; set; } diff --git a/backend/src/Squidex.Infrastructure/Cloneable{T}.cs b/backend/src/Squidex.Infrastructure/Cloneable{T}.cs deleted file mode 100644 index fafd8f569..000000000 --- a/backend/src/Squidex.Infrastructure/Cloneable{T}.cs +++ /dev/null @@ -1,19 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; - -namespace Squidex.Infrastructure -{ - public abstract class Cloneable : Cloneable where T : Cloneable - { - protected T Clone(Action updater) - { - return base.Clone(updater); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/CollectionExtensions.cs b/backend/src/Squidex.Infrastructure/CollectionExtensions.cs index eac71f6a6..617fbfd10 100644 --- a/backend/src/Squidex.Infrastructure/CollectionExtensions.cs +++ b/backend/src/Squidex.Infrastructure/CollectionExtensions.cs @@ -7,12 +7,87 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Squidex.Infrastructure { public static class CollectionExtensions { + public static bool TryAdd(this IReadOnlyDictionary source, TKey key, TValue value, [MaybeNullWhen(false)] out Dictionary result) where TKey : notnull + { + result = null; + + if (!source.ContainsKey(key)) + { + var clone = new Dictionary(source) + { + [key] = value + }; + + result = clone; + + return true; + } + + return false; + } + + public static bool TrySet(this IReadOnlyDictionary source, TKey key, TValue value, [MaybeNullWhen(false)] out Dictionary result) where TKey : notnull + { + result = null; + + if (!source.TryGetValue(key, out var found) || !Equals(found, value)) + { + var clone = new Dictionary(source) + { + [key] = value + }; + + result = clone; + + return true; + } + + return false; + } + + public static bool TryUpdate(this IReadOnlyDictionary source, TKey key, TValue value, [MaybeNullWhen(false)] out Dictionary result) where TKey : notnull + { + result = null; + + if (source.TryGetValue(key, out var found) && !Equals(found, value)) + { + var clone = new Dictionary(source) + { + [key] = value + }; + + result = clone; + + return true; + } + + return false; + } + + public static bool TryRemove(this IReadOnlyDictionary source, TKey key, [MaybeNullWhen(false)] out Dictionary result) where TKey : notnull + { + result = null; + + if (source.ContainsKey(key)) + { + var clone = new Dictionary(source); + + result = clone; + result.Remove(key); + + return true; + } + + return false; + } + public static bool SetEquals(this IReadOnlyCollection source, IReadOnlyCollection other) { return source.Count == other.Count && source.Intersect(other).Count() == other.Count; @@ -152,27 +227,6 @@ namespace Squidex.Infrastructure return hashCode; } - public static int OrderedHashCode(this IEnumerable collection) where T : notnull - { - return collection.OrderedHashCode(EqualityComparer.Default); - } - - public static int OrderedHashCode(this IEnumerable collection, IEqualityComparer comparer) where T : notnull - { - Guard.NotNull(comparer, nameof(comparer)); - - var hashCodes = collection.Where(x => !Equals(x, null)).Select(x => x.GetHashCode()).OrderBy(x => x).ToArray(); - - var hashCode = 17; - - foreach (var code in hashCodes) - { - hashCode = (hashCode * 23) + code; - } - - return hashCode; - } - public static int DictionaryHashCode(this IReadOnlyDictionary dictionary) where TKey : notnull { return DictionaryHashCode(dictionary, EqualityComparer.Default, EqualityComparer.Default); @@ -222,6 +276,39 @@ namespace Squidex.Infrastructure return !dictionary.Except(other, comparer).Any(); } + public static bool EqualsList(this IReadOnlyList list, IReadOnlyList? other) + { + return EqualsList(list, other, EqualityComparer.Default); + } + + public static bool EqualsList(this IReadOnlyList list, IReadOnlyList? other, IEqualityComparer comparer) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(list, other)) + { + return true; + } + + if (list.Count != other.Count) + { + return false; + } + + for (var i = 0; i < list.Count; i++) + { + if (!comparer.Equals(list[i], other[i])) + { + return false; + } + } + + return true; + } + public static Dictionary ToDictionary(this IReadOnlyDictionary dictionary) where TKey : notnull { return dictionary.ToDictionary(x => x.Key, x => x.Value); diff --git a/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs b/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs index de9d8962c..d8c1a819a 100644 --- a/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs +++ b/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary.cs @@ -13,9 +13,50 @@ namespace Squidex.Infrastructure.Collections { public static class ImmutableDictionary { - public static ImmutableDictionary ToImmutableDictionary(this IEnumerable source, Func keyExtractor) where TKey : notnull + private static class Empties where TKey : notnull { - return new ImmutableDictionary(source.ToDictionary(keyExtractor)); +#pragma warning disable SA1401 // Fields should be private + public static ImmutableDictionary Instance = new ImmutableDictionary(); +#pragma warning restore SA1401 // Fields should be private + } + + public static ImmutableDictionary Empty() where TKey : notnull + { + return Empties.Instance; + } + + public static ImmutableDictionary ToImmutableDictionary(this Dictionary source) where TKey : notnull + { + if (source.Count == 0) + { + return Empty(); + } + + return new ImmutableDictionary(source); + } + + public static ImmutableDictionary ToImmutableDictionary(this IEnumerable source, Func keySelector) where TKey : notnull + { + var inner = source.ToDictionary(keySelector); + + if (inner.Count == 0) + { + return Empty(); + } + + return new ImmutableDictionary(inner); + } + + public static ImmutableDictionary ToImmutableDictionary(this IEnumerable source, Func keySelector, Func elementSelector) where TKey : notnull + { + var inner = source.ToDictionary(keySelector, elementSelector); + + if (inner.Count == 0) + { + return Empty(); + } + + return new ImmutableDictionary(inner); } } } diff --git a/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs b/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs index c5c7e4cf0..11ee89794 100644 --- a/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs +++ b/backend/src/Squidex.Infrastructure/Collections/ImmutableDictionary{TKey,TValue}.cs @@ -5,16 +5,17 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; namespace Squidex.Infrastructure.Collections { - public class ImmutableDictionary : IReadOnlyDictionary where TKey : notnull + public class ImmutableDictionary : IReadOnlyDictionary, IEquatable> where TKey : notnull { private static readonly Dictionary EmptyInner = new Dictionary(); - private Dictionary inner; + private readonly IDictionary inner; public TValue this[TKey key] { @@ -49,78 +50,13 @@ namespace Squidex.Infrastructure.Collections { } - public ImmutableDictionary(Dictionary inner) + public ImmutableDictionary(IDictionary inner) { Guard.NotNull(inner, nameof(inner)); this.inner = inner; } - public ImmutableDictionary With(TKey key, TValue value, IEqualityComparer? valueComparer = null) - { - return With>(key, value, valueComparer); - } - - public TArray With(TKey key, TValue value, IEqualityComparer? valueComparer = null) where TArray : ImmutableDictionary - { - if (!TryGetValue(key, out var found) || !IsEqual(value, found, valueComparer)) - { - var clone = new Dictionary(inner) - { - [key] = value - }; - - return Create(clone); - } - - return Self(); - } - - private static bool IsEqual(TValue lhs, TValue rhs, IEqualityComparer? comparer = null) - { - comparer ??= EqualityComparer.Default; - - return comparer.Equals(lhs, rhs); - } - - public ImmutableDictionary Without(TKey key) - { - return Without>(key); - } - - public TArray Without(TKey key) where TArray : ImmutableDictionary - { - if (!inner.ContainsKey(key)) - { - return Self(); - } - - if (Count == 1) - { - return Create(EmptyInner); - } - - var clone = new Dictionary(inner); - - clone.Remove(key); - - return Create(clone); - } - - private TArray Self() where TArray : ImmutableDictionary - { - return (this as TArray)!; - } - - private TArray Create(Dictionary clone) where TArray : ImmutableDictionary - { - var newClone = (TArray)MemberwiseClone(); - - newClone.inner = clone; - - return newClone; - } - public bool ContainsKey(TKey key) { return inner.ContainsKey(key); @@ -145,5 +81,20 @@ namespace Squidex.Infrastructure.Collections { return collection; } + + public override bool Equals(object? obj) + { + return Equals(obj as ImmutableDictionary); + } + + public bool Equals(ImmutableDictionary? other) + { + return this.EqualsDictionary(other); + } + + public override int GetHashCode() + { + return this.DictionaryHashCode(); + } } } diff --git a/backend/src/Squidex.Infrastructure/Collections/ImmutableList.cs b/backend/src/Squidex.Infrastructure/Collections/ImmutableList.cs new file mode 100644 index 000000000..5f73170c4 --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Collections/ImmutableList.cs @@ -0,0 +1,49 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; + +namespace Squidex.Infrastructure.Collections +{ + public static class ImmutableList + { + private static class Empties + { +#pragma warning disable SA1401 // Fields should be private + public static ImmutableList Instance = new ImmutableList(); +#pragma warning restore SA1401 // Fields should be private + } + + public static ImmutableList Empty() + { + return Empties.Instance; + } + + public static ImmutableList Create(params T[]? items) + { + if (items == null || items.Length == 0) + { + return Empty(); + } + + return new ImmutableList(items.ToList()); + } + + public static ImmutableList ToImmutableList(this IEnumerable source) + { + var inner = source.ToList(); + + if (inner.Count == 0) + { + return Empty(); + } + + return new ImmutableList(inner); + } + } +} diff --git a/backend/src/Squidex.Infrastructure/Collections/ImmutableList{T}.cs b/backend/src/Squidex.Infrastructure/Collections/ImmutableList{T}.cs new file mode 100644 index 000000000..56154f34d --- /dev/null +++ b/backend/src/Squidex.Infrastructure/Collections/ImmutableList{T}.cs @@ -0,0 +1,43 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Squidex.Infrastructure.Collections +{ + public class ImmutableList : ReadOnlyCollection, IEquatable> + { + private static readonly List EmptyInner = new List(); + + public ImmutableList() + : base(EmptyInner) + { + } + + public ImmutableList(IList list) + : base(list) + { + } + + public override bool Equals(object? obj) + { + return Equals(obj as ImmutableList); + } + + public virtual bool Equals(ImmutableList? other) + { + return this.EqualsList(other); + } + + public override int GetHashCode() + { + return this.SequentialHashCode(); + } + } +} diff --git a/backend/src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs b/backend/src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs deleted file mode 100644 index 83466a3a0..000000000 --- a/backend/src/Squidex.Infrastructure/Collections/ReadOnlyCollection.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; - -namespace Squidex.Infrastructure.Collections -{ - public static class ReadOnlyCollection - { - private static class Empties - { - public static readonly ReadOnlyCollection Collection = new ReadOnlyCollection(new List()); - } - - public static ReadOnlyCollection Create(params T[] items) - { - return new ReadOnlyCollection(items.ToList()); - } - - public static ReadOnlyCollection Empty() - { - return Empties.Collection; - } - - public static ReadOnlyCollection ToReadOnlyCollection(this IEnumerable source) - { - return new ReadOnlyCollection(source.ToList()); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/FodyWeavers.xml b/backend/src/Squidex.Infrastructure/FodyWeavers.xml deleted file mode 100644 index 1b04e09ca..000000000 --- a/backend/src/Squidex.Infrastructure/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure/FodyWeavers.xsd b/backend/src/Squidex.Infrastructure/FodyWeavers.xsd deleted file mode 100644 index f9b7ba295..000000000 --- a/backend/src/Squidex.Infrastructure/FodyWeavers.xsd +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. - - - - - A comma-separated list of error codes that can be safely ignored in assembly verification. - - - - - 'false' to turn off automatic generation of the XML Schema file. - - - - - \ No newline at end of file diff --git a/backend/src/Squidex.Infrastructure/IFreezable.cs b/backend/src/Squidex.Infrastructure/IFreezable.cs deleted file mode 100644 index c7095fccd..000000000 --- a/backend/src/Squidex.Infrastructure/IFreezable.cs +++ /dev/null @@ -1,14 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure -{ - public interface IFreezable - { - void Freeze(); - } -} diff --git a/backend/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs b/backend/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs index 2250f3886..0ff984e6c 100644 --- a/backend/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs +++ b/backend/src/Squidex.Infrastructure/Json/Newtonsoft/ConverterContractResolver.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using Squidex.Infrastructure.Collections; namespace Squidex.Infrastructure.Json.Newtonsoft { @@ -45,6 +46,20 @@ namespace Squidex.Infrastructure.Json.Newtonsoft return base.CreateArrayContract(implementationType); } + if (objectType.BaseType?.IsGenericType == true && objectType.BaseType.GetGenericTypeDefinition() == typeof(ImmutableList<>)) + { + var contract = base.CreateArrayContract(objectType); + + return contract; + } + + if (objectType.IsGenericType == true && objectType.GetGenericTypeDefinition() == typeof(ImmutableList<>)) + { + var contract = base.CreateArrayContract(objectType); + + return contract; + } + return base.CreateArrayContract(objectType); } diff --git a/backend/src/Squidex.Infrastructure/Queries/OData/PropertyPathVisitor.cs b/backend/src/Squidex.Infrastructure/Queries/OData/PropertyPathVisitor.cs index a5a3f750e..ad4bbc3b8 100644 --- a/backend/src/Squidex.Infrastructure/Queries/OData/PropertyPathVisitor.cs +++ b/backend/src/Squidex.Infrastructure/Queries/OData/PropertyPathVisitor.cs @@ -19,9 +19,9 @@ namespace Squidex.Infrastructure.Queries.OData { } - public static ImmutableList Visit(QueryNode node) + public static PropertyPath Visit(QueryNode node) { - return node.Accept(Instance); + return new PropertyPath(node.Accept(Instance)); } public override ImmutableList Visit(ConvertNode nodeIn) diff --git a/backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs b/backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs index a6bf83e07..1d52eeb8d 100644 --- a/backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs +++ b/backend/src/Squidex.Infrastructure/Queries/PropertyPath.cs @@ -7,13 +7,12 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Linq; +using Squidex.Infrastructure.Collections; namespace Squidex.Infrastructure.Queries { - public sealed class PropertyPath : ReadOnlyCollection + public sealed class PropertyPath : ImmutableList { private static readonly char[] Separators = { '.', '/' }; @@ -28,27 +27,36 @@ namespace Squidex.Infrastructure.Queries public static implicit operator PropertyPath(string path) { - return new PropertyPath(path?.Split(Separators, StringSplitOptions.RemoveEmptyEntries).ToList()!); + return Create(path?.Split(Separators, StringSplitOptions.RemoveEmptyEntries).ToList()); } public static implicit operator PropertyPath(string[] path) { - return new PropertyPath(path?.ToList()!); + return Create(path); } public static implicit operator PropertyPath(List path) { - return new PropertyPath(path); + return Create(path); } - public static implicit operator PropertyPath(ImmutableList path) + public override string ToString() { - return new PropertyPath(path); + return string.Join(".", this); } - public override string ToString() + private static PropertyPath Create(IEnumerable? source) { - return string.Join(".", this); + var inner = source?.ToList(); + + if (inner == null || inner.Count == 0) + { + throw new ArgumentException("Path cannot be empty.", nameof(source)); + } + else + { + return new PropertyPath(inner); + } } } } diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/ArrayComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/ArrayComparer.cs deleted file mode 100644 index cb2f0461c..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/ArrayComparer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class ArrayComparer : IDeepComparer - { - private readonly IDeepComparer itemComparer; - - public ArrayComparer(IDeepComparer itemComparer) - { - this.itemComparer = itemComparer; - } - - public bool IsEquals(object? x, object? y) - { - var lhs = (T[])x!; - var rhs = (T[])y!; - - if (lhs.Length != rhs.Length) - { - return false; - } - - for (var i = 0; i < lhs.Length; i++) - { - if (!itemComparer.IsEquals(lhs[i], rhs[i])) - { - return false; - } - } - - return true; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/CollectionComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/CollectionComparer.cs deleted file mode 100644 index 646e64a47..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/CollectionComparer.cs +++ /dev/null @@ -1,69 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections; -using Squidex.Infrastructure.Reflection.Internal; - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class CollectionComparer : IDeepComparer - { - private readonly IDeepComparer itemComparer; - private readonly PropertyAccessor? sizeProperty; - - public CollectionComparer(IDeepComparer itemComparer, PropertyAccessor? sizeProperty) - { - this.itemComparer = itemComparer; - this.sizeProperty = sizeProperty; - } - - public bool IsEquals(object? x, object? y) - { - var lhs = (IEnumerable)x!; - var rhs = (IEnumerable)y!; - - if (sizeProperty != null) - { - var sizeLhs = sizeProperty.Get(lhs); - var sizeRhs = sizeProperty.Get(rhs); - - if (!Equals(sizeLhs, sizeRhs)) - { - return false; - } - } - - var enumeratorLhs = lhs.GetEnumerator(); - var enumeratorRhs = rhs.GetEnumerator(); - - while (true) - { - var movedLhs = enumeratorLhs.MoveNext(); - var movedRhs = enumeratorRhs.MoveNext(); - - if (movedRhs != movedLhs) - { - return false; - } - - if (movedRhs) - { - if (!itemComparer.IsEquals(enumeratorLhs.Current, enumeratorRhs.Current)) - { - return false; - } - } - else - { - break; - } - } - - return true; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/DeepEqualityComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/DeepEqualityComparer.cs deleted file mode 100644 index f5a6fe230..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/DeepEqualityComparer.cs +++ /dev/null @@ -1,32 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; - -namespace Squidex.Infrastructure.Reflection.Equality -{ - public sealed class DeepEqualityComparer : IEqualityComparer - { - public static readonly DeepEqualityComparer Default = new DeepEqualityComparer(); - private readonly IDeepComparer comparer; - - public DeepEqualityComparer(IDeepComparer? comparer = null) - { - this.comparer = comparer ?? SimpleEquals.Build(typeof(T)); - } - - public bool Equals(T? x, T? y) - { - return comparer.IsEquals(x, y); - } - - public int GetHashCode(T obj) - { - return 0; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/DefaultComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/DefaultComparer.cs deleted file mode 100644 index 6a48c944e..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/DefaultComparer.cs +++ /dev/null @@ -1,36 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class DefaultComparer : IDeepComparer - { - public bool IsEquals(object? x, object? y) - { - if (Equals(x, y)) - { - return true; - } - - if (x == null || y == null) - { - return false; - } - - var type = x.GetType(); - - if (type != y.GetType()) - { - return false; - } - - var inner = SimpleEquals.BuildInner(type); - - return inner.IsEquals(x, y); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/DictionaryComparer{TKey,TValue}.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/DictionaryComparer{TKey,TValue}.cs deleted file mode 100644 index 00fefdb1e..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/DictionaryComparer{TKey,TValue}.cs +++ /dev/null @@ -1,37 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using System.Linq; - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class DictionaryComparer : IDeepComparer where TKey : notnull - { - private readonly IEqualityComparer> comparer; - - public DictionaryComparer(IDeepComparer comparer) - { - this.comparer = new CollectionExtensions.KeyValuePairComparer( - new DeepEqualityComparer(comparer), - new DeepEqualityComparer(comparer)); - } - - public bool IsEquals(object? x, object? y) - { - var lhs = (IReadOnlyDictionary)x!; - var rhs = (IReadOnlyDictionary)y!; - - if (lhs.Count != rhs.Count) - { - return false; - } - - return !lhs.Except(rhs, comparer).Any(); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/IDeepComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/IDeepComparer.cs deleted file mode 100644 index c25fa57e0..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/IDeepComparer.cs +++ /dev/null @@ -1,14 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Reflection.Equality -{ - public interface IDeepComparer - { - bool IsEquals(object? x, object? y); - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/NoopComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/NoopComparer.cs deleted file mode 100644 index 138c1977d..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/NoopComparer.cs +++ /dev/null @@ -1,17 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class NoopComparer : IDeepComparer - { - public bool IsEquals(object? x, object? y) - { - return false; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/ObjectComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/ObjectComparer.cs deleted file mode 100644 index de6f3aec6..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/ObjectComparer.cs +++ /dev/null @@ -1,50 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Linq; -using System.Reflection; -using Squidex.Infrastructure.Reflection.Internal; - -namespace Squidex.Infrastructure.Reflection.Equality -{ - public sealed class ObjectComparer : IDeepComparer - { - private readonly PropertyAccessor[] propertyAccessors; - private readonly IDeepComparer valueComparer; - - public ObjectComparer(IDeepComparer valueComparer, Type type) - { - propertyAccessors = - type.GetPublicProperties() - .Where(x => x.CanRead) - .Where(x => x.GetCustomAttribute() == null) - .Select(x => new PropertyAccessor(x.DeclaringType!, x)) - .ToArray(); - - this.valueComparer = valueComparer; - } - - public bool IsEquals(object? x, object? y) - { - for (var i = 0; i < propertyAccessors.Length; i++) - { - var property = propertyAccessors[i]; - - var lhs = property.Get(x!); - var rhs = property.Get(y!); - - if (!valueComparer.IsEquals(lhs, rhs)) - { - return false; - } - } - - return true; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/Equality/SetComparer.cs b/backend/src/Squidex.Infrastructure/Reflection/Equality/SetComparer.cs deleted file mode 100644 index df933e763..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/Equality/SetComparer.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using System.Linq; - -namespace Squidex.Infrastructure.Reflection.Equality -{ - internal sealed class SetComparer : IDeepComparer - { - private readonly IEqualityComparer equalityComparer; - - public SetComparer(IDeepComparer comparer) - { - equalityComparer = new DeepEqualityComparer(comparer); - } - - public bool IsEquals(object? x, object? y) - { - var lhs = (ISet)x!; - var rhs = (ISet)y!; - - if (lhs.Count != rhs.Count) - { - return false; - } - - return lhs.Intersect(rhs, equalityComparer).Count() == rhs.Count; - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Reflection/SimpleEquals.cs b/backend/src/Squidex.Infrastructure/Reflection/SimpleEquals.cs deleted file mode 100644 index 75a741789..000000000 --- a/backend/src/Squidex.Infrastructure/Reflection/SimpleEquals.cs +++ /dev/null @@ -1,128 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using Squidex.Infrastructure.Reflection.Equality; -using Squidex.Infrastructure.Reflection.Internal; - -namespace Squidex.Infrastructure.Reflection -{ - public static class SimpleEquals - { - private static readonly ConcurrentDictionary Comparers = new ConcurrentDictionary(); - private static readonly HashSet SimpleTypes = new HashSet(); - private static readonly DefaultComparer DefaultComparer = new DefaultComparer(); - private static readonly NoopComparer NoopComparer = new NoopComparer(); - - static SimpleEquals() - { - SimpleTypes.Add(typeof(string)); - SimpleTypes.Add(typeof(Uri)); - } - - internal static IDeepComparer Build(Type type) - { - return BuildCore(type) ?? DefaultComparer; - } - - internal static IDeepComparer BuildInner(Type type) - { - return BuildCore(type) ?? NoopComparer; - } - - private static IDeepComparer? BuildCore(Type t) - { - return Comparers.GetOrAdd(t, type => - { - if (IsSimpleType(type) || IsEquatable(type)) - { - return null; - } - - if (IsArray(type)) - { - var comparerType = typeof(ArrayComparer<>).MakeGenericType(type.GetElementType()!); - - return (IDeepComparer)Activator.CreateInstance(comparerType, DefaultComparer)!; - } - - if (IsSet(type)) - { - var comparerType = typeof(SetComparer<>).MakeGenericType(type.GetGenericArguments()); - - return (IDeepComparer)Activator.CreateInstance(comparerType, DefaultComparer)!; - } - - if (IsDictionary(type)) - { - var comparerType = typeof(DictionaryComparer<,>).MakeGenericType(type.GetGenericArguments()); - - return (IDeepComparer)Activator.CreateInstance(comparerType, DefaultComparer)!; - } - - if (IsCollection(type)) - { - PropertyAccessor? count = null; - - var countProperty = type.GetProperty("Count"); - - if (countProperty != null && countProperty.PropertyType == typeof(int)) - { - count = new PropertyAccessor(type, countProperty); - } - - return (IDeepComparer)Activator.CreateInstance(typeof(CollectionComparer), DefaultComparer, count)!; - } - - return new ObjectComparer(DefaultComparer, type); - }); - } - - private static bool IsArray(Type type) - { - return type.IsArray; - } - - private static bool IsCollection(Type type) - { - return type.GetInterfaces().Contains(typeof(IEnumerable)); - } - - private static bool IsSimpleType(Type type) - { - return type.IsValueType || SimpleTypes.Contains(type); - } - - private static bool IsEquatable(Type type) - { - return type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEquatable<>)); - } - - private static bool IsSet(Type type) - { - return - type.GetGenericArguments().Length == 1 && - type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ISet<>)); - } - - private static bool IsDictionary(Type type) - { - return - type.GetGenericArguments().Length == 2 && - type.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>)); - } - - public static bool IsEquals(T x, T y) - { - return DefaultComparer.IsEquals(x, y); - } - } -} diff --git a/backend/src/Squidex.Infrastructure/Security/PermissionSet.cs b/backend/src/Squidex.Infrastructure/Security/PermissionSet.cs index c90896185..6e3c616fe 100644 --- a/backend/src/Squidex.Infrastructure/Security/PermissionSet.cs +++ b/backend/src/Squidex.Infrastructure/Security/PermissionSet.cs @@ -6,46 +6,42 @@ // ========================================================================== using System; -using System.Collections; using System.Collections.Generic; using System.Linq; +using Squidex.Infrastructure.Collections; namespace Squidex.Infrastructure.Security { - public sealed class PermissionSet : IReadOnlyCollection + public sealed class PermissionSet : ImmutableList { public static readonly PermissionSet Empty = new PermissionSet(Array.Empty()); - private readonly List permissions; private readonly Lazy display; - public int Count - { - get => permissions.Count; - } - public PermissionSet(params Permission[] permissions) - : this((IEnumerable)permissions) + : this(permissions?.ToList()!) { } public PermissionSet(params string[] permissions) - : this(permissions?.Select(x => new Permission(x))!) + : this(permissions?.Select(x => new Permission(x)).ToList()!) { } public PermissionSet(IEnumerable permissions) - : this(permissions?.Select(x => new Permission(x))!) + : this(permissions?.Select(x => new Permission(x)).ToList()!) { } public PermissionSet(IEnumerable permissions) + : this(permissions?.ToList()!) { - Guard.NotNull(permissions, nameof(permissions)); - - this.permissions = permissions.ToList(); + } - display = new Lazy(() => string.Join(";", this.permissions)); + public PermissionSet(IList permissions) + : base(permissions) + { + display = new Lazy(() => string.Join(";", this)); } public PermissionSet Add(string permission) @@ -59,7 +55,7 @@ namespace Squidex.Infrastructure.Security { Guard.NotNull(permission, nameof(permission)); - return new PermissionSet(permissions.Union(Enumerable.Repeat(permission, 1)).Distinct()); + return new PermissionSet(this.Union(Enumerable.Repeat(permission, 1)).Distinct()); } public bool Allows(Permission? other) @@ -69,7 +65,7 @@ namespace Squidex.Infrastructure.Security return false; } - return permissions.Any(x => x.Allows(other)); + return this.Any(x => x.Allows(other)); } public bool Includes(Permission? other) @@ -79,7 +75,7 @@ namespace Squidex.Infrastructure.Security return false; } - return permissions.Any(x => x.Includes(other)); + return this.Any(x => x.Includes(other)); } public override string ToString() @@ -89,17 +85,7 @@ namespace Squidex.Infrastructure.Security public IEnumerable ToIds() { - return permissions.Select(x => x.Id); - } - - public IEnumerator GetEnumerator() - { - return permissions.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return permissions.GetEnumerator(); + return this.Select(x => x.Id); } } } 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 dc79acfd8..415b49890 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateAppSettingsDto.cs @@ -44,9 +44,9 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models Patterns?.Select(x => new Pattern(x.Name, x.Regex) { Message = x.Message - }).ToReadOnlyCollection()!, + }).ToImmutableList()!, Editors = - Editors?.Select(x => new Editor(x.Name, x.Url)).ToReadOnlyCollection()! + Editors?.Select(x => new Editor(x.Name, x.Url)).ToImmutableList()! } }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateWorkflowDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateWorkflowDto.cs index de4f64e64..050b11495 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateWorkflowDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/UpdateWorkflowDto.cs @@ -6,10 +6,10 @@ // ========================================================================== using System.Collections.Generic; -using System.Linq; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Validation; namespace Squidex.Areas.Api.Controllers.Apps.Models @@ -30,7 +30,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models /// /// The schema ids. /// - public List? SchemaIds { get; set; } + public ImmutableList? SchemaIds { get; set; } /// /// The initial step. @@ -42,7 +42,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models { var workflow = new Workflow( Initial, - Steps?.ToDictionary( + Steps?.ToImmutableDictionary( x => x.Key, x => x.Value?.ToStep()!), SchemaIds, 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 2d4bce96d..55fe559fa 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowStepDto.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Validation; using NoUpdateType = Squidex.Domain.Apps.Core.Contents.NoUpdate; @@ -64,7 +65,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models public WorkflowStep ToStep() { return new WorkflowStep( - Transitions?.ToDictionary( + Transitions?.ToImmutableDictionary( y => y.Key, y => y.Value?.ToTransition()!), Color, diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs index 52ef13473..2d65c52ed 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/Triggers/ContentChangedRuleTriggerDto.cs @@ -26,7 +26,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models.Triggers public override RuleTrigger ToTrigger() { - var schemas = Schemas?.Select(x => x.ToTrigger()).ToReadOnlyCollection(); + var schemas = Schemas?.Select(x => x.ToTrigger()).ToImmutableList(); return new ContentChangedTriggerV2 { HandleAll = HandleAll, Schemas = schemas }; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs index 9c7f949b1..5bb8cecc1 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs @@ -6,11 +6,11 @@ // ========================================================================== using System; -using System.Collections.ObjectModel; using System.Linq; using System.Runtime.Serialization; using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Validation; using Squidex.Web.Json; @@ -61,7 +61,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// /// Tags for automation processes. /// - public ReadOnlyCollection? Tags { get; set; } + public ImmutableList? Tags { get; set; } public abstract FieldProperties ToProperties(); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs index 8168d6990..7f969a907 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/AssetsFieldPropertiesDto.cs @@ -6,8 +6,8 @@ // ========================================================================== using System; -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields @@ -22,12 +22,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The language specific default value as a list of asset ids. /// - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; set; } /// /// The default value as a list of asset ids. /// - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; set; } /// /// The initial id to the folder. @@ -107,7 +107,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The allowed file extensions. /// - public ReadOnlyCollection? AllowedExtensions { get; set; } + public ImmutableList? AllowedExtensions { get; set; } /// /// True, if duplicate values are allowed. diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs index 01d187079..e302aab76 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/NumberFieldPropertiesDto.cs @@ -5,8 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields @@ -36,7 +36,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The allowed values for the field value. /// - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; set; } /// /// Indicates if the field value must be unique. Ignored for nested fields and localized fields. diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs index 0a9467157..11b7db44a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ReferencesFieldPropertiesDto.cs @@ -5,9 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields @@ -17,12 +17,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The language specific default value as a list of content ids. /// - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; set; } /// /// The default value as a list of content ids. /// - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; set; } /// /// The minimum allowed items for the field value. @@ -57,7 +57,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The id of the referenced schemas. /// - public ReadOnlyCollection? SchemaIds { get; set; } + public ImmutableList? SchemaIds { get; set; } public override FieldProperties ToProperties() { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs index 6db2e0932..da8fb2d8b 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/StringFieldPropertiesDto.cs @@ -5,8 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields @@ -71,7 +71,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The allowed values for the field value. /// - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; set; } /// /// Indicates if the field value must be unique. Ignored for nested fields and localized fields. diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs index a26e7cec1..ab72def48 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/TagsFieldPropertiesDto.cs @@ -5,8 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields @@ -16,12 +16,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The language specific default value for the field value. /// - public LocalizedValue DefaultValues { get; set; } + public LocalizedValue?> DefaultValues { get; set; } /// /// The default value. /// - public string[]? DefaultValue { get; set; } + public ImmutableList? DefaultValue { get; set; } /// /// The minimum allowed items for the field value. @@ -36,7 +36,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields /// /// The allowed values for the field value. /// - public ReadOnlyCollection? AllowedValues { get; set; } + public ImmutableList? AllowedValues { get; set; } /// /// The editor that is used to manage this field. diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs index 7b04c088c..58fd628b5 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaPropertiesDto.cs @@ -5,7 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; +using System.Collections.Immutable; using Squidex.Infrastructure.Validation; namespace Squidex.Areas.Api.Controllers.Schemas.Models @@ -47,6 +47,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// /// Tags for automation processes. /// - public ReadOnlyCollection? Tags { get; set; } + public ImmutableList? Tags { get; set; } } } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs index 50a26e45b..49695734a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateSchemaDto.cs @@ -5,9 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.ObjectModel; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Validation; @@ -50,7 +50,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// /// Tags for automation processes. /// - public ReadOnlyCollection? Tags { get; set; } + public ImmutableList? Tags { get; set; } public UpdateSchema ToCommand() { diff --git a/backend/src/Squidex/Config/Domain/AppsServices.cs b/backend/src/Squidex/Config/Domain/AppsServices.cs index e538d5ba3..e60098e36 100644 --- a/backend/src/Squidex/Config/Domain/AppsServices.cs +++ b/backend/src/Squidex/Config/Domain/AppsServices.cs @@ -66,7 +66,7 @@ namespace Squidex.Config.Domain { Settings = new AppSettings { - Patterns = patterns.ToReadOnlyCollection() + Patterns = ImmutableList.ToImmutableList(patterns) } }; }); diff --git a/backend/src/Squidex/Config/Domain/SerializationServices.cs b/backend/src/Squidex/Config/Domain/SerializationServices.cs index 3f44a7468..ce1b24267 100644 --- a/backend/src/Squidex/Config/Domain/SerializationServices.cs +++ b/backend/src/Squidex/Config/Domain/SerializationServices.cs @@ -46,8 +46,6 @@ namespace Squidex.Config.Domain new ExecutionResultJsonConverter(new ErrorInfoProvider()), new JsonValueConverter(), new StringEnumConverter(), - new SurrogateConverter(), - new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter, JsonFilterSurrogate>(), new SurrogateConverter(), @@ -55,7 +53,6 @@ namespace Squidex.Config.Domain new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter(), - new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter(), new WriteonlyGeoJsonConverter()); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs index b7299d6a8..50621811c 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientJsonTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.TestHelpers; using Xunit; @@ -35,7 +34,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps var serialized = clients.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(clients); + Assert.Equal(clients, serialized); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs index aca0e21b3..17e81f8ba 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Xunit; @@ -22,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var clients_1 = clients_0.Add("2", "my-secret"); - clients_1["2"].Should().BeEquivalentTo(new AppClient("2", "my-secret")); + Assert.Equal(new AppClient("2", "my-secret"), clients_1["2"]); } [Fact] @@ -30,7 +29,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var clients_1 = clients_0.Add("2", "my-secret", Role.Reader); - clients_1["2"].Should().BeEquivalentTo(new AppClient("2", "my-secret") with { Role = Role.Reader }); + Assert.Equal(new AppClient("2", "my-secret") with { Role = Role.Reader }, clients_1["2"]); } [Fact] @@ -46,7 +45,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var client_1 = clients_0.Update("1", role: Role.Reader); - client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret") with { Role = Role.Reader }); + Assert.Equal(new AppClient("1", "my-secret") with { Role = Role.Reader }, client_1["1"]); } [Fact] @@ -54,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var client_1 = clients_0.Update("1", name: "New-Name"); - client_1["1"].Should().BeEquivalentTo(new AppClient("New-Name", "my-secret")); + Assert.Equal(new AppClient("New-Name", "my-secret"), client_1["1"]); } [Fact] @@ -62,7 +61,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var client_1 = clients_0.Update("1", allowAnonymous: true); - client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret") with { AllowAnonymous = true }); + Assert.Equal(new AppClient("1", "my-secret") with { AllowAnonymous = true }, client_1["1"]); } [Fact] @@ -70,7 +69,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var client_1 = clients_0.Update("1", apiCallsLimit: 1000); - client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret") with { ApiCallsLimit = 1000 }); + Assert.Equal(new AppClient("1", "my-secret") with { ApiCallsLimit = 1000 }, client_1["1"]); } [Fact] @@ -78,7 +77,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var client_1 = clients_0.Update("1", apiTrafficLimit: 1000); - client_1["1"].Should().BeEquivalentTo(new AppClient("1", "my-secret") with { ApiTrafficLimit = 1000 }); + Assert.Equal(new AppClient("1", "my-secret") with { ApiTrafficLimit = 1000 }, client_1["1"]); } [Fact] diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs index dc5622ab8..e2e7ad17e 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppContributorsJsonTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.TestHelpers; using Xunit; @@ -25,7 +24,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps var serialized = contributors.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(contributors); + Assert.Equal(contributors, serialized); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppImageTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppImageTests.cs index 39b761a3f..1c475d5dc 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppImageTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppImageTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.TestHelpers; using Xunit; @@ -41,7 +40,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps var serialized = appImage.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(appImage); + Assert.Equal(appImage, serialized); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs index 6aec25506..4c3fe4065 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPlanTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure; @@ -22,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps var serialized = plan.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(plan); + Assert.Equal(plan, serialized); } } } 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 331e94f05..8822804cb 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 @@ -9,6 +9,7 @@ using System.Collections.Generic; using FluentAssertions; using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Xunit; #pragma warning disable SA1310 // Field names must not contain underscore @@ -114,7 +115,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Language.EN] = new LanguageConfig(), [Language.DE] = new LanguageConfig(), [Language.ES] = new LanguageConfig(true), - [Language.IT] = new LanguageConfig(true, Language.ES) + [Language.IT] = new LanguageConfig(true, ImmutableList.Create(Language.ES)) }); Assert.Equal(Language.DE, config.Master); @@ -126,6 +127,14 @@ namespace Squidex.Domain.Apps.Core.Model.Apps config_0.Set(Language.EN); } + [Fact] + public void Should_return_same_language_if_already_added() + { + var config_1 = config_0.Set(Language.EN); + + Assert.Same(config_1, config_0); + } + [Fact] public void Should_make_master_language() { @@ -246,7 +255,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig(true, Language.EN) + [Language.IT] = new LanguageConfig(true, ImmutableList.Create(Language.EN)) }); Assert.Equal(Language.EN, config_2.Master); @@ -263,7 +272,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps new Dictionary { [Language.EN] = new LanguageConfig(), - [Language.IT] = new LanguageConfig(true, Language.EN) + [Language.IT] = new LanguageConfig(true, ImmutableList.Create(Language.EN)) }); Assert.Equal(Language.EN, config_2.Master); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs index a71ffeda8..37750775b 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs @@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps { var roles_1 = roles_0.Add(role); - roles_1[role].Should().BeEquivalentTo(Role.Create(role)); + Assert.Equal(Role.Create(role), roles_1[role]); } [Fact] 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 85dba7576..fcc4306f0 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 @@ -7,7 +7,6 @@ using System; using System.Linq; -using FluentAssertions; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure.Json.Objects; @@ -26,7 +25,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents var serialized = fieldData.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(fieldData); + Assert.Equal(fieldData, serialized); } [Fact] 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 6c9f00839..ab97363d8 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 @@ -7,11 +7,11 @@ using System.Collections.Generic; using System.Linq; -using FluentAssertions; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents.Json; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Xunit; namespace Squidex.Domain.Apps.Core.Model.Contents @@ -22,20 +22,22 @@ namespace Squidex.Domain.Apps.Core.Model.Contents public void Should_serialize_and_deserialize() { var workflow = new Workflow( - Status.Draft, new Dictionary + Status.Draft, + new Dictionary { [Status.Draft] = new WorkflowStep( new Dictionary { [Status.Published] = WorkflowTransition.When("Expression", "Role1", "Role2") - }, + }.ToImmutableDictionary(), "#00ff00", NoUpdate.When("Expression", "Role1", "Role2")) - }, new List { DomainId.NewGuid() }, "MyName"); + }.ToImmutableDictionary(), + ImmutableList.Create(DomainId.NewGuid()), "MyName"); var serialized = workflow.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(workflow); + Assert.Equal(workflow, serialized); } [Fact] @@ -45,7 +47,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents var serialized = workflow.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(workflow); + Assert.Equal(workflow, serialized); } [Fact] @@ -55,7 +57,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents var serialized = jsonStep.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(new WorkflowStep(null, null, NoUpdate.Always)); + Assert.Equal(new WorkflowStep(null, null, NoUpdate.Always), serialized); } [Fact] @@ -65,7 +67,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents var serialized = step.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(step); + Assert.Equal(step, serialized); } [Fact] @@ -80,4 +82,4 @@ namespace Squidex.Domain.Apps.Core.Model.Contents Assert.Equal(source.Role, result?.Roles?.Single()); } } -} \ No newline at end of file +} 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 f6f926f3b..df73daa48 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 @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Linq; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Infrastructure.Collections; using Xunit; namespace Squidex.Domain.Apps.Core.Model.Contents @@ -15,7 +16,8 @@ namespace Squidex.Domain.Apps.Core.Model.Contents public class WorkflowTests { private readonly Workflow workflow = new Workflow( - Status.Draft, new Dictionary + Status.Draft, + new Dictionary { [Status.Draft] = new WorkflowStep( @@ -23,13 +25,13 @@ namespace Squidex.Domain.Apps.Core.Model.Contents { [Status.Archived] = WorkflowTransition.When("ToArchivedExpr", "ToArchivedRole"), [Status.Published] = WorkflowTransition.When("ToPublishedExpr", "ToPublishedRole") - }, + }.ToImmutableDictionary(), StatusColors.Draft), [Status.Archived] = new WorkflowStep(), [Status.Published] = new WorkflowStep() - }); + }.ToImmutableDictionary()); [Fact] public void Should_provide_default_workflow_if_none_found() @@ -142,4 +144,4 @@ namespace Squidex.Domain.Apps.Core.Model.Contents Assert.Same(workflow.Steps[status1], step1); } } -} \ No newline at end of file +} diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs index deccd7a12..832480f24 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsJsonTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using FluentAssertions; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure; @@ -22,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Model.Contents var serialized = workflows.SerializeAndDeserialize(); - serialized.Should().BeEquivalentTo(workflows); + Assert.Equal(workflows, serialized); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs index 4a06e2104..b524f4149 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules private readonly Rule rule_0 = new Rule(new ContentChangedTriggerV2(), new TestAction1()); - public sealed class OtherTrigger : RuleTrigger + public sealed record OtherTrigger : RuleTrigger { public override T Accept(IRuleTriggerVisitor visitor) { @@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules } } - public sealed class MigratedTrigger : RuleTrigger, IMigrated + public sealed record MigratedTrigger : RuleTrigger, IMigrated { public override T Accept(IRuleTriggerVisitor visitor) { @@ -53,13 +53,13 @@ namespace Squidex.Domain.Apps.Core.Model.Rules } [TypeName(nameof(TestAction1))] - public sealed class TestAction1 : RuleAction + public sealed record TestAction1 : RuleAction { public string Property { get; set; } } [TypeName(nameof(TestAction2))] - public sealed class TestAction2 : RuleAction + public sealed record TestAction2 : RuleAction { public string Property { get; set; } } @@ -184,12 +184,5 @@ namespace Squidex.Domain.Apps.Core.Model.Rules Assert.IsType(serialized.Trigger); } - - [Theory] - [MemberData(nameof(Triggers))] - public void Should_freeze_triggers(RuleTrigger trigger) - { - TestUtils.TestFreeze(trigger); - } } } 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 ae087f634..4f69c8c0d 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 @@ -5,7 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Collections; using Xunit; namespace Squidex.Domain.Apps.Core.Model.Schemas @@ -17,18 +19,18 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas { var lhs = new StringFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { ["iv"] = "ABC" - } + }) }; var rhs = new StringFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { ["iv"] = "ABC" - } + }) }; Assert.Equal(lhs, rhs); @@ -39,18 +41,18 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas { var lhs = new TagsFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue?>(new Dictionary?> { - ["iv"] = new[] { "A", "B", "C" } - } + ["iv"] = ImmutableList.Create("A", "B", "C") + }) }; var rhs = new TagsFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue?>(new Dictionary?> { - ["iv"] = new[] { "A", "B", "C" } - } + ["iv"] = ImmutableList.Create("A", "B", "C") + }) }; Assert.Equal(lhs, rhs); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs index 1623e18c2..b6a5f8aec 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Generic; using System.Linq; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.TestHelpers; using Xunit; #pragma warning disable SA1310 // Field names must not contain underscore @@ -30,7 +29,6 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas [Fact] public void Should_instantiate_field() { - Assert.True(field_0.RawProperties.IsFrozen); Assert.Equal("my-field", field_0.Name); } @@ -97,7 +95,6 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas var field_1 = field_0.Update(new NumberFieldProperties { Hints = "my-hints" }); Assert.Null(field_0.RawProperties.Hints); - Assert.True(field_1.RawProperties.IsFrozen); Assert.Equal("my-hints", field_1.RawProperties.Hints); } @@ -106,12 +103,5 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas { Assert.Throws(() => field_0.Update(new StringFieldProperties())); } - - [Theory] - [MemberData(nameof(FieldProperties))] - public void Should_freeze_field_properties(FieldProperties action) - { - TestUtils.TestFreeze(action); - } } } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs index a097e860e..2a9eaf9ea 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/DefaultValues/DefaultValuesTests.cs @@ -5,12 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using NodaTime; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.DefaultValues; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Json.Objects; using Xunit; @@ -94,7 +96,7 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues { var field = Fields.Assets(1, "1", Partitioning.Invariant, - new AssetsFieldProperties { DefaultValue = new[] { "1", "2" } }); + new AssetsFieldProperties { DefaultValue = ImmutableList.Create("1", "2" ) }); Assert.Equal(JsonValue.Array("1", "2"), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); } @@ -106,11 +108,11 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.Assets(1, "1", Partitioning.Invariant, new AssetsFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue?>(new Dictionary?> { [language.Iso2Code] = null - }, - DefaultValue = new[] { "1", "2" } + }), + DefaultValue = ImmutableList.Create("1", "2") }); Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -133,10 +135,10 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.Boolean(1, "1", Partitioning.Invariant, new BooleanFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { [language.Iso2Code] = null - }, + }), DefaultValue = true }); @@ -180,10 +182,10 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.DateTime(1, "1", Partitioning.Invariant, new DateTimeFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { [language.Iso2Code] = null - }, + }), DefaultValue = FutureDays(15) }); @@ -227,10 +229,10 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.Number(1, "1", Partitioning.Invariant, new NumberFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { [language.Iso2Code] = null - }, + }), DefaultValue = 12 }); @@ -252,7 +254,7 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues { var field = Fields.References(1, "1", Partitioning.Invariant, - new ReferencesFieldProperties { DefaultValue = new[] { "1", "2" } }); + new ReferencesFieldProperties { DefaultValue = ImmutableList.Create("1", "2") }); Assert.Equal(JsonValue.Array("1", "2"), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); } @@ -264,11 +266,11 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.References(1, "1", Partitioning.Invariant, new ReferencesFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue?>(new Dictionary?> { [language.Iso2Code] = null - }, - DefaultValue = new[] { "1", "2" } + }), + DefaultValue = ImmutableList.Create("1", "2") }); Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); @@ -291,10 +293,10 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.String(1, "1", Partitioning.Invariant, new StringFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue(new Dictionary { [language.Iso2Code] = null - }, + }), DefaultValue = "default" }); @@ -306,7 +308,7 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues { var field = Fields.Tags(1, "1", Partitioning.Invariant, - new TagsFieldProperties { DefaultValue = new[] { "tag1", "tag2" } }); + new TagsFieldProperties { DefaultValue = ImmutableList.Create("tag1", "tag2") }); Assert.Equal(JsonValue.Array("tag1", "tag2"), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); } @@ -318,11 +320,11 @@ namespace Squidex.Domain.Apps.Core.Operations.DefaultValues Fields.Tags(1, "1", Partitioning.Invariant, new TagsFieldProperties { - DefaultValues = new LocalizedValue + DefaultValues = new LocalizedValue?>(new Dictionary?> { [language.Iso2Code] = null - }, - DefaultValue = new[] { "tag1", "tag2" } + }), + DefaultValue = ImmutableList.Create("tag1", "tag2") }); Assert.Equal(JsonValue.Array(), DefaultValueFactory.CreateDefaultValue(field, now, language.Iso2Code)); 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 8b1d02d17..e4f4a8fb3 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 @@ -152,7 +152,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization var events = sourceSchema.Synchronize(targetSchema, idGenerator); events.ShouldHaveSameEvents( - new SchemaUIFieldsConfigured { FieldsInLists = new FieldNames("2", "1") } + new SchemaUIFieldsConfigured { FieldsInLists = FieldNames.Create("2", "1") } ); } @@ -170,7 +170,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization var events = sourceSchema.Synchronize(targetSchema, idGenerator); events.ShouldHaveSameEvents( - new SchemaUIFieldsConfigured { FieldsInReferences = new FieldNames("2", "1") } + new SchemaUIFieldsConfigured { FieldsInReferences = FieldNames.Create("2", "1") } ); } @@ -188,7 +188,7 @@ namespace Squidex.Domain.Apps.Core.Operations.EventSynchronization var events = sourceSchema.Synchronize(targetSchema, idGenerator); events.ShouldHaveSameEvents( - new SchemaFieldRulesConfigured { FieldRules = new FieldRules(FieldRule.Hide("1")) } + new SchemaFieldRulesConfigured { FieldRules = FieldRules.Create(FieldRule.Hide("1")) } ); } diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs index a21b7632c..9a5011f8a 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleElementRegistryTests.cs @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules Display = "Action display", Description = "Action description.", ReadMore = "https://www.readmore.com/")] - public sealed class MyInvalidRuleAction : RuleAction + public sealed record MyInvalidRuleAction : RuleAction { [DataType(DataType.Custom)] public string Custom { get; set; } @@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules Display = "Action display", Description = "Action description.", ReadMore = "https://www.readmore.com/")] - public sealed class MyRuleAction : RuleAction + public sealed record MyRuleAction : RuleAction { [LocalizedRequired] [Display(Name = "Url Name", Description = "Url Description")] 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 ce43c50fe..2fe4af2e4 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 @@ -47,11 +47,11 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { } - public sealed class InvalidAction : RuleAction + public sealed record InvalidAction : RuleAction { } - public sealed class ValidAction : RuleAction + public sealed record ValidAction : RuleAction { } @@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules public int Value { get; set; } } - public sealed class InvalidTrigger : RuleTrigger + public sealed record InvalidTrigger : RuleTrigger { public override T Accept(IRuleTriggerVisitor visitor) { diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs index 2caf96032..f31650b83 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/NumberFieldTests.cs @@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent [Fact] public async Task Should_add_error_if_number_is_not_allowed() { - var sut = Field(new NumberFieldProperties { AllowedValues = ReadOnlyCollection.Create(10d) }); + var sut = Field(new NumberFieldProperties { AllowedValues = ImmutableList.Create(10d) }); await sut.ValidateAsync(CreateValue(20), errors); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs index 736279ea9..8fde7b3c1 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/StringFieldTests.cs @@ -107,7 +107,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent [Fact] public async Task Should_add_error_if_string_not_allowed() { - var sut = Field(new StringFieldProperties { AllowedValues = ReadOnlyCollection.Create("Foo") }); + var sut = Field(new StringFieldProperties { AllowedValues = ImmutableList.Create("Foo") }); await sut.ValidateAsync(CreateValue("Bar"), errors); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs index 7aca56f73..1bbf310c1 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs @@ -139,7 +139,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent [Fact] public async Task Should_add_error_if_value_contains_an_not_allowed_values() { - var sut = Field(new TagsFieldProperties { AllowedValues = ReadOnlyCollection.Create("tag-2", "tag-3") }); + var sut = Field(new TagsFieldProperties { AllowedValues = ImmutableList.Create("tag-2", "tag-3") }); await sut.ValidateAsync(CreateValue("tag-1", "tag-2", null), errors); 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 3ced92b97..6b59bc6ef 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 @@ -214,7 +214,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ValidateContent.Validators [Fact] public async Task Should_add_error_if_image_has_invalid_extension() { - var sut = Validator(new AssetsFieldProperties { AllowedExtensions = ReadOnlyCollection.Create("mp4") }); + var sut = Validator(new AssetsFieldProperties { AllowedExtensions = ImmutableList.Create("mp4") }); await sut.ValidateAsync(CreateValue(document.AssetId, image1.AssetId), errors); diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs index 116095707..f8dc43189 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs @@ -5,9 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using System.Linq; -using System.Reflection; using System.Security.Claims; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -22,14 +19,12 @@ using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules.Json; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas.Json; -using Squidex.Infrastructure; using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Reflection; -using Xunit; namespace Squidex.Domain.Apps.Core.TestHelpers { @@ -57,8 +52,6 @@ namespace Squidex.Domain.Apps.Core.TestHelpers new EnvelopeHeadersConverter(), new JsonValueConverter(), new StringEnumConverter(), - new SurrogateConverter(), - new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter, JsonFilterSurrogate>(), new SurrogateConverter(), @@ -66,7 +59,6 @@ namespace Squidex.Domain.Apps.Core.TestHelpers new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter(), - new SurrogateConverter(), new SurrogateConverter(), new SurrogateConverter(), new WriteonlyGeoJsonConverter()), @@ -122,7 +114,7 @@ namespace Squidex.Domain.Apps.Core.TestHelpers .AddReferences(109, "root-references", Partitioning.Invariant, new ReferencesFieldProperties()) .AddString(110, "root-string1", Partitioning.Invariant, - new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ReadOnlyCollection.Create("a", "b") }) + new StringFieldProperties { Label = "My String1", IsRequired = true, AllowedValues = ImmutableList.Create("a", "b") }) .AddString(111, "root-string2", Partitioning.Invariant, new StringFieldProperties { Hints = "My String1" }) .AddTags(112, "root-tags", Partitioning.Language, @@ -152,47 +144,5 @@ namespace Squidex.Domain.Apps.Core.TestHelpers return DefaultSerializer.Deserialize(json); } - - public static void TestFreeze(IFreezable sut) - { - var properties = - sut.GetType().GetRuntimeProperties() - .Where(x => - x.CanWrite && - x.CanRead && - x.Name != "IsFrozen"); - - foreach (var property in properties) - { - var value = - property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; - - property.SetValue(sut, value); - - var result = property.GetValue(sut); - - Assert.Equal(value, result); - } - - sut.Freeze(); - - foreach (var property in properties) - { - var value = - property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null; - - Assert.Throws(() => - { - try - { - property.SetValue(sut, value); - } - catch (Exception ex) when (ex.InnerException != null) - { - throw ex.InnerException; - } - }); - } - } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppTests.cs index fe81ec7f0..f04d2c92d 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Apps/DomainObject/Guards/GuardAppTests.cs @@ -159,7 +159,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { Settings = new AppSettings { - Patterns = ReadOnlyCollection.Create( + Patterns = ImmutableList.Create( new Pattern(null!, "[a-z]")) } }; @@ -175,7 +175,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { Settings = new AppSettings { - Patterns = ReadOnlyCollection.Create( + Patterns = ImmutableList.Create( new Pattern("name", null!)) } }; @@ -206,7 +206,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { Settings = new AppSettings { - Editors = ReadOnlyCollection.Create( + Editors = ImmutableList.Create( new Editor(null!, "[a-z]")) } }; @@ -222,7 +222,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { Settings = new AppSettings { - Editors = ReadOnlyCollection.Create( + Editors = ImmutableList.Create( new Editor("name", null!)) } }; @@ -238,9 +238,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { Settings = new AppSettings { - Patterns = ReadOnlyCollection.Create( + Patterns = ImmutableList.Create( new Pattern("name", "[a-z]")), - Editors = ReadOnlyCollection.Create( + Editors = ImmutableList.Create( new Editor("name", "url/to/editor")) } }; 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 22946e760..907f2cb3b 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 @@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Validation; using Xunit; @@ -75,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards new Dictionary { [Status.Published] = new WorkflowStep() - }), + }.ToImmutableDictionary()), WorkflowId = workflowId }; @@ -93,7 +94,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards new Dictionary { [Status.Published] = new WorkflowStep() - }), + }.ToImmutableDictionary()), WorkflowId = workflowId }; @@ -111,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards new Dictionary { [Status.Draft] = new WorkflowStep() - }), + }.ToImmutableDictionary()), WorkflowId = workflowId }; @@ -130,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards { [Status.Published] = null!, [Status.Draft] = new WorkflowStep() - }), + }.ToImmutableDictionary()), WorkflowId = workflowId }; @@ -152,9 +153,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards new Dictionary { [Status.Archived] = WorkflowTransition.Always - }), + }.ToImmutableDictionary()), [Status.Draft] = new WorkflowStep() - }), + }.ToImmutableDictionary()), WorkflowId = workflowId }; @@ -178,8 +179,8 @@ namespace Squidex.Domain.Apps.Entities.Apps.DomainObject.Guards new Dictionary { [Status.Draft] = null! - }) - }), + }.ToImmutableDictionary()) + }.ToImmutableDictionary()), WorkflowId = workflowId }; 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 46291eb1b..474142d46 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentChangedTriggerHandlerTests.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using FakeItEasy; @@ -23,6 +22,7 @@ using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Contents; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.EventSourcing; using Xunit; @@ -152,13 +152,11 @@ namespace Squidex.Domain.Apps.Entities.Contents { var trigger = new ContentChangedTriggerV2 { - Schemas = new ReadOnlyCollection(new List - { + Schemas = ImmutableList.Create( new ContentChangedTriggerSchemaV2 { SchemaId = schemaMatch.Id - } - }) + }) }; var ctx = Context(trigger); @@ -361,13 +359,14 @@ namespace Squidex.Domain.Apps.Entities.Contents if (schemaId != null) { - trigger.Schemas = new ReadOnlyCollection(new List + trigger = trigger with { - new ContentChangedTriggerSchemaV2 - { - SchemaId = schemaId.Id, Condition = condition - } - }); + Schemas = ImmutableList.Create( + new ContentChangedTriggerSchemaV2 + { + SchemaId = schemaId.Id, Condition = condition + }) + }; } action(Context(trigger)); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs index a3189aa86..083d6b7a7 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultWorkflowsValidatorTests.cs @@ -15,6 +15,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Xunit; namespace Squidex.Domain.Apps.Entities.Contents @@ -60,8 +61,8 @@ namespace Squidex.Domain.Apps.Entities.Contents var workflows = Workflows.Empty .Add(id1, "workflow1") .Add(id2, "workflow2") - .Update(id1, new Workflow(default, null, new List { schemaId.Id })) - .Update(id2, new Workflow(default, null, new List { schemaId.Id })); + .Update(id1, new Workflow(default, null, ImmutableList.Create(schemaId.Id))) + .Update(id2, new Workflow(default, null, ImmutableList.Create(schemaId.Id))); var errors = await sut.ValidateAsync(appId.Id, workflows); @@ -79,8 +80,8 @@ namespace Squidex.Domain.Apps.Entities.Contents var workflows = Workflows.Empty .Add(id1, "workflow1") .Add(id2, "workflow2") - .Update(id1, new Workflow(default, null, new List { oldSchemaId })) - .Update(id2, new Workflow(default, null, new List { oldSchemaId })); + .Update(id1, new Workflow(default, null, ImmutableList.Create(oldSchemaId))) + .Update(id2, new Workflow(default, null, ImmutableList.Create(oldSchemaId))); var errors = await sut.ValidateAsync(appId.Id, workflows); @@ -96,7 +97,7 @@ namespace Squidex.Domain.Apps.Entities.Contents var workflows = Workflows.Empty .Add(id1, "workflow1") .Add(id2, "workflow2") - .Update(id1, new Workflow(default, null, new List { schemaId.Id })); + .Update(id1, new Workflow(default, null, ImmutableList.Create(schemaId.Id))); var errors = await sut.ValidateAsync(appId.Id, workflows); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs index d3c925750..3cdc81b76 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DynamicContentWorkflowTests.cs @@ -16,6 +16,7 @@ using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; +using Squidex.Infrastructure.Collections; using Xunit; namespace Squidex.Domain.Apps.Entities.Contents @@ -38,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents new Dictionary { [Status.Draft] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Archived, NoUpdate.Always), [Status.Draft] = new WorkflowStep( @@ -46,7 +47,7 @@ namespace Squidex.Domain.Apps.Entities.Contents { [Status.Archived] = WorkflowTransition.Always, [Status.Published] = WorkflowTransition.When("data.field.iv === 2", "Editor") - }, + }.ToImmutableDictionary(), StatusColors.Draft), [Status.Published] = new WorkflowStep( @@ -54,9 +55,9 @@ namespace Squidex.Domain.Apps.Entities.Contents { [Status.Archived] = WorkflowTransition.Always, [Status.Draft] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Published, NoUpdate.When("data.field.iv === 2", "Owner", "Editor")) - }); + }.ToImmutableDictionary()); public DynamicContentWorkflowTests() { @@ -71,17 +72,17 @@ namespace Squidex.Domain.Apps.Entities.Contents new Dictionary { [Status.Published] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Draft), [Status.Published] = new WorkflowStep( new Dictionary { [Status.Draft] = WorkflowTransition.Always - }, + }.ToImmutableDictionary(), StatusColors.Published) - }, - new List { simpleSchemaId.Id }); + }.ToImmutableDictionary(), + ImmutableList.Create(simpleSchemaId.Id)); var workflows = Workflows.Empty.Set(workflow).Set(DomainId.NewGuid(), simpleWorkflow); @@ -419,4 +420,4 @@ namespace Squidex.Domain.Apps.Entities.Contents return content; } } -} \ No newline at end of file +} 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 a6ed431a6..4ef1f85ef 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 @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards private readonly NamedId schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); private readonly IAppProvider appProvider = A.Fake(); - public sealed class TestAction : RuleAction + public sealed record TestAction : RuleAction { public Uri Url { get; set; } } @@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards { Trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Empty() + Schemas = ImmutableList.Empty() }, Action = null!, }); @@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards { Trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Empty() + Schemas = ImmutableList.Empty() }, Action = new TestAction { @@ -111,7 +111,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards { Trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Empty() + Schemas = ImmutableList.Empty() }, Action = new TestAction { 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 8a8d59017..357994074 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 @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers { var trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2()) + Schemas = ImmutableList.Create(new ContentChangedTriggerSchemaV2()) }; var errors = await RuleTriggerValidator.ValidateAsync(appId.Id, trigger, appProvider); @@ -54,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers var trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId.Id }) + Schemas = ImmutableList.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId.Id }) }; var errors = await RuleTriggerValidator.ValidateAsync(appId.Id, trigger, appProvider); @@ -81,7 +81,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers { var trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Empty() + Schemas = ImmutableList.Empty() }; var errors = await RuleTriggerValidator.ValidateAsync(appId.Id, trigger, appProvider); @@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject.Guards.Triggers var trigger = new ContentChangedTriggerV2 { - Schemas = ReadOnlyCollection.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId.Id }) + Schemas = ImmutableList.Create(new ContentChangedTriggerSchemaV2 { SchemaId = schemaId.Id }) }; var errors = await RuleTriggerValidator.ValidateAsync(appId.Id, trigger, appProvider); 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 b6506299b..a6dc41562 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 @@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject get => DomainId.Combine(AppId, ruleId); } - public sealed class TestAction : RuleAction + public sealed record TestAction : RuleAction { public int Value { get; set; } } @@ -284,4 +284,4 @@ namespace Squidex.Domain.Apps.Entities.Rules.DomainObject return result.Payload; } } -} \ No newline at end of file +} 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 9ed33d93a..0002f5e13 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Entities.Rules private readonly NamedId appId = NamedId.Of(DomainId.NewGuid(), "my-app"); private readonly RuleEnqueuer sut; - public sealed class TestAction : RuleAction + public sealed record TestAction : RuleAction { public Uri Url { get; set; } } @@ -172,4 +172,4 @@ namespace Squidex.Domain.Apps.Entities.Rules return new RuleEntity { RuleDef = rule, Id = DomainId.NewGuid() }; } } -} \ No newline at end of file +} 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 046929204..a543da36c 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 @@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards.FieldProperti [InlineData(NumberFieldEditor.Radio)] public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(NumberFieldEditor editor) { - var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create(1.0) }; + var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create(1.0) }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards.FieldProperti [InlineData(NumberFieldEditor.Stars)] public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(NumberFieldEditor editor) { - var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create(1.0) }; + var sut = new NumberFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create(1.0) }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); 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 dff857464..2d75f08f4 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 @@ -153,7 +153,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards.FieldProperti [InlineData(StringFieldEditor.TextArea)] public void Should_add_error_if_inline_editing_is_not_allowed_for_editor(StringFieldEditor editor) { - var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create("Value") }; + var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create("Value") }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -170,7 +170,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards.FieldProperti [InlineData(StringFieldEditor.Slug)] public void Should_not_add_error_if_inline_editing_is_allowed_for_editor(StringFieldEditor editor) { - var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ReadOnlyCollection.Create("Value") }; + var sut = new StringFieldProperties { InlineEditable = true, Editor = editor, AllowedValues = ImmutableList.Create("Value") }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); 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 cd772faf5..dedcd782e 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 @@ -331,8 +331,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards Partitioning = Partitioning.Invariant.Key } }, - FieldsInLists = new FieldNames("field1"), - FieldsInReferences = new FieldNames("field1"), + FieldsInLists = FieldNames.Create("field1"), + FieldsInReferences = FieldNames.Create("field1"), Name = "new-schema" }); @@ -367,7 +367,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards Partitioning = Partitioning.Invariant.Key } }, - FieldsInLists = new FieldNames(null!, null!, "field3", "field1", "field1", "field4"), + FieldsInLists = FieldNames.Create(null!, null!, "field3", "field1", "field1", "field4"), FieldsInReferences = null, Name = "new-schema" }); @@ -406,7 +406,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards } }, FieldsInLists = null, - FieldsInReferences = new FieldNames(null!, null!, "field3", "field1", "field1", "field4"), + FieldsInReferences = FieldNames.Create(null!, null!, "field3", "field1", "field1", "field4"), Name = "new-schema" }); @@ -429,7 +429,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards var command = CreateCommand(new CreateSchema { FieldsInLists = null, - FieldsInReferences = new FieldNames("meta.id"), + FieldsInReferences = FieldNames.Create("meta.id"), Name = "new-schema" }); @@ -479,8 +479,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards } } }, - FieldsInLists = new FieldNames("field1", "meta.id"), - FieldsInReferences = new FieldNames("field1"), + FieldsInLists = FieldNames.Create("field1", "meta.id"), + FieldsInReferences = FieldNames.Create("field1"), Name = "new-schema" }); @@ -492,7 +492,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards { var command = new ConfigureUIFields { - FieldsInLists = new FieldNames(null!, null!, "field3", "field1", "field1", "field4"), + FieldsInLists = FieldNames.Create(null!, null!, "field3", "field1", "field1", "field4"), FieldsInReferences = null }; @@ -515,7 +515,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards var command = new ConfigureUIFields { FieldsInLists = null, - FieldsInReferences = new FieldNames(null!, null!, "field3", "field1", "field1", "field4") + FieldsInReferences = FieldNames.Create(null!, null!, "field3", "field1", "field1", "field4") }; ValidationAssert.Throws(() => GuardSchema.CanConfigureUIFields(command, schema_0), @@ -537,7 +537,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards var command = new ConfigureUIFields { FieldsInLists = null, - FieldsInReferences = new FieldNames("meta.id") + FieldsInReferences = FieldNames.Create("meta.id") }; ValidationAssert.Throws(() => GuardSchema.CanConfigureUIFields(command, schema_0), @@ -550,8 +550,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject.Guards { var command = new ConfigureUIFields { - FieldsInLists = new FieldNames("field1", "meta.id"), - FieldsInReferences = new FieldNames("field2") + FieldsInLists = FieldNames.Create("field1", "meta.id"), + FieldsInReferences = FieldNames.Create("field2") }; GuardSchema.CanConfigureUIFields(command, schema_0); 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 f19fc48dc..5b14931f7 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 @@ -175,7 +175,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject LastEvents .ShouldHaveSameEvents( - CreateEvent(new SchemaFieldRulesConfigured { FieldRules = new FieldRules(FieldRule.Disable("field1")) }) + CreateEvent(new SchemaFieldRulesConfigured { FieldRules = FieldRules.Create(FieldRule.Disable("field1")) }) ); } @@ -184,7 +184,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { var command = new ConfigureUIFields { - FieldsInLists = new FieldNames(fieldName) + FieldsInLists = FieldNames.Create(fieldName) }; await ExecuteCreateAsync(); @@ -207,7 +207,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.DomainObject { var command = new ConfigureUIFields { - FieldsInReferences = new FieldNames(fieldName) + FieldsInReferences = FieldNames.Create(fieldName) }; await ExecuteCreateAsync(); 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 1d5989c0f..6eb53ecab 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaCommandsTests.cs @@ -38,8 +38,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas Partitioning = "language" } }, - FieldsInLists = new FieldNames("meta.id", "myString"), - FieldsInReferences = new FieldNames("myString"), + FieldsInLists = FieldNames.Create("meta.id", "myString"), + FieldsInReferences = FieldNames.Create("myString"), Scripts = new SchemaScripts { Change = "change-script" @@ -60,8 +60,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas }) .HideField(1).DisableField(1).LockField(1) .ChangeCategory("myCategory") - .SetFieldsInLists(new FieldNames("meta.id", "myString")) - .SetFieldsInReferences(new FieldNames("myString")) + .SetFieldsInLists(FieldNames.Create("meta.id", "myString")) + .SetFieldsInReferences(FieldNames.Create("myString")) .SetScripts(new SchemaScripts { Change = "change-script" diff --git a/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs b/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs index e649d36c8..a929ab19f 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/CollectionExtensionsTests.cs @@ -201,33 +201,6 @@ namespace Squidex.Infrastructure Assert.NotEqual(collection2.SequentialHashCode(), collection1.SequentialHashCode()); } - [Fact] - public void OrderedHashCode_should_return_same_hash_codes_for_list_with_same_order() - { - var collection1 = new[] { 3, 5, 6 }; - var collection2 = new[] { 3, 5, 6 }; - - Assert.Equal(collection2.OrderedHashCode(), collection1.OrderedHashCode()); - } - - [Fact] - public void OrderedHashCode_should_return_different_hash_codes_for_list_with_different_items() - { - var collection1 = new[] { 3, 5, 6 }; - var collection2 = new[] { 3, 4, 1 }; - - Assert.NotEqual(collection2.OrderedHashCode(), collection1.OrderedHashCode()); - } - - [Fact] - public void OrderedHashCode_should_return_same_hash_codes_for_list_with_different_order() - { - var collection1 = new[] { 3, 5, 6 }; - var collection2 = new[] { 6, 5, 3 }; - - Assert.Equal(collection2.OrderedHashCode(), collection1.OrderedHashCode()); - } - [Fact] public void EqualsDictionary_should_return_true_for_equal_dictionaries() { @@ -312,6 +285,73 @@ namespace Squidex.Infrastructure Assert.NotEqual(lhs.DictionaryHashCode(), rhs.DictionaryHashCode()); } + [Fact] + public void EqualsList_should_return_true_for_equal_lists() + { + var lhs = new List + { + 1, + 2 + }; + var rhs = new List + { + 1, + 2 + }; + + Assert.True(lhs.EqualsList(rhs)); + } + + [Fact] + public void EqualsList_should_return_false_for_different_sizes() + { + var lhs = new List + { + 1, + 2 + }; + var rhs = new List + { + 1 + }; + + Assert.False(lhs.EqualsList(rhs)); + } + + [Fact] + public void EqualsList_should_return_false_for_different_values() + { + var lhs = new List + { + 1, + 2 + }; + var rhs = new List + { + 1, + 3 + }; + + Assert.False(lhs.EqualsList(rhs)); + } + + [Fact] + public void EqualsList_should_return_false_for_different_order() + { + var lhs = new List + { + 1, + 2 + }; + var rhs = new List + { + 2, + 1 + }; + + Assert.False(lhs.EqualsList(rhs)); + } + [Fact] public void Foreach_should_call_action_foreach_item_with_index() { @@ -329,4 +369,4 @@ namespace Squidex.Infrastructure Assert.Equal(source, targetItems); } } -} \ No newline at end of file +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableDictionaryTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableDictionaryTests.cs new file mode 100644 index 000000000..7ead992e4 --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableDictionaryTests.cs @@ -0,0 +1,68 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Squidex.Infrastructure.Collections +{ + public class ImmutableDictionaryTests + { + [Fact] + public void Should_return_empty_instance_for_empty_source() + { + var result = new Dictionary().ToImmutableDictionary(); + + Assert.Same(ImmutableDictionary.Empty(), result); + } + + [Fact] + public void Should_return_empty_instance_for_empty_source_and_key_selector() + { + var result = Enumerable.Empty().ToImmutableDictionary(x => x); + + Assert.Same(ImmutableDictionary.Empty(), result); + } + + [Fact] + public void Should_return_empty_instance_for_empty_source_and_value_selector() + { + var result = Enumerable.Empty().ToImmutableDictionary(x => x, x => x); + + Assert.Same(ImmutableDictionary.Empty(), result); + } + + [Fact] + public void Should_make_correct_object_equal_comparisons() + { + var obj1a = new Dictionary { [1] = 1 }.ToImmutableDictionary(); + var obj1b = new Dictionary { [1] = 1 }.ToImmutableDictionary(); + + var dictionaryOtherValue = new Dictionary { [1] = 2 }.ToImmutableDictionary(); + var dictionaryOtherKey = new Dictionary { [2] = 1 }.ToImmutableDictionary(); + + var dictionaryOtherCount = new Dictionary { [1] = 1, [2] = 2 }.ToImmutableDictionary(); + + Assert.Equal(obj1a, obj1b); + Assert.Equal(obj1a.GetHashCode(), obj1b.GetHashCode()); + Assert.True(obj1a.Equals((object)obj1b)); + + Assert.NotEqual(obj1a, dictionaryOtherValue); + Assert.NotEqual(obj1a.GetHashCode(), dictionaryOtherValue.GetHashCode()); + Assert.False(obj1a.Equals((object)dictionaryOtherValue)); + + Assert.NotEqual(obj1a, dictionaryOtherKey); + Assert.NotEqual(obj1a.GetHashCode(), dictionaryOtherKey.GetHashCode()); + Assert.False(obj1a.Equals((object)dictionaryOtherKey)); + + Assert.NotEqual(obj1a, dictionaryOtherCount); + Assert.NotEqual(obj1a.GetHashCode(), dictionaryOtherCount.GetHashCode()); + Assert.False(obj1a.Equals((object)dictionaryOtherCount)); + } + } +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs new file mode 100644 index 000000000..fba54285f --- /dev/null +++ b/backend/tests/Squidex.Infrastructure.Tests/Collections/ImmutableListTests.cs @@ -0,0 +1,62 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Squidex.Infrastructure.Collections +{ + public class ImmutableListTests + { + [Fact] + public void Should_return_empty_instance_for_empty_array() + { + var result = ImmutableList.Create(); + + Assert.Same(ImmutableList.Empty(), result); + } + + [Fact] + public void Should_return_empty_instance_for_null_array() + { + var result = ImmutableList.Create((int[]?)null); + + Assert.Same(ImmutableList.Empty(), result); + } + + [Fact] + public void Should_return_empty_instance_for_empty_enumerable() + { + var result = Enumerable.Empty().ToImmutableList(); + + Assert.Same(ImmutableList.Empty(), result); + } + + [Fact] + public void Should_make_correct_equal_comparisons() + { + var list1a = ImmutableList.Create(1); + var list1b = ImmutableList.Create(1); + + var listOtherValue = ImmutableList.Create(2); + var listOtherSize = ImmutableList.Create(1, 2); + + Assert.Equal(list1a, list1b); + Assert.Equal(list1a.GetHashCode(), list1b.GetHashCode()); + Assert.True(list1a.Equals((object)list1b)); + + Assert.NotEqual(list1a, listOtherValue); + Assert.NotEqual(list1a.GetHashCode(), listOtherValue.GetHashCode()); + Assert.False(list1a.Equals((object)listOtherValue)); + + Assert.NotEqual(list1a, listOtherSize); + Assert.NotEqual(list1a.GetHashCode(), listOtherSize.GetHashCode()); + Assert.False(list1a.Equals((object)listOtherSize)); + } + } +} diff --git a/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs b/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs index fc115830f..a48165304 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs @@ -138,21 +138,21 @@ namespace Squidex.Infrastructure var domainId_1_a = DomainId.Create("1"); var domainId_1_b = DomainId.Create("1"); - var domainId2_a = DomainId.Create("2"); + var domainId_2_a = DomainId.Create("2"); Assert.Equal(domainId_1_a, domainId_1_b); Assert.Equal(domainId_1_a.GetHashCode(), domainId_1_b.GetHashCode()); Assert.True(domainId_1_a.Equals((object)domainId_1_b)); - Assert.NotEqual(domainId_1_a, domainId2_a); - Assert.NotEqual(domainId_1_a.GetHashCode(), domainId2_a.GetHashCode()); - Assert.False(domainId_1_a.Equals((object)domainId2_a)); + Assert.NotEqual(domainId_1_a, domainId_2_a); + Assert.NotEqual(domainId_1_a.GetHashCode(), domainId_2_a.GetHashCode()); + Assert.False(domainId_1_a.Equals((object)domainId_2_a)); Assert.True(domainId_1_a == domainId_1_b); - Assert.True(domainId_1_a != domainId2_a); + Assert.True(domainId_1_a != domainId_2_a); Assert.False(domainId_1_a != domainId_1_b); - Assert.False(domainId_1_a == domainId2_a); + Assert.False(domainId_1_a == domainId_2_a); } [Fact] diff --git a/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs index ab4bdab26..62c4e79f2 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs +++ b/backend/tests/Squidex.Infrastructure.Tests/Json/Objects/JsonObjectTests.cs @@ -19,62 +19,63 @@ namespace Squidex.Infrastructure.Json.Objects [Fact] public void Should_make_correct_object_equal_comparisons() { - var obj_count1_key1_val1_a = JsonValue.Object().Add("key1", 1); - var obj_count1_key1_val1_b = JsonValue.Object().Add("key1", 1); + var obj1a = JsonValue.Object().Add("key1", 1); + var obj1b = JsonValue.Object().Add("key1", 1); - var obj_count1_key1_val2 = JsonValue.Object().Add("key1", 2); - var obj_count1_key2_val1 = JsonValue.Object().Add("key2", 1); - var obj_count2_key1_val1 = JsonValue.Object().Add("key1", 1).Add("key2", 2); + var objOtherValue = JsonValue.Object().Add("key1", 2); + var objOtherKey = JsonValue.Object().Add("key2", 1); + + var objOtherCount = JsonValue.Object().Add("key1", 1).Add("key2", 2); var number = JsonValue.Create(1); - Assert.Equal(obj_count1_key1_val1_a, obj_count1_key1_val1_b); - Assert.Equal(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key1_val1_b.GetHashCode()); - Assert.True(obj_count1_key1_val1_a.Equals((object)obj_count1_key1_val1_b)); + Assert.Equal(obj1a, obj1b); + Assert.Equal(obj1a.GetHashCode(), obj1b.GetHashCode()); + Assert.True(obj1a.Equals((object)obj1b)); - Assert.NotEqual(obj_count1_key1_val1_a, obj_count1_key1_val2); - Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key1_val2.GetHashCode()); - Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count1_key1_val2)); + Assert.NotEqual(obj1a, objOtherValue); + Assert.NotEqual(obj1a.GetHashCode(), objOtherValue.GetHashCode()); + Assert.False(obj1a.Equals((object)objOtherValue)); - Assert.NotEqual(obj_count1_key1_val1_a, obj_count1_key2_val1); - Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count1_key2_val1.GetHashCode()); - Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count1_key2_val1)); + Assert.NotEqual(obj1a, objOtherKey); + Assert.NotEqual(obj1a.GetHashCode(), objOtherKey.GetHashCode()); + Assert.False(obj1a.Equals((object)objOtherKey)); - Assert.NotEqual(obj_count1_key1_val1_a, obj_count2_key1_val1); - Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), obj_count2_key1_val1.GetHashCode()); - Assert.False(obj_count1_key1_val1_a.Equals((object)obj_count2_key1_val1)); + Assert.NotEqual(obj1a, objOtherCount); + Assert.NotEqual(obj1a.GetHashCode(), objOtherCount.GetHashCode()); + Assert.False(obj1a.Equals((object)objOtherCount)); - Assert.NotEqual(obj_count1_key1_val1_a, number); - Assert.NotEqual(obj_count1_key1_val1_a.GetHashCode(), number.GetHashCode()); - Assert.False(obj_count1_key1_val1_a.Equals((object)number)); + Assert.NotEqual(obj1a, number); + Assert.NotEqual(obj1a.GetHashCode(), number.GetHashCode()); + Assert.False(obj1a.Equals((object)number)); } [Fact] public void Should_make_correct_array_equal_comparisons() { - var array_count1_val1_a = JsonValue.Array(1); - var array_count1_val1_b = JsonValue.Array(1); + var array1a = JsonValue.Array(1); + var array1b = JsonValue.Array(1); - var array_count1_val2 = JsonValue.Array(2); - var array_count2_val1 = JsonValue.Array(1, 2); + var arrayOtherValue = JsonValue.Array(2); + var arrayOtherSize = JsonValue.Array(1, 2); var number = JsonValue.Create(1); - Assert.Equal(array_count1_val1_a, array_count1_val1_b); - Assert.Equal(array_count1_val1_a.GetHashCode(), array_count1_val1_b.GetHashCode()); - Assert.True(array_count1_val1_a.Equals((object)array_count1_val1_b)); + Assert.Equal(array1a, array1b); + Assert.Equal(array1a.GetHashCode(), array1b.GetHashCode()); + Assert.True(array1a.Equals((object)array1b)); - Assert.NotEqual(array_count1_val1_a, array_count1_val2); - Assert.NotEqual(array_count1_val1_a.GetHashCode(), array_count1_val2.GetHashCode()); - Assert.False(array_count1_val1_a.Equals((object)array_count1_val2)); + Assert.NotEqual(array1a, arrayOtherValue); + Assert.NotEqual(array1a.GetHashCode(), arrayOtherValue.GetHashCode()); + Assert.False(array1a.Equals((object)arrayOtherValue)); - Assert.NotEqual(array_count1_val1_a, array_count2_val1); - Assert.NotEqual(array_count1_val1_a.GetHashCode(), array_count2_val1.GetHashCode()); - Assert.False(array_count1_val1_a.Equals((object)array_count2_val1)); + Assert.NotEqual(array1a, arrayOtherSize); + Assert.NotEqual(array1a.GetHashCode(), arrayOtherSize.GetHashCode()); + Assert.False(array1a.Equals((object)arrayOtherSize)); - Assert.NotEqual(array_count1_val1_a, number); - Assert.NotEqual(array_count1_val1_a.GetHashCode(), number.GetHashCode()); - Assert.False(array_count1_val1_a.Equals((object)number)); + Assert.NotEqual(array1a, number); + Assert.NotEqual(array1a.GetHashCode(), number.GetHashCode()); + Assert.False(array1a.Equals((object)number)); } [Fact] diff --git a/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleEqualsTests.cs b/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleEqualsTests.cs deleted file mode 100644 index e426461a3..000000000 --- a/backend/tests/Squidex.Infrastructure.Tests/Reflection/SimpleEqualsTests.cs +++ /dev/null @@ -1,270 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using Squidex.Infrastructure.Collections; -using Squidex.Infrastructure.Reflection.Equality; -using Xunit; - -#pragma warning disable CA1822 // Mark members as static - -namespace Squidex.Infrastructure.Reflection -{ - public class SimpleEqualsTests - { - public class Class : ClassBase - { - public int Scalar { get; set; } - - public int ReadOnly - { - set => Debug.WriteLine(value); - } - } - - public class CustomEquals : IEquatable - { - private readonly int value; - - public CustomEquals(int value) - { - this.value = value; - } - - public override bool Equals(object? obj) - { - return Equals(obj as CustomEquals); - } - - public bool Equals(CustomEquals? other) - { - return other != null && other.value == value; - } - - public override int GetHashCode() - { - return value; - } - } - - public class ClassBase - { - [IgnoreEquals] - public int Ignored { get; set; } - - public List Complex { get; set; } - } - - public static IEnumerable RandomValues() - { - yield return new object[] - { - Guid.NewGuid(), - Guid.NewGuid() - }; - - yield return new object[] - { - 12, - 22 - }; - - yield return new object[] - { - DateTime.UtcNow, - DateTime.UtcNow.AddSeconds(2) - }; - - yield return new object[] - { - TimeSpan.FromMilliseconds(123), - TimeSpan.FromMilliseconds(55) - }; - - yield return new object[] - { - new Uri("/url1", UriKind.Relative), - new Uri("/url2", UriKind.Relative) - }; - } - - [Theory] - [MemberData(nameof(RandomValues))] - public void Should_compare_values(object lhs, object rhs) - { - Assert.True(SimpleEquals.IsEquals(lhs, lhs)); - Assert.True(DeepEqualityComparer.Default.Equals(lhs, lhs)); - - Assert.False(SimpleEquals.IsEquals(lhs, rhs)); - Assert.True(DeepEqualityComparer.Default.Equals(lhs, rhs)); - } - - [Fact] - public void Should_compare_equal_customs() - { - var customA_1 = new CustomEquals(1); - var customA_2 = new CustomEquals(1); - - Assert.True(SimpleEquals.IsEquals(customA_1, customA_1)); - Assert.True(SimpleEquals.IsEquals(customA_1, customA_2)); - } - - [Fact] - public void Should_compare_non_equal_customs() - { - var customA_1 = new CustomEquals(1); - var customB_1 = new CustomEquals(2); - - Assert.False(SimpleEquals.IsEquals(customA_1, customB_1)); - Assert.False(SimpleEquals.IsEquals(customA_1, null!)); - } - - [Fact] - public void Should_compare_equal_strings() - { - var stringA_1 = "a"; - var stringA_2 = new string(new[] { 'a' }); - - Assert.True(SimpleEquals.IsEquals(stringA_1, stringA_1)); - Assert.True(SimpleEquals.IsEquals(stringA_1, stringA_2)); - } - - [Fact] - public void Should_compare_non_equal_strings() - { - var stringA_1 = "a"; - var stringB_2 = new string(new[] { 'b' }); - - Assert.False(SimpleEquals.IsEquals(stringA_1, stringB_2)); - Assert.False(SimpleEquals.IsEquals(stringA_1, null!)); - } - - [Fact] - public void Should_compare_equal_lists() - { - var listA_1 = new List { "a" }; - var listA_2 = new List { "a" }; - - Assert.True(SimpleEquals.IsEquals(listA_1, listA_1)); - Assert.True(SimpleEquals.IsEquals(listA_1, listA_2)); - } - - [Fact] - public void Should_compare_non_equal_lists() - { - var listA_1 = new List { "a" }; - var listB_1 = new List { "b" }; - var listC_1 = new List { "b", "c" }; - - Assert.False(SimpleEquals.IsEquals(listA_1, listB_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, listC_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, null!)); - } - - [Fact] - public void Should_compare_equal_sets() - { - var setA_1 = new HashSet { "a", "b" }; - var setA_2 = new HashSet { "b", "a" }; - - Assert.True(SimpleEquals.IsEquals(setA_1, setA_1)); - Assert.True(SimpleEquals.IsEquals(setA_1, setA_2)); - } - - [Fact] - public void Should_compare_non_equal_sets() - { - var setA_1 = new HashSet { "a" }; - var setB_1 = new HashSet { "b" }; - - Assert.False(SimpleEquals.IsEquals(setA_1, setB_1)); - Assert.False(SimpleEquals.IsEquals(setA_1, null!)); - } - - [Fact] - public void Should_compare_equal_collections() - { - var listA_1 = ReadOnlyCollection.Create("a"); - var listA_2 = ReadOnlyCollection.Create("a"); - - Assert.True(SimpleEquals.IsEquals(listA_1, listA_1)); - Assert.True(SimpleEquals.IsEquals(listA_1, listA_2)); - } - - [Fact] - public void Should_compare_non_equal_collections() - { - var listA_1 = ReadOnlyCollection.Create("a"); - var listB_1 = ReadOnlyCollection.Create("b"); - var listC_1 = ReadOnlyCollection.Create("b"); - - Assert.False(SimpleEquals.IsEquals(listA_1, listB_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, listC_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, null!)); - } - - [Fact] - public void Should_compare_equal_dictionaries() - { - var dictionaryA_1 = new Dictionary { ["key1"] = 123 }; - var dictionaryA_2 = new Dictionary { ["key1"] = 123 }; - - Assert.True(SimpleEquals.IsEquals(dictionaryA_1, dictionaryA_1)); - Assert.True(SimpleEquals.IsEquals(dictionaryA_1, dictionaryA_2)); - } - - [Fact] - public void Should_compare_non_equal_dictionaries() - { - var listA_1 = new Dictionary { ["key1"] = 123 }; - var listB_1 = new Dictionary { ["key2"] = 123 }; - var listC_1 = new Dictionary { ["key1"] = 555 }; - var listD_1 = new Dictionary { ["key1"] = 123, ["key2"] = 55 }; - - Assert.False(SimpleEquals.IsEquals(listA_1, listB_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, listC_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, listD_1)); - Assert.False(SimpleEquals.IsEquals(listA_1, null!)); - } - - [Fact] - public void Should_compare_equal_objects() - { - var objectA_1 = new Class { Scalar = 1, Complex = new List { 1, 4 } }; - var objectA_2 = new Class { Scalar = 1, Complex = new List { 1, 4 } }; - - Assert.True(SimpleEquals.IsEquals(objectA_1, objectA_1)); - Assert.True(SimpleEquals.IsEquals(objectA_1, objectA_2)); - } - - [Fact] - public void Should_compare_equal_objects_with_ignored_properties() - { - var objectA_1 = new Class { Ignored = 1 }; - var objectA_2 = new Class { Ignored = 2 }; - - Assert.True(SimpleEquals.IsEquals(objectA_1, objectA_1)); - Assert.True(SimpleEquals.IsEquals(objectA_1, objectA_2)); - } - - [Fact] - public void Should_compare_non_equal_objects() - { - var objectA_1 = new Class { Scalar = 1, Complex = new List { 1, 4 } }; - var objectB_1 = new Class { Scalar = 1, Complex = new List { 1, 2 } }; - var objectC_1 = new Class { Scalar = 1, Complex = new List { 1, 2 } }; - var objectD_1 = new Class { Scalar = 2, Complex = null! }; - - Assert.False(SimpleEquals.IsEquals(objectA_1, objectB_1)); - Assert.False(SimpleEquals.IsEquals(objectA_1, objectC_1)); - Assert.False(SimpleEquals.IsEquals(objectA_1, objectD_1)); - Assert.False(SimpleEquals.IsEquals(objectA_1, null!)); - } - } -}