diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldTypeProvider.cs similarity index 84% rename from backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs rename to backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldTypeProvider.cs index eaab4e27d..00c6559f3 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldTypeProvider.cs @@ -9,14 +9,14 @@ using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Schemas { - public class FieldRegistry : ITypeProvider + public class FieldTypeProvider : ITypeProvider { private const string Suffix = "Properties"; private const string SuffixOld = "FieldProperties"; public void Map(TypeNameRegistry typeNameRegistry) { - var types = typeof(FieldRegistry).Assembly.GetTypes().Where(x => typeof(FieldProperties).IsAssignableFrom(x) && !x.IsAbstract); + var types = typeof(FieldTypeProvider).Assembly.GetTypes().Where(x => typeof(FieldProperties).IsAssignableFrom(x) && !x.IsAbstract); var addedTypes = new HashSet(); @@ -25,7 +25,6 @@ namespace Squidex.Domain.Apps.Core.Schemas if (addedTypes.Add(type)) { typeNameRegistry.Map(type, type.TypeName(false, Suffix)); - typeNameRegistry.MapObsolete(type, type.TypeName(false, SuffixOld)); } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleRegistry.cs b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs similarity index 98% rename from backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleRegistry.cs rename to backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs index 24f18849f..f47327d07 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleRegistry.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleTypeProvider.cs @@ -16,7 +16,7 @@ using Squidex.Text; namespace Squidex.Domain.Apps.Core.HandleRules { - public sealed class RuleRegistry : ITypeProvider + public sealed class RuleTypeProvider : ITypeProvider { private const string ActionSuffix = "Action"; private const string ActionSuffixV2 = "ActionV2"; @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules get => actionTypes; } - public RuleRegistry(IEnumerable? registrations = null) + public RuleTypeProvider(IEnumerable? registrations = null) { if (registrations != null) { diff --git a/backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs b/backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs index 374bbb17e..a9987ef07 100644 --- a/backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs +++ b/backend/src/Squidex.Web/GraphQL/DynamicUserContextBuilder.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using GraphQL; using GraphQL.Server.Transports.AspNetCore; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -19,6 +20,8 @@ namespace Squidex.Web.GraphQL public Task> BuildUserContext(HttpContext httpContext) { + var x = httpContext.RequestServices.GetRequiredService(); + var executionContext = (GraphQLExecutionContext)factory(httpContext.RequestServices, new object[] { httpContext.Context() }); return Task.FromResult>(executionContext); diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs index 296bd6735..cd983bb21 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs @@ -17,9 +17,9 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models { public sealed class RuleActionProcessor : IDocumentProcessor { - private readonly RuleRegistry ruleRegistry; + private readonly RuleTypeProvider ruleRegistry; - public RuleActionProcessor(RuleRegistry ruleRegistry) + public RuleActionProcessor(RuleTypeProvider ruleRegistry) { this.ruleRegistry = ruleRegistry; } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index f33bf602c..84d936d12 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -34,14 +34,14 @@ namespace Squidex.Areas.Api.Controllers.Rules private readonly IRuleEventRepository ruleEventsRepository; private readonly IRuleQueryService ruleQuery; private readonly IRuleRunnerService ruleRunnerService; - private readonly RuleRegistry ruleRegistry; + private readonly RuleTypeProvider ruleRegistry; public RulesController(ICommandBus commandBus, IAppProvider appProvider, IRuleEventRepository ruleEventsRepository, IRuleQueryService ruleQuery, IRuleRunnerService ruleRunnerService, - RuleRegistry ruleRegistry, + RuleTypeProvider ruleRegistry, EventJsonSchemaGenerator eventJsonSchemaGenerator) : base(commandBus) { diff --git a/backend/src/Squidex/Config/Domain/RuleServices.cs b/backend/src/Squidex/Config/Domain/RuleServices.cs index a78e6371f..ba3c76864 100644 --- a/backend/src/Squidex/Config/Domain/RuleServices.cs +++ b/backend/src/Squidex/Config/Domain/RuleServices.cs @@ -83,7 +83,7 @@ namespace Squidex.Config.Domain services.AddSingletonAs() .AsSelf(); - services.AddSingletonAs() + services.AddSingletonAs() .As().AsSelf(); services.AddSingletonAs() @@ -104,7 +104,7 @@ namespace Squidex.Config.Domain services.AddSingletonAs>() .AsSelf(); - services.AddInitializer("Serializer (Rules)", registry => + services.AddInitializer("Serializer (Rules)", registry => { RuleActionConverter.Mapping = registry.Actions.ToDictionary(x => x.Key, x => x.Value.Type); }, -1); diff --git a/backend/src/Squidex/Config/Domain/SerializationServices.cs b/backend/src/Squidex/Config/Domain/SerializationServices.cs index acee6625f..9141205b5 100644 --- a/backend/src/Squidex/Config/Domain/SerializationServices.cs +++ b/backend/src/Squidex/Config/Domain/SerializationServices.cs @@ -7,9 +7,9 @@ using System.Security.Claims; using GraphQL; -using GraphQL.DI; using GraphQL.Execution; using GraphQL.NewtonsoftJson; +using GraphQL.Server; using Migrations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; @@ -30,6 +30,7 @@ using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries.Json; using Squidex.Infrastructure.Reflection; +using IGraphQLBuilder = GraphQL.DI.IGraphQLBuilder; namespace Squidex.Config.Domain { @@ -44,7 +45,6 @@ namespace Squidex.Config.Domain new CompareOperatorJsonConverter(), new ContentFieldDataConverter(), new EnvelopeHeadersConverter(), - new ExecutionResultJsonConverter(new ErrorInfoProvider()), new JsonValueConverter(), new StringEnumConverter(), new SurrogateConverter(), @@ -82,7 +82,7 @@ namespace Squidex.Config.Domain services.AddSingletonAs>() .As(); - services.AddSingletonAs() + services.AddSingletonAs() .As(); services.AddSingletonAs() @@ -122,5 +122,23 @@ namespace Squidex.Config.Domain return builder; } + + public static IGraphQLBuilder AddSquidexJson(this IGraphQLBuilder builder) + { + builder.AddDocumentWriter(c => + { + var errorInfoProvider = c.GetRequiredService(); + + return new DocumentWriter(options => + { + options.ContractResolver = new ExecutionResultContractResolver(new ErrorInfoProvider()); + + options.Converters.Add(new JsonValueConverter()); + options.Converters.Add(new WriteonlyGeoJsonConverter()); + }); + }); + + return builder; + } } } diff --git a/backend/src/Squidex/Config/Web/WebServices.cs b/backend/src/Squidex/Config/Web/WebServices.cs index 17f89daa5..ab62e64bc 100644 --- a/backend/src/Squidex/Config/Web/WebServices.cs +++ b/backend/src/Squidex/Config/Web/WebServices.cs @@ -94,6 +94,7 @@ namespace Squidex.Config.Web }) .AddSchema() .AddSystemTextJson() + .AddSquidexJson() // Use Newtonsoft.JSON for custom converters. .AddDataLoader(); services.AddSingletonAs() 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 4e4a9960e..11f5d9287 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 @@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules { public class RuleElementRegistryTests { - private readonly RuleRegistry sut = new RuleRegistry(); + private readonly RuleTypeProvider sut = new RuleTypeProvider(); private abstract class MyRuleActionHandler : RuleActionHandler { 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 d2ae9dbbd..ae527face 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/TestHelpers/TestUtils.cs @@ -41,8 +41,8 @@ namespace Squidex.Domain.Apps.Core.TestHelpers { var typeNameRegistry = new TypeNameRegistry() - .Map(new FieldRegistry()) - .Map(new RuleRegistry()) + .Map(new FieldTypeProvider()) + .Map(new RuleTypeProvider()) .MapUnmapped(typeof(TestUtils).Assembly); var serializerSettings = new JsonSerializerSettings diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs index da39f438d..963a75850 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs +++ b/backend/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json.Linq; using Squidex.ClientLibrary; using Squidex.ClientLibrary.Management; using TestSuite.Fixtures; +using TestSuite.Model; using Xunit; #pragma warning disable SA1300 // Element should begin with upper-case letter @@ -63,6 +64,42 @@ namespace TestSuite.ApiTests public string Name { get; set; } } + [Fact] + public async Task Should_query_json() + { + // STEP 1: Create a content with JSON. + var content_0 = await _.Contents.CreateAsync(new TestEntityData + { + Json = JToken.FromObject(new + { + value = 1, + obj = new + { + value = 2 + } + }) + }, ContentCreateOptions.AsPublish); + + + // STEP 2: Query this content. + var query = new + { + query = @" + { + findMyWritesContent(id: """") { + flatData { + json + } + } + }".Replace("", content_0.Id, StringComparison.Ordinal) + }; + + var result1 = await _.Contents.GraphQlAsync(query); + + Assert.Equal(1, result1["findMyWritesContent"]["flatData"]["json"]["value"].Value()); + Assert.Equal(2, result1["findMyWritesContent"]["flatData"]["json"]["obj"]["value"].Value()); + } + [Fact] public async Task Should_create_and_query_with_graphql() {