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 99034c52b..e69653ee1 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 @@ -48,6 +48,8 @@ internal sealed class Builder public IInterfaceGraphType ComponentInterface { get; } = new ComponentInterfaceGraphType(); + public FieldMap FieldMap { get; private set; } + public Builder(IAppEntity app, GraphQLOptions options) { partitionResolver = app.PartitionResolver(); @@ -64,7 +66,7 @@ internal sealed class Builder allSchemas.AddRange(SchemaInfo.Build(schemas, typeNames).Where(x => x.Fields.Count > 0)); // Only published normal schemas (not components are used for entities). - var normalSchemas = allSchemas.Where(x => x.Schema.SchemaDef.IsPublished && x.Schema.SchemaDef.Type != SchemaType.Component).ToList(); + var normalSchemas = allSchemas.Where(IsNormalSchema).ToList(); foreach (var schemaInfo in normalSchemas) { @@ -74,7 +76,7 @@ internal sealed class Builder contentResultTypes[schemaInfo] = new ContentResultGraphType(contentType, schemaInfo); } - foreach (var schemaInfo in normalSchemas) + foreach (var schemaInfo in allSchemas) { var componentType = new ComponentGraphType(schemaInfo); @@ -92,6 +94,8 @@ internal sealed class Builder newSchema.Directives.Register(SharedTypes.CacheDirective); newSchema.Directives.Register(SharedTypes.OptimizeFieldQueriesDirective); + FieldMap = new FieldMap(allSchemas); + if (normalSchemas.Any()) { var mutations = new ApplicationMutations(this, normalSchemas); @@ -132,6 +136,11 @@ internal sealed class Builder return newSchema; } + private static bool IsNormalSchema(SchemaInfo schema) + { + return schema.Schema.SchemaDef.IsPublished && schema.Schema.SchemaDef.Type != SchemaType.Component; + } + public FieldGraphSchema GetGraphType(FieldInfo fieldInfo) { return fieldInfo.Field.Accept(fieldVisitor, fieldInfo); 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 8c77001a3..6db4255ea 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 @@ -6,16 +6,15 @@ // ========================================================================== 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; internal sealed class DataInputGraphType : InputObjectGraphType { + private readonly FieldMap fieldMap; + public DataInputGraphType(Builder builder, SchemaInfo schemaInfo) { // The name is used for equal comparison. Therefore it is important to treat it as readonly. @@ -62,52 +61,12 @@ internal sealed class DataInputGraphType : InputObjectGraphType } Description = $"The structure of the {schemaInfo.DisplayName} data input type."; + + fieldMap = builder.FieldMap; } 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?.InnerType() is IComplexGraphType nestedType) - { - var array = new JsonArray(list.Count()); - - foreach (var item in list) - { - if (item is JsonValue { Value: 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; + return fieldMap.MapData(this, value); } } 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 2e3b0b6db..d9a42ddf0 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 @@ -6,13 +6,13 @@ // ========================================================================== 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; internal sealed class NestedInputGraphType : InputObjectGraphType { + private readonly FieldMap fieldMap; + public NestedInputGraphType(Builder builder, FieldInfo fieldInfo) { // The name is used for equal comparison. Therefore it is important to treat it as readonly. @@ -38,20 +38,12 @@ internal sealed class NestedInputGraphType : InputObjectGraphType } Description = $"The structure of the {fieldInfo.DisplayName} nested schema."; + + fieldMap = builder.FieldMap; } 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 new JsonValue(result); + return fieldMap.MapNested(this, value); } } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/FieldMap.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/FieldMap.cs new file mode 100644 index 000000000..b649aecd2 --- /dev/null +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/FieldMap.cs @@ -0,0 +1,135 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using GraphQL.Types; +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents; +using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Primitives; +using Squidex.Infrastructure.Json.Objects; + +namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types; + +internal sealed class FieldMap +{ + private readonly Dictionary> schemas = new Dictionary>(); + + public FieldMap(IEnumerable source) + { + foreach (var schema in source) + { + var fieldMap = schema.Fields.ToDictionary(x => x.FieldName, x => x.Field.Name); + + schemas[schema.Schema.Id.ToString()] = fieldMap; + schemas[schema.Schema.SchemaDef.Name] = fieldMap; + } + } + + public ContentData MapData(IInputObjectGraphType inputType, IDictionary source) + { + var result = new ContentData(); + + foreach (var field in inputType.Fields) + { + if (field.ResolvedType is IComplexGraphType complexType && source.TryGetValue(field.Name, out var value) && value is IDictionary nested) + { + result[field.SourceName()] = MapField(nested, complexType); + } + } + + return result; + } + + public JsonValue MapNested(IInputObjectGraphType inputType, IDictionary source) + { + var result = new JsonObject(source.Count); + + foreach (var field in inputType.Fields) + { + if (source.TryGetValue(field.Name, out var value)) + { + result[field.SourceName()] = MapValue(JsonGraphType.ParseJson(value)); + } + } + + return result; + } + + private ContentFieldData MapField(IDictionary source, IComplexGraphType type) + { + var result = new ContentFieldData(); + + foreach (var field in type.Fields) + { + if (source.TryGetValue(field.Name, out var value)) + { + result[field.SourceName()] = MapValue(JsonGraphType.ParseJson(value)); + } + } + + return result; + } + + private JsonValue MapValue(JsonValue source) + { + switch (source.Value) + { + case JsonArray arr: + return MapArray(arr); + case JsonObject obj: + return MapObject(obj); + default: + return source; + } + } + + private JsonValue MapArray(JsonArray source) + { + var result = new JsonArray(source.Count); + + foreach (var value in source) + { + result.Add(MapValue(value)); + } + + return result; + } + + private JsonValue MapObject(JsonObject source) + { + Dictionary? fieldMap = null; + + if (source.TryGetValue(Component.Discriminator, out var d1) && d1.Value is string discriminator) + { + schemas.TryGetValue(discriminator, out fieldMap); + } + else if (source.TryGetValue(Component.Descriptor, out var d2) && d2.Value is string descriptor) + { + schemas.TryGetValue(descriptor, out fieldMap); + } + + if (fieldMap == null) + { + return source; + } + + var result = new JsonObject(source.Count); + + foreach (var (key, value) in source) + { + var sourceName = key; + + if (fieldMap != null && fieldMap.TryGetValue(key, out var name)) + { + sourceName = name; + } + + result[sourceName] = MapValue(value); + } + + return result; + } +} 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 d2d65061f..e66cd4c67 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 @@ -23,8 +23,10 @@ public sealed class JsonGraphType : JsonNoopGraphType return ParseJson(value); } - public static JsonValue ParseJson(object? input) + public static JsonValue ParseJson(object? input, Func?>? keyMap = null) { + keyMap ??= x => null; + switch (input) { case GraphQLBooleanValue booleanValue: @@ -44,7 +46,7 @@ public sealed class JsonGraphType : JsonNoopGraphType case GraphQLListValue listValue: { - var json = new JsonArray(); + var json = new JsonArray(listValue.Values?.Count ?? 0); if (listValue.Values != null) { @@ -59,13 +61,22 @@ public sealed class JsonGraphType : JsonNoopGraphType case GraphQLObjectValue objectValue: { - var json = JsonValue.Object(); + var json = new JsonObject(objectValue.Fields?.Count ?? 0); if (objectValue.Fields != null) { + var map = keyMap(objectValue); + foreach (var field in objectValue.Fields) { - json[field.Name.ToString()] = ParseJson(field.Value); + var sourceField = field.Name.ToString(); + + if (map?.TryGetValue(sourceField, out var temp) == true) + { + sourceField = temp; + } + + json[sourceField] = ParseJson(field.Value); } } @@ -74,7 +85,7 @@ public sealed class JsonGraphType : JsonNoopGraphType case IEnumerable list: { - var json = new JsonArray(); + var json = new JsonArray(list.Count()); foreach (var item in list) { @@ -86,11 +97,23 @@ public sealed class JsonGraphType : JsonNoopGraphType case IDictionary obj: { - var json = JsonValue.Object(); + var json = new JsonObject(obj.Count); - foreach (var (key, value) in obj) + if (obj.Count > 0) { - json[key] = ParseJson(value); + var map = keyMap(obj); + + foreach (var (key, value) in obj) + { + var sourceField = key; + + if (map?.TryGetValue(sourceField, out var temp) == true) + { + sourceField = temp; + } + + json[sourceField] = ParseJson(value); + } } return json; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedExtensions.cs index 5c5ae090b..6d8c1e0cb 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/SharedExtensions.cs @@ -10,9 +10,7 @@ using GraphQL.Types; using GraphQL.Utilities; using GraphQLParser; using GraphQLParser.AST; -using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents; using Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Directives; -using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.ObjectPool; @@ -93,7 +91,7 @@ public static class SharedExtensions return typed.SourceName; } - throw new InvalidOperationException("Invalid field type"); + return field.Name; } internal static DomainId SchemaId(this FieldType field) @@ -126,6 +124,25 @@ public static class SharedExtensions return type; } + public static bool TryGetValue(this GraphQLObjectValue source, string fieldName, out object value) + { + value = null!; + + if (source.Fields != null) + { + foreach (var field in source.Fields) + { + if (field.Name == fieldName) + { + value = field.Value; + return true; + } + } + } + + return false; + } + public static TimeSpan CacheDuration(this IResolveFieldContext context) { return CacheDirective.CacheDuration(context); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs index 0c7aa5a13..968142a05 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs @@ -35,7 +35,7 @@ public class AppProviderExtensionsTests : GivenContext public async Task Should_resolve_self_as_component() { var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -60,7 +60,7 @@ public class AppProviderExtensionsTests : GivenContext .Returns(component); var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -82,7 +82,7 @@ public class AppProviderExtensionsTests : GivenContext .Returns(component); var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddComponents(1, "1", Partitioning.Invariant, new ComponentsFieldProperties { @@ -104,7 +104,7 @@ public class AppProviderExtensionsTests : GivenContext .Returns(component); var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddArray(1, "1", Partitioning.Invariant, a => a .AddComponent(2, "2", new ComponentFieldProperties @@ -122,7 +122,7 @@ public class AppProviderExtensionsTests : GivenContext public async Task Should_resolve_self_referencing_component() { var component = - Mocks.Schema(AppId, componentId1, + Mocks.Schema(AppId, componentId1.Id, new Schema(componentId1.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -133,7 +133,7 @@ public class AppProviderExtensionsTests : GivenContext .Returns(component); var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -150,7 +150,7 @@ public class AppProviderExtensionsTests : GivenContext public async Task Should_resolve_component_of_component() { var component1 = - Mocks.Schema(AppId, componentId1, + Mocks.Schema(AppId, componentId1.Id, new Schema(componentId1.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -158,7 +158,7 @@ public class AppProviderExtensionsTests : GivenContext })); var component2 = - Mocks.Schema(AppId, componentId2, + Mocks.Schema(AppId, componentId2.Id, new Schema(componentId2.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { @@ -172,7 +172,7 @@ public class AppProviderExtensionsTests : GivenContext .Returns(component2); var schema = - Mocks.Schema(AppId, schemaId, + Mocks.Schema(AppId, schemaId.Id, new Schema(schemaId.Name) .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties { diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs index 11e402258..be664c1a2 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs @@ -192,6 +192,6 @@ public class DefaultContentWorkflowTests ValidateOnPublish = validateOnPublish }); - return Mocks.Schema(NamedId.Of(DomainId.NewGuid(), "my-app"), NamedId.Of(DomainId.NewGuid(), schema.Name), schema); + return Mocks.Schema(NamedId.Of(DomainId.NewGuid(), "my-app"), DomainId.NewGuid(), schema); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs index 18fc31046..e0e11f285 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/DomainObject/Guards/GuardContentTests.cs @@ -35,19 +35,19 @@ public class GuardContentTests : GivenContext, IClassFixture commandBus.PublishAsync(A.Ignored, A._)) .Returns(commandContext); @@ -93,7 +94,7 @@ public class GraphQLMutationTests : GraphQLTestBase }, Variables = new { - data = TestContent.Input(content, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id), + data = TestContent.Input(content, TestSchemas.Reference1.Id, TestSchemas.Reference2.Id), }, Permission = PermissionIds.AppContentsCreate }); @@ -111,7 +112,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.CallTo(() => commandBus.PublishAsync( A.That.Matches(x => x.ExpectedVersion == EtagVersion.Any && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published && x.Data.Equals(content.Data)), A._)) @@ -138,7 +139,7 @@ public class GraphQLMutationTests : GraphQLTestBase }, Variables = new { - data = TestContent.Input(content, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id) + data = TestContent.Input(content, TestSchemas.Reference1.Id, TestSchemas.Reference2.Id) }, Permission = PermissionIds.AppContentsCreate }); @@ -157,7 +158,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.That.Matches(x => x.ExpectedVersion == EtagVersion.Any && x.ContentId == contentId && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published && x.Data.Equals(content.Data)), A._)) @@ -234,7 +235,7 @@ public class GraphQLMutationTests : GraphQLTestBase }, Variables = new { - data = TestContent.Input(content, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id) + data = TestContent.Input(content, TestSchemas.Reference1.Id, TestSchemas.Reference2.Id) }, Permission = PermissionIds.AppContentsUpdateOwn }); @@ -253,7 +254,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.That.Matches(x => x.ContentId == contentId && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Data.Equals(content.Data)), A._)) .MustHaveHappened(); @@ -325,7 +326,7 @@ public class GraphQLMutationTests : GraphQLTestBase }, Variables = new { - data = TestContent.Input(content, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id) + data = TestContent.Input(content, TestSchemas.Reference1.Id, TestSchemas.Reference2.Id) }, Permission = PermissionIds.AppContentsUpsert }); @@ -344,7 +345,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.That.Matches(x => x.ContentId == contentId && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published && x.Data.Equals(content.Data)), A._)) @@ -421,7 +422,7 @@ public class GraphQLMutationTests : GraphQLTestBase }, Variables = new { - data = TestContent.Input(content, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id) + data = TestContent.Input(content, TestSchemas.Reference1.Id, TestSchemas.Reference2.Id) }, Permission = PermissionIds.AppContentsUpdateOwn }); @@ -440,7 +441,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.That.Matches(x => x.ContentId == contentId && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Data.Equals(content.Data)), A._)) .MustHaveHappened(); @@ -534,7 +535,7 @@ public class GraphQLMutationTests : GraphQLTestBase x.ContentId == contentId && x.DueTime == dueTime && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published), A._)) .MustHaveHappened(); @@ -576,7 +577,7 @@ public class GraphQLMutationTests : GraphQLTestBase x.ContentId == contentId && x.DueTime == null && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published), A._)) .MustHaveHappened(); @@ -618,7 +619,7 @@ public class GraphQLMutationTests : GraphQLTestBase x.ContentId == contentId && x.DueTime == null && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId) && + x.SchemaId.Equals(TestSchemas.Default.NamedId()) && x.Status == Status.Published), A._)) .MustHaveHappened(); @@ -708,7 +709,7 @@ public class GraphQLMutationTests : GraphQLTestBase A.That.Matches(x => x.ContentId == contentId && x.ExpectedVersion == 10 && - x.SchemaId.Equals(TestSchemas.DefaultId)), + x.SchemaId.Equals(TestSchemas.Default.NamedId())), A._)) .MustHaveHappened(); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index 9fa80dc1a..79666087b 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -7,6 +7,7 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Assets; +using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; @@ -502,7 +503,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_return_null_if_single_content_from_another_schema() { var contentId = DomainId.NewGuid(); - var content = TestContent.CreateRef(TestSchemas.Ref1Id, contentId, "ref1-field", "ref1"); + var content = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentId, "reference1-field", "reference1"); A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.That.HasIdsWithoutTotal(contentId), @@ -612,7 +613,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_embedded_contents_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "schemaRef1Field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -641,9 +642,9 @@ public class GraphQLQueriesTests : GraphQLTestBase ... on Content { id } - ... on MyRefSchema1 { + ... on MyReference1 { data { - schemaRef1Field { + reference1Field { iv } } @@ -681,9 +682,9 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentRefId, data = new { - schemaRef1Field = new + reference1Field = new { - iv = "ref1" + iv = "reference1" } } } @@ -702,7 +703,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_referenced_contents_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "schemaRef1Field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -728,7 +729,7 @@ public class GraphQLQueriesTests : GraphQLTestBase iv { id data { - schemaRef1Field { + reference1Field { iv } } @@ -761,9 +762,9 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentRefId, data = new { - schemaRef1Field = new + reference1Field = new { - iv = "ref1" + iv = "reference1" } } } @@ -781,7 +782,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_referenced_contents_from_flat_data_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "schemaRef1Field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -843,7 +844,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_cache_referenced_contents_from_flat_data_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "schemaRef1Field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -914,7 +915,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_referencing_contents_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "ref1-field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -933,7 +934,7 @@ public class GraphQLQueriesTests : GraphQLTestBase { Query = @" query { - findMyRefSchema1Content(id: '{contentId}') { + findMyReference1Content(id: '{contentId}') { id referencingMySchemaContents(top: 30, skip: 5) { id @@ -955,7 +956,7 @@ public class GraphQLQueriesTests : GraphQLTestBase { data = new { - findMyRefSchema1Content = new + findMyReference1Content = new { id = contentRefId, referencingMySchemaContents = new[] @@ -983,7 +984,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_referencing_contents_with_total_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "ref1-field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -1002,7 +1003,7 @@ public class GraphQLQueriesTests : GraphQLTestBase { Query = @" query { - findMyRefSchema1Content(id: '{contentId}') { + findMyReference1Content(id: '{contentId}') { id referencingMySchemaContentsWithTotal(top: 30, skip: 5) { total @@ -1027,7 +1028,7 @@ public class GraphQLQueriesTests : GraphQLTestBase { data = new { - findMyRefSchema1Content = new + findMyReference1Content = new { id = contentRefId, referencingMySchemaContentsWithTotal = new @@ -1059,7 +1060,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_references_contents_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "ref1-field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -1080,7 +1081,7 @@ public class GraphQLQueriesTests : GraphQLTestBase query { findMySchemaContent(id: '{contentId}') { id - referencesMyRefSchema1Contents(top: 30, skip: 5) { + referencesMyReference1Contents(top: 30, skip: 5) { id } } @@ -1098,7 +1099,7 @@ public class GraphQLQueriesTests : GraphQLTestBase findMySchemaContent = new { id = contentId, - referencesMyRefSchema1Contents = new[] + referencesMyReference1Contents = new[] { new { @@ -1116,7 +1117,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_references_contents_with_total_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "ref1-field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -1137,7 +1138,7 @@ public class GraphQLQueriesTests : GraphQLTestBase query { findMySchemaContent(id: '{contentId}') { id - referencesMyRefSchema1ContentsWithTotal(top: 30, skip: 5) { + referencesMyReference1ContentsWithTotal(top: 30, skip: 5) { total items { id @@ -1158,7 +1159,7 @@ public class GraphQLQueriesTests : GraphQLTestBase findMySchemaContent = new { id = contentId, - referencesMyRefSchema1ContentsWithTotal = new + referencesMyReference1ContentsWithTotal = new { total = 10, items = new[] @@ -1180,7 +1181,7 @@ public class GraphQLQueriesTests : GraphQLTestBase public async Task Should_also_fetch_union_contents_if_field_is_included_in_query() { var contentRefId = DomainId.NewGuid(); - var contentRef = TestContent.CreateRef(TestSchemas.Ref1Id, contentRefId, "schemaRef1Field", "ref1"); + var contentRef = TestContent.CreateRef(TestSchemas.Reference1.NamedId(), contentRefId, "reference1-field", "reference1"); var contentId = DomainId.NewGuid(); var content = TestContent.Create(contentId, contentRefId); @@ -1207,9 +1208,9 @@ public class GraphQLQueriesTests : GraphQLTestBase ... on Content { id } - ... on MyRefSchema1 { + ... on MyReference1 { data { - schemaRef1Field { + reference1Field { iv } } @@ -1244,12 +1245,12 @@ public class GraphQLQueriesTests : GraphQLTestBase id = contentRefId, data = new { - schemaRef1Field = new + reference1Field = new { - iv = "ref1" + iv = "reference1" } }, - __typename = "MyRefSchema1" + __typename = "MyReference1" } } } 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 ccb84af7a..50be7d694 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 @@ -76,7 +76,7 @@ public abstract class GraphQLTestBase : IClassFixture protected Task ExecuteAsync(TestQuery query) { // Use a shared instance to test caching. - sut ??= CreateSut(TestSchemas.Default, TestSchemas.Ref1, TestSchemas.Ref2); + sut ??= CreateSut(TestSchemas.Default, TestSchemas.Reference1, TestSchemas.Reference2, TestSchemas.Component); var options = query.ToOptions(sut.Services); diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs index 9b115f2ea..a593e3993 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestContent.cs @@ -7,6 +7,7 @@ using NodaTime; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; @@ -88,7 +89,7 @@ public static class TestContent iv { schemaId schemaName - schemaRef1Field + componentField } } myComponents__Dynamic { @@ -97,15 +98,20 @@ public static class TestContent myComponents { iv { __typename - ... on MyRefSchema1Component { + ... on MyReference1Component { schemaId schemaName - schemaRef1Field + reference1Field } - ... on MyRefSchema2Component { + ... on MyReference2Component { schemaId schemaName - schemaRef2Field + reference2Field + } + ... on MyComponentComponent { + schemaId + schemaName + componentField } } } @@ -176,20 +182,25 @@ public static class TestContent myComponent { schemaId schemaName - schemaRef1Field + componentField } myComponents__Dynamic myComponents { __typename - ... on MyRefSchema1Component { + ... on MyReference1Component { + schemaId + schemaName + reference1Field + } + ... on MyReference2Component { schemaId schemaName - schemaRef1Field + reference2Field } - ... on MyRefSchema2Component { + ... on MyComponentComponent { schemaId schemaName - schemaRef2Field + componentField } } myTags @@ -249,21 +260,25 @@ public static class TestContent new ContentFieldData() .AddInvariant( JsonValue.Object() - .Add(Component.Discriminator, TestSchemas.Ref1.Id) - .Add(Component.Descriptor, TestSchemas.Ref1.SchemaDef.Name) - .Add("schemaRef1Field", "Component1"))) + .Add(Component.Discriminator, TestSchemas.Component.Id) + .Add(Component.Descriptor, TestSchemas.Component.SchemaDef.Name) + .Add("component-field", "Component1"))) .AddField("my-components", new ContentFieldData() .AddInvariant( JsonValue.Array( JsonValue.Object() - .Add(Component.Discriminator, TestSchemas.Ref1.Id) - .Add(Component.Descriptor, TestSchemas.Ref1.SchemaDef.Name) - .Add("schemaRef1Field", "Component1"), + .Add(Component.Discriminator, TestSchemas.Reference1.Id) + .Add(Component.Descriptor, TestSchemas.Reference1.SchemaDef.Name) + .Add("reference1-field", "Component1"), JsonValue.Object() - .Add(Component.Discriminator, TestSchemas.Ref2.Id) - .Add(Component.Descriptor, TestSchemas.Ref2.SchemaDef.Name) - .Add("schemaRef2Field", "Component2")))) + .Add(Component.Discriminator, TestSchemas.Reference2.Id) + .Add(Component.Descriptor, TestSchemas.Reference2.SchemaDef.Name) + .Add("reference2-field", "Component2"), + JsonValue.Object() + .Add(Component.Discriminator, TestSchemas.Component.Id) + .Add(Component.Descriptor, TestSchemas.Component.SchemaDef.Name) + .Add("component-field", "Component3")))) .AddField("my-json", new ContentFieldData() .AddInvariant( @@ -321,7 +336,7 @@ public static class TestContent LastModified = now, LastModifiedBy = RefToken.Client("client1"), Data = data, - SchemaId = TestSchemas.DefaultId, + SchemaId = TestSchemas.Default.NamedId(), Status = Status.Draft, StatusColor = "red", NewStatus = Status.Published, @@ -492,9 +507,9 @@ public static class TestContent { iv = new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component1" } }, ["myComponents"] = new @@ -503,15 +518,21 @@ public static class TestContent { new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Reference1.Id.ToString(), + ["schemaName"] = TestSchemas.Reference1.SchemaDef.Name, + ["reference1Field"] = "Component1" }, new Dictionary { - ["schemaId"] = TestSchemas.Ref2.Id.ToString(), - ["schemaName"] = TestSchemas.Ref2.SchemaDef.Name, - ["schemaRef2Field"] = "Component2" + ["schemaId"] = TestSchemas.Reference2.Id.ToString(), + ["schemaName"] = TestSchemas.Reference2.SchemaDef.Name, + ["reference2Field"] = "Component2" + }, + new Dictionary + { + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component3" } } }, @@ -659,18 +680,18 @@ public static class TestContent { iv = new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["component-field"] = "Component1" } }, ["myComponent"] = new { iv = new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component1" } }, ["myComponents__Dynamic"] = new @@ -679,15 +700,21 @@ public static class TestContent { new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Reference1.Id.ToString(), + ["schemaName"] = TestSchemas.Reference1.SchemaDef.Name, + ["reference1-field"] = "Component1" + }, + new Dictionary + { + ["schemaId"] = TestSchemas.Reference2.Id.ToString(), + ["schemaName"] = TestSchemas.Reference2.SchemaDef.Name, + ["reference2-field"] = "Component2" }, new Dictionary { - ["schemaId"] = TestSchemas.Ref2.Id.ToString(), - ["schemaName"] = TestSchemas.Ref2.SchemaDef.Name, - ["schemaRef2Field"] = "Component2" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["component-field"] = "Component3" } } }, @@ -697,17 +724,24 @@ public static class TestContent { new Dictionary { - ["__typename"] = "MyRefSchema1Component", - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["__typename"] = "MyReference1Component", + ["schemaId"] = TestSchemas.Reference1.Id.ToString(), + ["schemaName"] = TestSchemas.Reference1.SchemaDef.Name, + ["reference1Field"] = "Component1" + }, + new Dictionary + { + ["__typename"] = "MyReference2Component", + ["schemaId"] = TestSchemas.Reference2.Id.ToString(), + ["schemaName"] = TestSchemas.Reference2.SchemaDef.Name, + ["reference2Field"] = "Component2" }, new Dictionary { - ["__typename"] = "MyRefSchema2Component", - ["schemaId"] = TestSchemas.Ref2.Id.ToString(), - ["schemaName"] = TestSchemas.Ref2.SchemaDef.Name, - ["schemaRef2Field"] = "Component2" + ["__typename"] = "MyComponentComponent", + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component3" } } }, @@ -788,46 +822,59 @@ public static class TestContent }, ["myComponent__Dynamic"] = new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["component-field"] = "Component1" }, ["myComponent"] = new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component1" }, ["myComponents__Dynamic"] = new[] { new Dictionary { - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["schemaId"] = TestSchemas.Reference1.Id.ToString(), + ["schemaName"] = TestSchemas.Reference1.SchemaDef.Name, + ["reference1-field"] = "Component1" }, new Dictionary { - ["schemaId"] = TestSchemas.Ref2.Id.ToString(), - ["schemaName"] = TestSchemas.Ref2.SchemaDef.Name, - ["schemaRef2Field"] = "Component2" + ["schemaId"] = TestSchemas.Reference2.Id.ToString(), + ["schemaName"] = TestSchemas.Reference2.SchemaDef.Name, + ["reference2-field"] = "Component2" + }, + new Dictionary + { + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["component-field"] = "Component3" } }, ["myComponents"] = new object[] { new Dictionary { - ["__typename"] = "MyRefSchema1Component", - ["schemaId"] = TestSchemas.Ref1.Id.ToString(), - ["schemaName"] = TestSchemas.Ref1.SchemaDef.Name, - ["schemaRef1Field"] = "Component1" + ["__typename"] = "MyReference1Component", + ["schemaId"] = TestSchemas.Reference1.Id.ToString(), + ["schemaName"] = TestSchemas.Reference1.SchemaDef.Name, + ["reference1Field"] = "Component1" + }, + new Dictionary + { + ["__typename"] = "MyReference2Component", + ["schemaId"] = TestSchemas.Reference2.Id.ToString(), + ["schemaName"] = TestSchemas.Reference2.SchemaDef.Name, + ["reference2Field"] = "Component2" }, new Dictionary { - ["__typename"] = "MyRefSchema2Component", - ["schemaId"] = TestSchemas.Ref2.Id.ToString(), - ["schemaName"] = TestSchemas.Ref2.SchemaDef.Name, - ["schemaRef2Field"] = "Component2" + ["__typename"] = "MyComponentComponent", + ["schemaId"] = TestSchemas.Component.Id.ToString(), + ["schemaName"] = TestSchemas.Component.SchemaDef.Name, + ["componentField"] = "Component3" } }, ["myTags"] = new[] diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestQuery.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestQuery.cs index 4fed4bdc5..3dbcad65f 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestQuery.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestQuery.cs @@ -73,7 +73,7 @@ public sealed class TestQuery return new Context(Mocks.FrontendUser(), TestApp.Default); } - var permission = PermissionIds.ForApp(permissionId, TestApp.Default.Name, TestSchemas.DefaultId.Name).Id; + var permission = PermissionIds.ForApp(permissionId, TestApp.Default.Name, TestSchemas.Default.SchemaDef.Name).Id; return new Context(Mocks.FrontendUser(permission: permission), TestApp.Default); } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs index f8340ef34..0946c4d6c 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/TestSchemas.cs @@ -16,26 +16,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL; public static class TestSchemas { - public static readonly NamedId DefaultId = NamedId.Of(DomainId.NewGuid(), "my-schema"); - public static readonly NamedId Ref1Id = NamedId.Of(DomainId.NewGuid(), "my-ref-schema1"); - public static readonly NamedId Ref2Id = NamedId.Of(DomainId.NewGuid(), "my-ref-schema2"); - public static readonly ISchemaEntity Default; - public static readonly ISchemaEntity Ref1; - public static readonly ISchemaEntity Ref2; + public static readonly ISchemaEntity Reference1; + public static readonly ISchemaEntity Reference2; + public static readonly ISchemaEntity Component; static TestSchemas() { - Ref1 = Mocks.Schema(TestApp.DefaultId, Ref1Id, - new Schema(Ref1Id.Name) - .Publish() - .AddString(1, "schemaRef1Field", Partitioning.Invariant)); - - Ref2 = Mocks.Schema(TestApp.DefaultId, Ref2Id, - new Schema(Ref2Id.Name) - .Publish() - .AddString(1, "schemaRef2Field", Partitioning.Invariant)); - var enums = ReadonlyList.Create("EnumA", "EnumB", "EnumC"); var jsonSchema = @" @@ -56,8 +43,22 @@ public static class TestSchemas nestedArray: [String] }"; - Default = Mocks.Schema(TestApp.DefaultId, DefaultId, - new Schema(DefaultId.Name) + Component = Mocks.Schema(TestApp.DefaultId, DomainId.NewGuid(), + new Schema("my-component", type: SchemaType.Component) + .AddString(1, "component-field", Partitioning.Invariant)); + + Reference1 = Mocks.Schema(TestApp.DefaultId, DomainId.NewGuid(), + new Schema("my-reference1") + .Publish() + .AddString(1, "reference1-field", Partitioning.Invariant)); + + Reference2 = Mocks.Schema(TestApp.DefaultId, DomainId.NewGuid(), + new Schema("my-reference2") + .Publish() + .AddString(1, "reference2-field", Partitioning.Invariant)); + + Default = Mocks.Schema(TestApp.DefaultId, DomainId.NewGuid(), + new Schema("my-schema") .Publish() .AddJson(1, "my-json", Partitioning.Invariant, new JsonFieldProperties()) @@ -78,15 +79,15 @@ public static class TestSchemas .AddDateTime(9, "my-datetime", Partitioning.Invariant, new DateTimeFieldProperties()) .AddReferences(10, "my-references", Partitioning.Invariant, - new ReferencesFieldProperties { SchemaId = Ref1Id.Id }) + new ReferencesFieldProperties { SchemaId = Reference1.Id }) .AddReferences(11, "my-union", Partitioning.Invariant, new ReferencesFieldProperties()) .AddGeolocation(12, "my-geolocation", Partitioning.Invariant, new GeolocationFieldProperties()) .AddComponent(13, "my-component", Partitioning.Invariant, - new ComponentFieldProperties { SchemaId = Ref1Id.Id }) + new ComponentFieldProperties { SchemaId = Component.Id }) .AddComponents(14, "my-components", Partitioning.Invariant, - new ComponentsFieldProperties { SchemaIds = ReadonlyList.Create(Ref1.Id, Ref2.Id) }) + new ComponentsFieldProperties { SchemaIds = ReadonlyList.Create(Reference1.Id, Reference2.Id, Component.Id) }) .AddTags(15, "my-tags", Partitioning.Invariant, new TagsFieldProperties()) .AddTags(16, "my-tags-enum", Partitioning.Invariant, @@ -97,7 +98,7 @@ public static class TestSchemas .AddNumber(122, "nested-number", new NumberFieldProperties())) .AddString(17, "my-embeds", Partitioning.Invariant, - new StringFieldProperties { IsEmbeddable = true, SchemaIds = ReadonlyList.Create(Ref1.Id, Ref2.Id) }) + new StringFieldProperties { IsEmbeddable = true, SchemaIds = ReadonlyList.Create(Reference1.Id, Reference2.Id) }) .SetScripts(new SchemaScripts { Query = "" })); } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs index cbaed3237..ede912c85 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/ContentsQueryFixture.cs @@ -221,12 +221,10 @@ public abstract class ContentsQueryFixture : IAsyncLifetime new Schema("my-schema") .AddField(Fields.Number(1, "value", Partitioning.Invariant)); - var schema = + return Mocks.Schema( NamedId.Of(appId, "my-app"), - NamedId.Of(schemaId, "my-schema"), + schemaId, schemaDef); - - return schema; } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs index 99d529364..454668957 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/Queries/ResolveReferencesTests.cs @@ -21,14 +21,14 @@ public class ResolveReferencesTests : GivenContext, IClassFixture(); private readonly IRequestCache requestCache = A.Fake(); - private readonly NamedId refSchemaId1 = NamedId.Of(DomainId.NewGuid(), "my-ref1"); - private readonly NamedId refSchemaId2 = NamedId.Of(DomainId.NewGuid(), "my-ref2"); + private readonly NamedId referenceSchemaId1 = NamedId.Of(DomainId.NewGuid(), "my-ref1"); + private readonly NamedId referenceSchemaId2 = NamedId.Of(DomainId.NewGuid(), "my-ref2"); private readonly ProvideSchema schemaProvider; private readonly ResolveReferences sut; public ResolveReferencesTests() { - var refSchemaDef = + var referencedSchemaDef = new Schema("my-ref") .AddString(1, "name", Partitioning.Invariant, new StringFieldProperties()) @@ -43,14 +43,14 @@ public class ResolveReferencesTests : GivenContext, IClassFixture requestCache.AddDependency(DomainId.Combine(AppId, refSchemaId1.Id), 0)) + A.CallTo(() => requestCache.AddDependency(DomainId.Combine(AppId, referenceSchemaId1.Id), 0)) .MustHaveHappened(); - A.CallTo(() => requestCache.AddDependency(DomainId.Combine(AppId, refSchemaId2.Id), 0)) + A.CallTo(() => requestCache.AddDependency(DomainId.Combine(AppId, referenceSchemaId2.Id), 0)) .MustHaveHappened(); A.CallTo(() => requestCache.AddDependency(ref1_1.UniqueId, ref1_1.Version)) @@ -122,10 +122,10 @@ public class ResolveReferencesTests : GivenContext, IClassFixture appId, NamedId schemaId, Schema? schemaDef = null) + public static ISchemaEntity Schema(NamedId appId, NamedId schemaId) { - var schema = A.Fake(); + var schemaEntity = A.Fake(); + var schemaDef = new Schema(schemaId.Name).Publish(); - schemaDef ??= new Schema(schemaId.Name).Publish(); + A.CallTo(() => schemaEntity.Id).Returns(schemaId.Id); + A.CallTo(() => schemaEntity.AppId).Returns(appId); + A.CallTo(() => schemaEntity.SchemaDef).Returns(schemaDef); + A.CallTo(() => schemaEntity.UniqueId).Returns(DomainId.Combine(appId, schemaId.Id)); - A.CallTo(() => schema.Id).Returns(schemaId.Id); - A.CallTo(() => schema.AppId).Returns(appId); - A.CallTo(() => schema.SchemaDef).Returns(schemaDef); - A.CallTo(() => schema.UniqueId).Returns(DomainId.Combine(appId, schemaId.Id)); + return schemaEntity; + } + + public static ISchemaEntity Schema(NamedId appId, DomainId schemaId, Schema schemaDef) + { + var schemaEntity = A.Fake(); + + A.CallTo(() => schemaEntity.Id).Returns(schemaId); + A.CallTo(() => schemaEntity.AppId).Returns(appId); + A.CallTo(() => schemaEntity.SchemaDef).Returns(schemaDef); + A.CallTo(() => schemaEntity.UniqueId).Returns(DomainId.Combine(appId, schemaId)); - return schema; + return schemaEntity; } public static ITeamEntity Team(DomainId teamId, string teamName) diff --git a/tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs b/tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs index 6d8116519..dee9a19be 100644 --- a/tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs +++ b/tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs @@ -63,6 +63,23 @@ public sealed class GraphQLFixture : ContentFixture } } + var createLocationRequest = new CreateSchemaDto + { + Name = "location", + Fields = new List + { + new UpsertSchemaFieldDto + { + Name = "name", + Properties = new StringFieldPropertiesDto() + } + }, + Type = SchemaType.Component + }; + + var locationsId = await CreateSchemaAsync(createLocationRequest); + + // STEP 1: Create cities schema. var createCitiesRequest = new CreateSchemaDto { @@ -73,6 +90,28 @@ public sealed class GraphQLFixture : ContentFixture { Name = "name", Properties = new StringFieldPropertiesDto() + }, + new UpsertSchemaFieldDto + { + Name = "topLocation", + Properties = new ComponentFieldPropertiesDto + { + SchemaIds = new List + { + locationsId + } + } + }, + new UpsertSchemaFieldDto + { + Name = "locations", + Properties = new ComponentsFieldPropertiesDto + { + SchemaIds = new List + { + locationsId + } + } } }, IsPublished = true @@ -149,6 +188,27 @@ public sealed class GraphQLFixture : ContentFixture name = new { iv = name + }, + topLocation = new + { + iv = new + { + name = $"{name} Top Location" + } + }, + locations = new + { + iv = new[] + { + new + { + name = $"{name} Location 1", + }, + new + { + name = $"{name} Location 2", + } + } } }; diff --git a/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs b/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs index 31f932dd5..aa1973fc6 100644 --- a/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs +++ b/tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using System.Net.Http.Json; +using FluentAssertions; using Newtonsoft.Json.Linq; using Squidex.ClientLibrary; using Squidex.ClientLibrary.Utils; @@ -61,6 +62,80 @@ public sealed class GraphQLTests : IClassFixture Assert.Equal(2, result["findMyWritesContent"]["flatData"]["json"]["obj"]["value"].Value()); } + [Fact] + public async Task Should_query_graphql_with_components() + { + var query = new + { + query = @" + { + cities: queryCitiesContents { + data: flatData { + name, + topLocation { + name + }, + locations { + name + } + } + } + }" + }; + + var result = await _.Client.SharedDynamicContents.GraphQlAsync(query); + + var cities = result["cities"].ToObject>(); + + cities.Should().BeEquivalentTo(new List + { + new City + { + Data = new CityData + { + Name = "Leipzig", + TopLocation = new LocationData + { + Name = "Leipzig Top Location" + }, + Locations = new List + { + new LocationData + { + Name = "Leipzig Location 1" + }, + new LocationData + { + Name = "Leipzig Location 2" + } + } + } + }, + new City + { + Data = new CityData + { + Name = "Munich", + TopLocation = new LocationData + { + Name = "Munich Top Location" + }, + Locations = new List + { + new LocationData + { + Name = "Munich Location 1" + }, + new LocationData + { + Name = "Munich Location 2" + } + } + } + } + }); + } + [Fact] public async Task Should_query_graphql_by_reference_selector() { diff --git a/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj b/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj index 334b241f7..6e1c2c1d4 100644 --- a/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj +++ b/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj @@ -15,6 +15,7 @@ + diff --git a/tools/TestSuite/TestSuite.Shared/Model/Geography.cs b/tools/TestSuite/TestSuite.Shared/Model/Geography.cs index ca5a91d1d..6b6472ea5 100644 --- a/tools/TestSuite/TestSuite.Shared/Model/Geography.cs +++ b/tools/TestSuite/TestSuite.Shared/Model/Geography.cs @@ -39,6 +39,15 @@ public sealed class City } public sealed class CityData +{ + public string Name { get; set; } + + public LocationData TopLocation { get; set; } + + public List Locations { get; set; } +} + +public sealed class LocationData { public string Name { get; set; } }