From 096609a82ff6bfb4f443a862b5289b758d80b351 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 23 Mar 2021 19:53:51 +0100 Subject: [PATCH] Migration to graphql 4. (#676) * Migration to graphql 4. * Updated packages. --- .../Squidex.Extensions.csproj | 2 +- .../Squidex.Domain.Apps.Core.Model.csproj | 2 +- .../Assets/AssetsFluidExtension.cs | 1 + .../Assets/AssetsJintExtension.cs | 2 +- .../Contents/GraphQL/Types/AllTypes.cs | 22 +-- .../GraphQL/Types/Assets/AssetActions.cs | 32 ++-- .../GraphQL/Types/Assets/AssetGraphType.cs | 6 +- .../Contents/GraphQL/Types/Builder.cs | 24 +-- .../GraphQL/Types/Contents/ContentActions.cs | 167 +++++++----------- .../GraphQL/Types/Contents/ContentFields.cs | 4 +- .../Types/Contents/DataInputGraphType.cs | 52 ++++++ .../Types/Contents/FieldInputVisitor.cs | 2 +- .../GraphQL/Types/Contents/FieldVisitor.cs | 84 +++++++-- .../Types/Contents/NestedInputGraphType.cs | 18 ++ .../Contents/GraphQL/Types/Extensions.cs | 101 +---------- .../GraphQL/Types/Primitives/Converters.cs | 79 --------- .../Types/Primitives/EntityResolvers.cs | 10 +- .../Types/Primitives/InstantConverter.cs | 32 ---- .../Types/Primitives/InstantGraphType.cs | 4 +- .../Types/Primitives/InstantValueNode.cs | 25 --- .../GraphQL/Types/Primitives/JsonConverter.cs | 72 -------- .../GraphQL/Types/Primitives/JsonGraphType.cs | 58 ++++-- ...{NoopGraphType.cs => JsonNoopGraphType.cs} | 20 +-- .../GraphQL/Types/Primitives/JsonValueNode.cs | 5 - .../Contents/GraphQL/Types/Resolvers.cs | 4 +- .../Contents/ReferencesFluidExtension.cs | 2 +- .../Squidex.Domain.Apps.Entities.csproj | 5 +- .../Squidex.Domain.Users.MongoDb.csproj | 2 +- .../Squidex.Domain.Users.csproj | 4 +- ...quidex.Infrastructure.GetEventStore.csproj | 2 +- .../Newtonsoft/NewtonsoftJsonSerializer.cs | 4 +- .../Json/Objects/JsonArray.cs | 5 + .../Squidex.Infrastructure.csproj | 4 +- .../Squidex.Web/GraphQL/GraphQLMiddleware.cs | 3 +- backend/src/Squidex.Web/Squidex.Web.csproj | 4 +- .../Squidex/Config/Domain/QueryServices.cs | 10 ++ .../Config/Domain/SerializationServices.cs | 1 + backend/src/Squidex/Squidex.csproj | 25 +-- .../Squidex.Domain.Apps.Core.Tests.csproj | 2 +- .../Assets/AssetPermanentDeleterTests.cs | 2 +- .../GraphQL/GraphQLIntrospectionTests.cs | 2 +- .../Contents/GraphQL/GraphQLMutationTests.cs | 18 +- .../Contents/GraphQL/GraphQLTestBase.cs | 4 + .../Squidex.Domain.Apps.Entities.Tests.csproj | 6 +- .../Squidex.Domain.Users.Tests.csproj | 2 +- .../Squidex.Infrastructure.Tests.csproj | 2 +- .../Squidex.Web.Tests.csproj | 6 +- 47 files changed, 379 insertions(+), 564 deletions(-) delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/Converters.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantConverter.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantValueNode.cs delete mode 100644 backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonConverter.cs rename backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/{NoopGraphType.cs => JsonNoopGraphType.cs} (76%) diff --git a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj index 168775f39..b83294232 100644 --- a/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj +++ b/backend/extensions/Squidex.Extensions/Squidex.Extensions.csproj @@ -10,7 +10,7 @@ - + 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 e0e359fe7..9becff324 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 @@ -10,7 +10,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsFluidExtension.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsFluidExtension.cs index 6ecd04860..9dfe59913 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsFluidExtension.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsFluidExtension.cs @@ -13,6 +13,7 @@ using Fluid; using Fluid.Ast; using Fluid.Tags; using GraphQL.Utilities; +using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Templates; using Squidex.Domain.Apps.Core.ValidateContent; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsJintExtension.cs b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsJintExtension.cs index 4df5836ef..bbe359ac5 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsJintExtension.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Assets/AssetsJintExtension.cs @@ -10,9 +10,9 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; -using GraphQL.Utilities; using Jint.Native; using Jint.Runtime; +using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Infrastructure; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs index 9070b63b8..0588d82f8 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs @@ -14,20 +14,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { public static class AllTypes { - public static readonly Type None = typeof(NoopGraphType); - public static readonly IGraphType Int = new IntGraphType(); - public static readonly IGraphType DomainId = new StringGraphType(); - public static readonly IGraphType Long = new LongGraphType(); - public static readonly IGraphType Guid = new GuidGraphType(); - - public static readonly IGraphType Date = new InstantGraphType(); - public static readonly IGraphType Json = new JsonGraphType(); + public static readonly IGraphType JsonNoop = new JsonNoopGraphType(); + public static readonly IGraphType Float = new FloatGraphType(); public static readonly IGraphType String = new StringGraphType(); @@ -36,18 +30,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public static readonly IGraphType Boolean = new BooleanGraphType(); + public static readonly IGraphType DateTime = new InstantGraphType(); + public static readonly IGraphType AssetType = new EnumerationGraphType(); public static readonly IGraphType NonNullInt = new NonNullGraphType(Int); - public static readonly IGraphType NonNullDomainId = new NonNullGraphType(DomainId); - public static readonly IGraphType NonNullLong = new NonNullGraphType(Long); - public static readonly IGraphType NonNullGuid = new NonNullGraphType(Guid); - - public static readonly IGraphType NonNullDate = new NonNullGraphType(Date); - public static readonly IGraphType NonNullFloat = new NonNullGraphType(Float); public static readonly IGraphType NonNullString = new NonNullGraphType(String); @@ -56,8 +46,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public static readonly IGraphType NonNullBoolean = new NonNullGraphType(Boolean); - public static readonly IGraphType NonNullAssetType = new NonNullGraphType(AssetType); + public static readonly IGraphType NonNullDateTime = new NonNullGraphType(DateTime); - public static readonly IGraphType NoopJson = new NoopGraphType(Json); + public static readonly IGraphType NonNullAssetType = new NonNullGraphType(AssetType); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs index c24fc00f4..32d725087 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetActions.cs @@ -19,12 +19,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "path", Description = "The path to the json value", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; @@ -32,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets { if (fieldContext.Arguments.TryGetValue("path", out var path)) { - source.Metadata.TryGetByPath(path as string, out var result); + source.Metadata.TryGetByPath(path.Value as string, out var result); return result; } @@ -45,12 +44,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "id", Description = "The id of the asset (usually GUID).", - DefaultValue = null, - ResolvedType = AllTypes.NonNullDomainId + DefaultValue = null } }; @@ -66,33 +64,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "top", Description = "Optional number of assets to take.", - DefaultValue = null, - ResolvedType = AllTypes.Int + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "skip", Description = "Optional number of assets to skip.", - DefaultValue = 0, - ResolvedType = AllTypes.Int + DefaultValue = 0 }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "filter", Description = "Optional OData filter.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "orderby", Description = "Optional OData order definition.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs index acac0a759..a324ba1bf 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Assets/AssetGraphType.cs @@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets AddField(new FieldType { Name = "created", - ResolvedType = AllTypes.NonNullDate, + ResolvedType = AllTypes.NonNullDateTime, Resolver = EntityResolvers.Created, Description = "The date and time when the asset has been created." }); @@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets AddField(new FieldType { Name = "lastModified", - ResolvedType = AllTypes.NonNullDate, + ResolvedType = AllTypes.NonNullDateTime, Resolver = EntityResolvers.LastModified, Description = "The date and time when the asset has been modified last." }); @@ -205,7 +205,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Assets { Name = "metadata", Arguments = AssetActions.Metadata.Arguments, - ResolvedType = AllTypes.NoopJson, + ResolvedType = AllTypes.JsonNoop, Resolver = AssetActions.Metadata.Resolver, Description = "The asset metadata." }); diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs index 24a696d8e..79e158a3d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs @@ -5,9 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using GraphQL; using GraphQL.Resolvers; @@ -16,10 +14,8 @@ using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents; -using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Json.Objects; using GraphQLSchema = GraphQL.Types.Schema; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types @@ -28,31 +24,21 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { private readonly Dictionary contentTypes = new Dictionary(ReferenceEqualityComparer.Instance); private readonly Dictionary contentResultTypes = new Dictionary(ReferenceEqualityComparer.Instance); - private readonly SharedTypes sharedTypes; private readonly FieldVisitor fieldVisitor; private readonly FieldInputVisitor fieldInputVisitor; private readonly PartitionResolver partitionResolver; - public SharedTypes SharedTypes - { - get => sharedTypes; - } + public SharedTypes SharedTypes { get; } static Builder() { - ValueConverter.Register(x => x.Value); - ValueConverter.Register(x => x.Value); - ValueConverter.Register(x => x.Value); - ValueConverter.Register(x => DateTimeOffset.Parse(x.Value, CultureInfo.InvariantCulture)); - ValueConverter.Register(DomainId.Create); - ValueConverter.Register(x => new Status(x)); } public Builder(IAppEntity app, SharedTypes sharedTypes) { - this.sharedTypes = sharedTypes; + SharedTypes = sharedTypes; partitionResolver = app.PartitionResolver(); @@ -77,10 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types Query = new AppQueriesGraphType(this, schemaInfos) }; - newSchema.RegisterValueConverter(JsonConverter.Instance); - newSchema.RegisterValueConverter(InstantConverter.Instance); - - newSchema.RegisterType(sharedTypes.ContentInterface); + newSchema.RegisterType(SharedTypes.ContentInterface); if (schemas.Any()) { @@ -98,7 +81,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types } newSchema.Initialize(); - newSchema.CleanupMetadata(); return newSchema; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs index 4b119f218..6c11293f9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs @@ -27,18 +27,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "path", Description = "The path to the json value", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; public static readonly ValueResolver Resolver = (value, fieldContext, context) => { - if (fieldContext.Arguments.TryGetValue("path", out var p) && p is string path) + if (fieldContext.Arguments.TryGetValue("path", out var v) && v.Value is string path) { value.TryGetByPath(path, out var result); @@ -51,12 +50,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public static readonly QueryArguments JsonPath = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "path", Description = "The path to the json value", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; @@ -64,19 +62,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "id", Description = "The id of the content (usually GUID).", - DefaultValue = null, - ResolvedType = AllTypes.NonNullDomainId + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "version", Description = "The optional version of the content to retrieve an older instance (not cached).", - DefaultValue = null, - ResolvedType = AllTypes.Int + DefaultValue = null } }; @@ -101,40 +97,35 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "top", Description = "Optional number of contents to take.", - DefaultValue = null, - ResolvedType = AllTypes.Int + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "skip", Description = "Optional number of contents to skip.", - DefaultValue = 0, - ResolvedType = AllTypes.Int + DefaultValue = 0 }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "filter", Description = "Optional OData filter.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "orderby", Description = "Optional OData order definition.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "search", Description = "Optional OData full text search.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; @@ -181,41 +172,37 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { return new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = "The data for the content.", - DefaultValue = null, - ResolvedType = new NonNullGraphType(inputType) + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Boolean) { Name = "publish", Description = "Set to true to autopublish content on create.", - DefaultValue = false, - ResolvedType = AllTypes.Boolean + DefaultValue = false }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "status", Description = "The initial status.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "id", Description = "The optional custom content id.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null } }; } public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsCreate, c => { - var contentData = GetContentData(c); var contentId = c.GetArgument("id"); + var contentData = c.GetArgument("data"); var contentStatus = c.GetArgument("status"); var command = new CreateContent { Data = contentData }; @@ -244,48 +231,43 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { return new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "id", Description = "The id of the content (usually GUID).", - DefaultValue = null, - ResolvedType = AllTypes.NonNullDomainId + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = "The data for the content.", - DefaultValue = null, - ResolvedType = new NonNullGraphType(inputType) + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Boolean) { Name = "publish", Description = "Set to true to autopublish content on create.", - DefaultValue = false, - ResolvedType = AllTypes.Boolean + DefaultValue = false }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "status", Description = "The initial status.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "expectedVersion", Description = "The expected version", - DefaultValue = EtagVersion.Any, - ResolvedType = AllTypes.Int + DefaultValue = EtagVersion.Any } }; } public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsUpsert, c => { - var contentData = GetContentData(c); var contentId = c.GetArgument("id"); + var contentData = c.GetArgument("data"); var contentStatus = c.GetArgument("status"); var id = DomainId.Create(contentId); @@ -311,26 +293,23 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { return new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "id", Description = "The optional custom content id.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = "The data for the content.", - DefaultValue = null, - ResolvedType = new NonNullGraphType(inputType) + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "expectedVersion", Description = "The expected version", - DefaultValue = EtagVersion.Any, - ResolvedType = AllTypes.Int + DefaultValue = EtagVersion.Any } }; } @@ -338,7 +317,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsUpdateOwn, c => { var contentId = c.GetArgument("id"); - var contentData = GetContentData(c); + var contentData = c.GetArgument("data"); return new UpdateContent { ContentId = contentId, Data = contentData }; }); @@ -350,26 +329,23 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { return new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.String) { Name = "id", Description = "The optional custom content id.", - DefaultValue = null, - ResolvedType = AllTypes.String + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(new NonNullGraphType(inputType)) { Name = "data", Description = "The data for the content.", - DefaultValue = null, - ResolvedType = new NonNullGraphType(inputType) + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "expectedVersion", Description = "The expected version", - DefaultValue = EtagVersion.Any, - ResolvedType = AllTypes.Int + DefaultValue = EtagVersion.Any } }; } @@ -377,7 +353,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public static readonly IFieldResolver Resolver = ResolveAsync(Permissions.AppContentsUpdateOwn, c => { var contentId = c.GetArgument("id"); - var contentData = GetContentData(c); + var contentData = c.GetArgument("data"); return new PatchContent { ContentId = contentId, Data = contentData }; }); @@ -387,33 +363,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "id", Description = "The id of the content (usually GUID).", - DefaultValue = null, - ResolvedType = AllTypes.NonNullDomainId + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "status", Description = "The new status", - DefaultValue = null, - ResolvedType = AllTypes.NonNullString + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.DateTime) { Name = "dueTime", Description = "When to change the status", - DefaultValue = null, - ResolvedType = AllTypes.Date + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "expectedVersion", Description = "The expected version", - DefaultValue = EtagVersion.Any, - ResolvedType = AllTypes.Int + DefaultValue = EtagVersion.Any } }; @@ -431,19 +403,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { public static readonly QueryArguments Arguments = new QueryArguments { - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.NonNullString) { Name = "id", Description = "The id of the content (usually GUID).", - DefaultValue = null, - ResolvedType = AllTypes.NonNullDomainId + DefaultValue = null }, - new QueryArgument(AllTypes.None) + new QueryArgument(AllTypes.Int) { Name = "expectedVersion", Description = "The expected version", - DefaultValue = EtagVersion.Any, - ResolvedType = AllTypes.Int + DefaultValue = EtagVersion.Any } }; @@ -455,13 +425,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents }); } - private static ContentData GetContentData(IResolveFieldContext c) - { - var source = c.GetArgument>("data"); - - return source.ToContentData((IComplexGraphType)c.FieldDefinition.Arguments.Find("data").Flatten()); - } - private static IFieldResolver ResolveAsync(string permissionId, Func action) { return Resolvers.Async(async (source, fieldContext, context) => diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs index c33c4fd96..2af627aef 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs @@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public static readonly FieldType Created = new FieldType { Name = "created", - ResolvedType = AllTypes.NonNullDate, + ResolvedType = AllTypes.NonNullDateTime, Resolver = EntityResolvers.Created, Description = "The date and time when the content has been created." }; @@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public static readonly FieldType LastModified = new FieldType { Name = "lastModified", - ResolvedType = AllTypes.NonNullDate, + ResolvedType = AllTypes.NonNullDateTime, Resolver = EntityResolvers.LastModified, Description = "The date and time when the content has been modified last." }; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs index 6e3886901..e508437bb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/DataInputGraphType.cs @@ -5,9 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; +using System.Linq; using GraphQL.Types; +using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; using Squidex.Domain.Apps.Entities.Schemas; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { @@ -54,5 +59,52 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents Description = $"The structure of the {schemaInfo.DisplayName} data input type."; } + + public override object ParseDictionary(IDictionary value) + { + var result = new ContentData(); + + static ContentFieldData ToFieldData(IDictionary source, IComplexGraphType type) + { + var result = new ContentFieldData(); + + foreach (var field in type.Fields) + { + if (source.TryGetValue(field.Name, out var value)) + { + if (value is IEnumerable list && field.ResolvedType.Flatten() is IComplexGraphType nestedType) + { + var array = new JsonArray(list.Count()); + + foreach (var item in list) + { + if (item is JsonObject nested) + { + array.Add(nested); + } + } + + result[field.SourceName()] = array; + } + else + { + result[field.SourceName()] = JsonGraphType.ParseJson(value); + } + } + } + + return result; + } + + foreach (var field in Fields) + { + if (field.ResolvedType is IComplexGraphType complexType && value.TryGetValue(field.Name, out var fieldValue) && fieldValue is IDictionary nested) + { + result[field.SourceName()] = ToFieldData(nested, complexType); + } + } + + return result; + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs index 922a8b56c..0187a7f56 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldInputVisitor.cs @@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public IGraphType? Visit(IField field, FieldInfo args) { - return AllTypes.Date; + return AllTypes.DateTime; } public IGraphType? Visit(IField field, FieldInfo args) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs index 4d412f37b..9a2e1f9e9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using System.Collections.Generic; using System.Linq; using GraphQL; @@ -19,8 +20,63 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents internal sealed class FieldVisitor : IFieldVisitor<(IGraphType?, IFieldResolver?, QueryArguments?), FieldInfo> { - private static readonly IFieldResolver Noop = CreateValueResolver((value, fieldContext, contex) => value); - private static readonly IFieldResolver Json = CreateValueResolver(ContentActions.Json.Resolver); + private static readonly IFieldResolver JsonNoop = CreateValueResolver((value, fieldContext, contex) => value); + private static readonly IFieldResolver JsonPath = CreateValueResolver(ContentActions.Json.Resolver); + + private static readonly IFieldResolver JsonBoolean = CreateValueResolver((value, fieldContext, contex) => + { + switch (value) + { + case JsonBoolean b: + return b.Value; + default: + throw new NotSupportedException(); + } + }); + + private static readonly IFieldResolver JsonDateTime = CreateValueResolver((value, fieldContext, contex) => + { + switch (value) + { + case JsonString n: + return n.Value; + default: + throw new NotSupportedException(); + } + }); + + private static readonly IFieldResolver JsonNumber = CreateValueResolver((value, fieldContext, contex) => + { + switch (value) + { + case JsonNumber n: + return n.Value; + default: + throw new NotSupportedException(); + } + }); + + private static readonly IFieldResolver JsonString = CreateValueResolver((value, fieldContext, contex) => + { + switch (value) + { + case JsonString s: + return s.Value; + default: + throw new NotSupportedException(); + } + }); + + private static readonly IFieldResolver JsonStrings = CreateValueResolver((value, fieldContext, contex) => + { + switch (value) + { + case JsonArray a: + return a.Select(x => x.ToString()).ToList(); + default: + throw new NotSupportedException(); + } + }); private static readonly IFieldResolver Assets = CreateValueResolver((value, _, context) => { @@ -46,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents new NonNullGraphType( new NestedGraphType(builder, args))); - return (schemaFieldType, Noop, null); + return (schemaFieldType, JsonNoop, null); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) @@ -56,42 +112,42 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Boolean, Noop, null); + return (AllTypes.Boolean, JsonBoolean, null); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Date, Noop, null); + return (AllTypes.DateTime, JsonDateTime, null); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Json, Json, ContentActions.Json.Arguments); + return (AllTypes.Json, JsonPath, ContentActions.Json.Arguments); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Json, Noop, null); + return (AllTypes.Json, JsonPath, ContentActions.Json.Arguments); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Float, Noop, null); + return (AllTypes.Float, JsonNumber, null); } - public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) + public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return ResolveReferences(field, args); + return (AllTypes.String, JsonString, null); } - public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) + public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.String, Noop, null); + return (AllTypes.Strings, JsonStrings, null); } - public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) + public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) { - return (AllTypes.Strings, Noop, null); + return ResolveReferences(field, args); } public (IGraphType?, IFieldResolver?, QueryArguments?) Visit(IField field, FieldInfo args) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs index 1abd5d330..d286a3896 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/NestedInputGraphType.cs @@ -5,7 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using GraphQL.Types; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents { @@ -33,5 +36,20 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents Description = $"The structure of the {fieldInfo.DisplayName} nested schema."; } + + public override object ParseDictionary(IDictionary value) + { + var result = JsonValue.Object(); + + foreach (var field in Fields) + { + if (value.TryGetValue(field.Name, out var fieldValue)) + { + result[field.SourceName()] = JsonGraphType.ParseJson(fieldValue); + } + } + + return result; + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs index 8f760e983..dc365b027 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Extensions.cs @@ -5,16 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; -using System.Linq; using GraphQL; using GraphQL.Types; -using GraphQL.Utilities; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.ObjectPool; -using GraphQLSchema = GraphQL.Types.Schema; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { @@ -27,11 +23,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types { sb.Append('?'); - foreach (var argument in context.Arguments) + foreach (var (key, value) in context.Arguments) { - var value = argument.Value?.ToString(); + var formatted = value.Value?.ToString(); - if (!string.IsNullOrWhiteSpace(value)) + if (!string.IsNullOrWhiteSpace(formatted)) { if (sb.Length > 1) { @@ -39,9 +35,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types } sb.Append('$'); - sb.Append(argument.Key); + sb.Append(key); sb.Append('='); - sb.Append(value); + sb.Append(formatted); } } @@ -90,20 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types private static FieldType WithMetadata(this FieldType field, string key, object value) { - if (field is MetadataProvider metadataProvider) - { - if (metadataProvider.Metadata is Dictionary dict) - { - dict[key] = value; - } - else - { - metadataProvider.Metadata = new Dictionary - { - [key] = value - }; - } - } + field.Metadata[key] = value; return field; } @@ -122,77 +105,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types return type; } - - public static void CleanupMetadata(this GraphQLSchema schema) - { - var targets = new HashSet(ReferenceEqualityComparer.Instance); - - foreach (var type in schema.AllTypes) - { - FindTargets(type, targets); - } - - foreach (var target in targets.OfType()) - { - var metadata = target.Metadata; - - if (metadata != null && metadata.Count == 0) - { - target.Metadata = null; - } - } - } - - private static void FindTargets(IGraphType type, HashSet targets) - { - if (type == null) - { - return; - } - - if (targets.Add(type)) - { - if (type is IComplexGraphType complexType) - { - foreach (var field in complexType.Fields) - { - targets.Add(field); - - FindTargets(field.ResolvedType, targets); - - if (field.Arguments != null) - { - foreach (var argument in field.Arguments) - { - targets.Add(argument); - - FindTargets(argument.ResolvedType, targets); - } - } - } - - if (type is IObjectGraphType { ResolvedInterfaces: { } } objectGraphType) - { - foreach (var @interface in objectGraphType.ResolvedInterfaces) - { - FindTargets(@interface, targets); - } - } - - if (type is IAbstractGraphType { PossibleTypes: { } } abstractGraphType) - { - foreach (var possibleType in abstractGraphType.PossibleTypes) - { - FindTargets(possibleType, targets); - } - } - } - - if (type is IProvideResolvedType provideType) - { - FindTargets(provideType.ResolvedType, targets); - } - } - } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/Converters.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/Converters.cs deleted file mode 100644 index 72eb23671..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/Converters.cs +++ /dev/null @@ -1,79 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using GraphQL.Types; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Infrastructure.Json.Objects; - -namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives -{ - internal static class Converters - { - public static ContentData ToContentData(this IDictionary source, IComplexGraphType type) - { - var result = new ContentData(); - - foreach (var field in type.Fields) - { - if (source.TryGetValue(field.Name, out var t) && t is IDictionary nested && field.ResolvedType is IComplexGraphType complexType) - { - result[field.SourceName()] = nested.ToFieldData(complexType); - } - } - - return result; - } - - public static ContentFieldData ToFieldData(this IDictionary source, IComplexGraphType type) - { - var result = new ContentFieldData(); - - foreach (var field in type.Fields) - { - if (source.TryGetValue(field.Name, out var value)) - { - if (value is List list && field.ResolvedType.Flatten() is IComplexGraphType nestedType) - { - var arr = new JsonArray(); - - foreach (var item in list) - { - if (item is IDictionary nested) - { - arr.Add(nested.ToNestedData(nestedType)); - } - } - - result[field.SourceName()] = arr; - } - else - { - result[field.SourceName()] = JsonConverter.ParseJson(value); - } - } - } - - return result; - } - - public static IJsonValue ToNestedData(this IDictionary source, IComplexGraphType type) - { - var result = JsonValue.Object(); - - foreach (var field in type.Fields) - { - if (source.TryGetValue(field.Name, out var value)) - { - result[field.SourceName()] = JsonConverter.ParseJson(value); - } - } - - return result; - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs index c9da4267d..c93998acb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/EntityResolvers.cs @@ -12,11 +12,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { internal static class EntityResolvers { - public static readonly IFieldResolver Id = Resolve(x => x.Id); - public static readonly IFieldResolver Created = Resolve(x => x.Created); - public static readonly IFieldResolver CreatedBy = Resolve(x => x.CreatedBy); - public static readonly IFieldResolver LastModified = Resolve(x => x.LastModified); - public static readonly IFieldResolver LastModifiedBy = Resolve(x => x.LastModifiedBy); + public static readonly IFieldResolver Id = Resolve(x => x.Id.ToString()); + public static readonly IFieldResolver Created = Resolve(x => x.Created.ToDateTimeUtc()); + public static readonly IFieldResolver CreatedBy = Resolve(x => x.CreatedBy.ToString()); + public static readonly IFieldResolver LastModified = Resolve(x => x.LastModified.ToDateTimeUtc()); + public static readonly IFieldResolver LastModifiedBy = Resolve(x => x.LastModifiedBy.ToString()); public static readonly IFieldResolver Version = Resolve(x => x.Version); private static IFieldResolver Resolve(Func resolver) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantConverter.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantConverter.cs deleted file mode 100644 index dba150ecb..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using GraphQL.Language.AST; -using GraphQL.Types; -using NodaTime; - -namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives -{ - internal sealed class InstantConverter : IAstFromValueConverter - { - public static readonly InstantConverter Instance = new InstantConverter(); - - private InstantConverter() - { - } - - public IValue Convert(object value, IGraphType type) - { - return new InstantValueNode((Instant)value); - } - - public bool Matches(object value, IGraphType type) - { - return type is InstantGraphType; - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs index c3b37b61d..127933ef3 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantGraphType.cs @@ -11,7 +11,7 @@ using NodaTime.Text; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { - internal sealed class InstantGraphType : DateTimeGraphType + public sealed class InstantGraphType : DateTimeGraphType { public override object Serialize(object value) { @@ -27,8 +27,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { switch (value) { - case InstantValueNode timeValue: - return timeValue.Value; case StringValue stringValue: return ParseValue(stringValue.Value); default: diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantValueNode.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantValueNode.cs deleted file mode 100644 index d9819e9a0..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/InstantValueNode.cs +++ /dev/null @@ -1,25 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using GraphQL.Language.AST; -using NodaTime; - -namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives -{ - internal sealed class InstantValueNode : ValueNode - { - public InstantValueNode(Instant value) - { - Value = value; - } - - protected override bool Equals(ValueNode node) - { - return Equals(Value, node.Value); - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonConverter.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonConverter.cs deleted file mode 100644 index 9db63ae8d..000000000 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonConverter.cs +++ /dev/null @@ -1,72 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Collections.Generic; -using GraphQL.Language.AST; -using GraphQL.Types; -using Squidex.Infrastructure.Json.Objects; - -namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives -{ - internal sealed class JsonConverter : IAstFromValueConverter - { - public static readonly JsonConverter Instance = new JsonConverter(); - - private JsonConverter() - { - } - - public IValue Convert(object value, IGraphType type) - { - return new JsonValueNode(ParseJson(value)); - } - - public bool Matches(object value, IGraphType type) - { - return type is JsonGraphType; - } - - public static IJsonValue ParseJson(object value) - { - switch (value) - { - case ListValue listValue: - return ParseJson(listValue.Value); - - case ObjectValue objectValue: - return ParseJson(objectValue.Value); - - case Dictionary dictionary: - { - var json = JsonValue.Object(); - - foreach (var (key, inner) in dictionary) - { - json[key] = ParseJson(inner); - } - - return json; - } - - case List list: - { - var array = JsonValue.Array(); - - foreach (var item in list) - { - array.Add(ParseJson(item)); - } - - return array; - } - - default: - return JsonValue.Create(value); - } - } - } -} diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs index 215dc7f23..74cf0ec0f 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonGraphType.cs @@ -5,20 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using GraphQL.Language.AST; -using GraphQL.Types; +using Squidex.Infrastructure.Json.Objects; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { - internal sealed class JsonGraphType : ScalarGraphType + public sealed class JsonGraphType : JsonNoopGraphType { - public JsonGraphType() - { - Name = "JsonScalar"; - - Description = "Unstructured Json object"; - } - public override object Serialize(object value) { return value; @@ -26,7 +20,46 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives public override object ParseValue(object value) { - return JsonConverter.ParseJson(value); + return ParseJson(value); + } + + public static IJsonValue ParseJson(object value) + { + switch (value) + { + case ListValue listValue: + return ParseJson(listValue.Value); + + case ObjectValue objectValue: + return ParseJson(objectValue.Value); + + case IReadOnlyDictionary dictionary: + { + var json = JsonValue.Object(); + + foreach (var (key, inner) in dictionary) + { + json[key] = ParseJson(inner); + } + + return json; + } + + case IEnumerable list: + { + var array = JsonValue.Array(); + + foreach (var item in list) + { + array.Add(ParseJson(item)); + } + + return array; + } + + default: + return JsonValue.Create(value); + } } public override object ParseLiteral(IValue value) @@ -38,5 +71,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives return value; } + + public override IValue ToAST(object value) + { + return new JsonValueNode(ParseJson(value)); + } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/NoopGraphType.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonNoopGraphType.cs similarity index 76% rename from backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/NoopGraphType.cs rename to backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonNoopGraphType.cs index e64a94e9b..4c4d7a78d 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/NoopGraphType.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonNoopGraphType.cs @@ -10,22 +10,18 @@ using GraphQL.Types; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { - internal sealed class NoopGraphType : ScalarGraphType + public class JsonNoopGraphType : ScalarGraphType { - public NoopGraphType(string name) + public JsonNoopGraphType() { - Name = name; - } + Name = "JsonScalar"; - public NoopGraphType(IGraphType type) - : this(type.Name) - { - Description = type.Description; + Description = "Unstructured Json object"; } - public override object Serialize(object value) + public override object ParseLiteral(IValue value) { - return value; + return value.Value; } public override object ParseValue(object value) @@ -33,9 +29,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives return value; } - public override object ParseLiteral(IValue value) + public override object Serialize(object value) { - return value.Value; + return value; } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonValueNode.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonValueNode.cs index 3f566204d..751c1a7a9 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonValueNode.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Primitives/JsonValueNode.cs @@ -16,10 +16,5 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives { Value = value; } - - protected override bool Equals(ValueNode node) - { - return Equals(Value, node.Value); - } } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Resolvers.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Resolvers.cs index 77532d2e7..b66e19308 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Resolvers.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Resolvers.cs @@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types executionContext.Log.LogWarning(ex, w => w .WriteProperty("action", "resolveField") .WriteProperty("status", "failed") - .WriteProperty("field", context.FieldName)); + .WriteProperty("field", context.FieldDefinition.Name)); throw; } @@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types executionContext.Log.LogWarning(ex, w => w .WriteProperty("action", "resolveField") .WriteProperty("status", "failed") - .WriteProperty("field", context.FieldName)); + .WriteProperty("field", context.FieldDefinition.Name)); throw; } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs index adbb034ab..67a427a6c 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/ReferencesFluidExtension.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using Fluid; using Fluid.Ast; using Fluid.Tags; -using GraphQL.Utilities; +using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Domain.Apps.Core.Templates; using Squidex.Domain.Apps.Entities.Apps; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 5aac0d60c..6abc4d527 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/backend/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -20,11 +20,12 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all diff --git a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index a9b678ec4..981894fab 100644 --- a/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/backend/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -18,7 +18,7 @@ - + diff --git a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj index cbf328a5d..537b082d2 100644 --- a/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj +++ b/backend/src/Squidex.Domain.Users/Squidex.Domain.Users.csproj @@ -16,8 +16,8 @@ - - + + diff --git a/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj b/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj index ac8e3e794..62e4b6ba4 100644 --- a/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj +++ b/backend/src/Squidex.Infrastructure.GetEventStore/Squidex.Infrastructure.GetEventStore.csproj @@ -10,7 +10,7 @@ True - + diff --git a/backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs b/backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs index ee8fc5c91..165ceca56 100644 --- a/backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs +++ b/backend/src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs @@ -28,7 +28,9 @@ namespace Squidex.Infrastructure.Json.Newtonsoft public string Serialize(T value, bool intented) { - return JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings); + var formatting = intented ? Formatting.Indented : Formatting.None; + + return JsonConvert.SerializeObject(value, formatting, settings); } public void Serialize(T value, Stream stream, bool leaveOpen = false) diff --git a/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs b/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs index 32bbfffe6..e030c3aa4 100644 --- a/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs +++ b/backend/src/Squidex.Infrastructure/Json/Objects/JsonArray.cs @@ -25,6 +25,11 @@ namespace Squidex.Infrastructure.Json.Objects { } + public JsonArray(int capacity) + : base(new List(capacity)) + { + } + public JsonArray(JsonArray source) : base(source.ToList()) { diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index e13f1573a..08d096914 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -10,10 +10,10 @@ - + - + all diff --git a/backend/src/Squidex.Web/GraphQL/GraphQLMiddleware.cs b/backend/src/Squidex.Web/GraphQL/GraphQLMiddleware.cs index e437303c2..fcc6f5270 100644 --- a/backend/src/Squidex.Web/GraphQL/GraphQLMiddleware.cs +++ b/backend/src/Squidex.Web/GraphQL/GraphQLMiddleware.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using GraphQL.Server.Transports.AspNetCore; -using GraphQL.Server.Transports.AspNetCore.Common; using Microsoft.AspNetCore.Http; namespace Squidex.Web.GraphQL @@ -17,7 +16,7 @@ namespace Squidex.Web.GraphQL private static readonly RequestDelegate Noop = _ => Task.CompletedTask; public GraphQLMiddleware(IGraphQLRequestDeserializer deserializer) - : base(Noop, default, deserializer) + : base(Noop, deserializer) { } } diff --git a/backend/src/Squidex.Web/Squidex.Web.csproj b/backend/src/Squidex.Web/Squidex.Web.csproj index a56e05544..78f27cf8f 100644 --- a/backend/src/Squidex.Web/Squidex.Web.csproj +++ b/backend/src/Squidex.Web/Squidex.Web.csproj @@ -12,11 +12,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/backend/src/Squidex/Config/Domain/QueryServices.cs b/backend/src/Squidex/Config/Domain/QueryServices.cs index 90fe106e6..c96898ce9 100644 --- a/backend/src/Squidex/Config/Domain/QueryServices.cs +++ b/backend/src/Squidex/Config/Domain/QueryServices.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Entities.Contents.GraphQL; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; using Squidex.Web.Services; namespace Squidex.Config.Domain @@ -29,6 +30,15 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .AsSelf(); + services.AddSingletonAs() + .AsSelf(); + + services.AddSingletonAs() + .AsSelf(); + + services.AddSingletonAs() + .AsSelf(); + services.AddSingletonAs() .As(); } diff --git a/backend/src/Squidex/Config/Domain/SerializationServices.cs b/backend/src/Squidex/Config/Domain/SerializationServices.cs index 6f4753661..168a8bc2e 100644 --- a/backend/src/Squidex/Config/Domain/SerializationServices.cs +++ b/backend/src/Squidex/Config/Domain/SerializationServices.cs @@ -134,6 +134,7 @@ namespace Squidex.Config.Domain builder.Services.AddSingleton(c => { var settings = ConfigureJson(new JsonSerializerSettings(), TypeNameHandling.None); + var serializer = new NewtonsoftJsonSerializer(settings); return new DefaultDocumentWriter(serializer); diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index 275b2c97f..7f2d8568b 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -33,17 +33,18 @@ - - - - - + + + + + + - - - - - + + + + + @@ -55,7 +56,7 @@ - + @@ -67,7 +68,7 @@ - + diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index 6c22a221d..e7e4f2842 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs index 358bef438..f8cfedfcb 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetPermanentDeleterTests.cs @@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Assets A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 0)) .Throws(new AssetNotFoundException("fileName")); - await sut.On(Envelope.Create(@event).SetEventStreamNumber(2));; + await sut.On(Envelope.Create(@event).SetEventStreamNumber(2)); A.CallTo(() => assetFiletore.DeleteAsync(appId.Id, @event.AssetId, 1)) .MustHaveHappened(); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs index 142f43c1e..aa91bccbd 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs @@ -97,7 +97,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var result = await ExecuteAsync(new ExecutionOptions { Query = query, OperationName = "IntrospectionQuery" }); - var json = serializer.Serialize(result.Data, true); + var json = serializer.Serialize(result, true); Assert.NotEmpty(json); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs index 382da3780..6395021dc 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs @@ -68,7 +68,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "createMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); @@ -209,7 +210,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "updateMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); @@ -316,7 +318,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "upsertMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); @@ -425,7 +428,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "patchMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); @@ -532,7 +536,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "changeMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); @@ -677,7 +682,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL "deleteMySchemaContent" } } - } + }, + data = (object?)null }; AssertResult(expected, result); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs index 9e8ff4ab3..bc0465007 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs @@ -23,6 +23,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; using Squidex.Domain.Apps.Entities.Contents.TestData; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.TestHelpers; @@ -198,6 +199,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL .AddSingleton(contentQuery) .AddSingleton(dataLoaderContext) .AddSingleton(dataLoaderListener) + .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index 035bb8b61..38ebc0548 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -17,10 +17,10 @@ - + - - + + diff --git a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj index eefb68e22..c8a4fa277 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj +++ b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index 82d23094c..971c1267e 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj index 24a37ab66..37667f789 100644 --- a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj +++ b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj @@ -11,9 +11,9 @@ - - - + + +