diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs index 56caec708..7fd817f78 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs @@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Core.Schemas; 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.Utils; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using GraphQLSchema = GraphQL.Types.Schema; @@ -53,6 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL schemasById = schemas.ToDictionary(x => x.Id); graphQLSchema = BuildSchema(this); + graphQLSchema.RegisterValueConverter(JTokenConverter.Instance); InitializeContentTypes(); } @@ -178,13 +180,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { Guard.NotNull(context, nameof(context)); + var inputs = InputConverter.ToInputs(query.Variables); + var result = await new DocumentExecuter().ExecuteAsync(options => { - options.Inputs = query.Variables?.ToInputs() ?? new Inputs(); - options.Query = query.Query; options.OperationName = query.OperationName; - options.Schema = graphQLSchema; options.UserContext = context; + options.Schema = graphQLSchema; + options.Inputs = inputs; + options.Query = query.Query; }).ConfigureAwait(false); return (result.Data, result.Errors?.Select(x => (object)new { x.Message, x.Locations }).ToArray()); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs index 00be09720..3d0def8e0 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs @@ -30,27 +30,21 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public static readonly IGraphType StatusType = new EnumerationGraphType(); - public static readonly IGraphType NonNullInt = new NonNullGraphType(new IntGraphType()); + public static readonly IGraphType NonNullInt = new NonNullGraphType(Int); - public static readonly IGraphType NonNullGuid = new NonNullGraphType(new GuidGraphType()); + public static readonly IGraphType NonNullGuid = new NonNullGraphType(Guid); - public static readonly IGraphType NonNullDate = new NonNullGraphType(new DateGraphType()); + public static readonly IGraphType NonNullDate = new NonNullGraphType(Date); - public static readonly IGraphType NonNullFloat = new NonNullGraphType(new FloatGraphType()); + public static readonly IGraphType NonNullFloat = new NonNullGraphType(Float); - public static readonly IGraphType NonNullString = new NonNullGraphType(new StringGraphType()); + public static readonly IGraphType NonNullString = new NonNullGraphType(String); - public static readonly IGraphType NonNullBoolean = new NonNullGraphType(new BooleanGraphType()); + public static readonly IGraphType NonNullBoolean = new NonNullGraphType(Boolean); - public static readonly IGraphType NonNullStatusType = new NonNullGraphType(new EnumerationGraphType()); + public static readonly IGraphType NonNullStatusType = new NonNullGraphType(StatusType); - public static readonly IGraphType ListOfNonNullGuid = new ListGraphType(new NonNullGraphType(new GuidGraphType())); - - public static readonly IGraphType ListOfNonNullString = new ListGraphType(new NonNullGraphType(new StringGraphType())); - - public static readonly IGraphType NoopInt = new NoopGraphType("Int"); - - public static readonly IGraphType NoopGuid = new NoopGraphType("Guid"); + public static readonly IGraphType NoopArray = new NoopGraphType("Array"); public static readonly IGraphType NoopDate = new NoopGraphType("Date"); @@ -66,6 +60,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public static readonly IGraphType NoopGeolocation = new NoopGraphType("Geolocation"); + public static readonly IGraphType NoopReferences = new NoopGraphType("References"); + public static readonly IGraphType CommandVersion = new CommandVersionGraphType(); public static readonly IGraphType GeolocationInput = new GeolocationInputGraphType(); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs index 3ccbc04b2..a47356642 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/InputFieldVisitor.cs @@ -20,27 +20,27 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public IGraphType Visit(IArrayField field) { - return AllTypes.NoopJson; + return AllTypes.NoopArray; } public IGraphType Visit(IField field) { - return AllTypes.ListOfNonNullGuid; + return AllTypes.NoopReferences; } public IGraphType Visit(IField field) { - return AllTypes.Boolean; + return AllTypes.NoopBoolean; } public IGraphType Visit(IField field) { - return AllTypes.Date; + return AllTypes.NoopDate; } public IGraphType Visit(IField field) { - return AllTypes.GeolocationInput; + return AllTypes.NoopGeolocation; } public IGraphType Visit(IField field) @@ -50,22 +50,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types public IGraphType Visit(IField field) { - return AllTypes.Float; + return AllTypes.NoopFloat; } public IGraphType Visit(IField field) { - return AllTypes.ListOfNonNullGuid; + return AllTypes.NoopReferences; } public IGraphType Visit(IField field) { - return AllTypes.String; + return AllTypes.NoopString; } public IGraphType Visit(IField field) { - return AllTypes.ListOfNonNullString; + return AllTypes.NoopTags; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InputConverter.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InputConverter.cs new file mode 100644 index 000000000..7e6d71ebd --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InputConverter.cs @@ -0,0 +1,73 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using GraphQL; +using Newtonsoft.Json.Linq; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils +{ + public static class InputConverter + { + public static Inputs ToInputs(JObject input) + { + var result = new Inputs(); + + if (input != null) + { + foreach (var kvp in input) + { + result.Add(kvp.Key, GetValue(kvp.Value, 1)); + } + } + + return result; + } + + private static object GetValue(object value, int level) + { + if (level == 3) + { + return value; + } + + switch (value) + { + case JObject jObject: + { + var result = new Dictionary(); + + foreach (var kvp in jObject) + { + result.Add(kvp.Key, GetValue(kvp.Value, level + 1)); + } + + return result; + } + + case JArray jArray: + { + var result = new List(); + + foreach (var item in jArray) + { + result.Add(GetValue(item, level + 1)); + } + + return result; + } + + case JValue jValue: + { + return jValue.Value; + } + } + + return value; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenConverter.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenConverter.cs new file mode 100644 index 000000000..8987e0d97 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenConverter.cs @@ -0,0 +1,32 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using GraphQL.Language.AST; +using GraphQL.Types; +using Newtonsoft.Json.Linq; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils +{ + public sealed class JTokenConverter : IAstFromValueConverter + { + public static readonly JTokenConverter Instance = new JTokenConverter(); + + private JTokenConverter() + { + } + + public IValue Convert(object value, IGraphType type) + { + return new JTokenValue(value as JToken); + } + + public bool Matches(object value, IGraphType type) + { + return value is JToken; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenValue.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenValue.cs new file mode 100644 index 000000000..2e230f324 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JTokenValue.cs @@ -0,0 +1,25 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using GraphQL.Language.AST; +using Newtonsoft.Json.Linq; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils +{ + public sealed class JTokenValue : ValueNode + { + public JTokenValue(JToken value) + { + Value = value; + } + + protected override bool Equals(ValueNode node) + { + return node.Value.Equals(Value); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/NoopGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/NoopGraphType.cs index 5be62090f..53863ca70 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/NoopGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/NoopGraphType.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using GraphQL.Language.AST; using GraphQL.Types; @@ -30,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils public override object ParseLiteral(IValue value) { - throw new NotSupportedException(); + return value.Value; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index bb30542e7..e7eb3a28d 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; diff --git a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 82878f303..9dfc0d4a6 100644 --- a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj index 72e45373d..bd03eadfe 100644 --- a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj +++ b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj @@ -11,7 +11,6 @@ - diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs index e0866ee5e..91660566c 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs @@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL public GraphQLMutationTests() { - content = CreateContent(contentId, Guid.Empty, Guid.Empty, null, true); + content = CreateContent(contentId, Guid.NewGuid(), Guid.NewGuid(), null); A.CallTo(() => commandBus.PublishAsync(A.Ignored)) .Returns(commandContext); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs index 56e9fe31a..e22d33f62 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs @@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL sut = new CachingGraphQLService(cache, appProvider, assetRepository, commandBus, contentQuery, new FakeUrlGenerator()); } - protected static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null, bool noJson = false) + protected static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null) { var now = DateTime.UtcNow.ToInstant(); @@ -126,6 +126,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL .AddField("my-geolocation", new ContentFieldData() .AddValue("iv", JToken.FromObject(new { latitude = 10, longitude = 20 }))) + .AddField("my-json", + new ContentFieldData() + .AddValue("iv", JToken.FromObject(new { value = 1 }))) .AddField("my-array", new ContentFieldData() .AddValue("iv", new JArray( @@ -136,13 +139,6 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL new JProperty("nested-boolean", false), new JProperty("nested-number", 2))))); - if (!noJson) - { - data.AddField("my-json", - new ContentFieldData() - .AddValue("iv", JToken.FromObject(new { value = 1 }))); - } - var content = new ContentEntity { Id = id, diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index 0cdbf976c..6325ad89d 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -21,6 +21,7 @@ +