diff --git a/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs b/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs index 2b7096d25..e726584be 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/AppProviderExtensions.cs @@ -28,16 +28,21 @@ namespace Squidex.Domain.Apps.Entities { foreach (var schemaId in schemaIds) { - if (result == null || !result.TryGetValue(schemaId, out _)) + if (schemaId == schema.Id) { - var resolvedEntity = await appProvider.GetSchemaAsync(appId, schemaId, true); + result ??= new Dictionary(); + result[schemaId] = schema.SchemaDef; + } + else if (result == null || !result.TryGetValue(schemaId, out _)) + { + var resolvedEntity = await appProvider.GetSchemaAsync(appId, schemaId, false); if (resolvedEntity != null) { - await ResolveSchemaAsync(resolvedEntity); - result ??= new Dictionary(); result[schemaId] = resolvedEntity.SchemaDef; + + await ResolveSchemaAsync(resolvedEntity); } } } diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs new file mode 100644 index 000000000..65a51a367 --- /dev/null +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs @@ -0,0 +1,194 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using FakeItEasy; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.TestHelpers; +using Squidex.Infrastructure; +using Xunit; + +namespace Squidex.Domain.Apps.Entities +{ + public class AppProviderExtensionsTests + { + private readonly IAppProvider appProvider = A.Fake(); + private readonly NamedId appId = NamedId.Of(DomainId.NewGuid(), "my-app"); + private readonly NamedId schemaId = NamedId.Of(DomainId.NewGuid(), "my-schema"); + private readonly NamedId componentId1 = NamedId.Of(DomainId.NewGuid(), "my-schema"); + private readonly NamedId componentId2 = NamedId.Of(DomainId.NewGuid(), "my-schema"); + + [Fact] + public async Task Should_do_nothing_if_no_component_found() + { + var schema = Mocks.Schema(appId, schemaId); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Empty(components); + + A.CallTo(() => appProvider.GetSchemaAsync(A._, A._, false)) + .MustNotHaveHappened(); + } + + [Fact] + public async Task Should_resolve_self_as_component() + { + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = schemaId.Id + })); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Single(components); + Assert.Same(schema.SchemaDef, components[schemaId.Id]); + + A.CallTo(() => appProvider.GetSchemaAsync(A._, A._, false)) + .MustNotHaveHappened(); + } + + [Fact] + public async Task Should_resolve_from_component() + { + var component = Mocks.Schema(appId, componentId1); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId1.Id, false)) + .Returns(component); + + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId1.Id + })); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Single(components); + Assert.Same(component.SchemaDef, components[componentId1.Id]); + } + + [Fact] + public async Task Should_resolve_from_components() + { + var component = Mocks.Schema(appId, componentId1); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId1.Id, false)) + .Returns(component); + + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddComponents(1, "1", Partitioning.Invariant, new ComponentsFieldProperties + { + SchemaId = componentId1.Id + })); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Single(components); + Assert.Same(component.SchemaDef, components[componentId1.Id]); + } + + [Fact] + public async Task Should_resolve_from_array() + { + var component = Mocks.Schema(appId, componentId1); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId1.Id, false)) + .Returns(component); + + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddArray(1, "1", Partitioning.Invariant, a => a + .AddComponent(2, "2", new ComponentFieldProperties + { + SchemaId = componentId1.Id + }))); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Single(components); + Assert.Same(component.SchemaDef, components[componentId1.Id]); + } + + [Fact] + public async Task Should_resolve_self_referencing_component() + { + var component = + Mocks.Schema(appId, componentId1, + new Schema(componentId1.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId1.Id + })); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId1.Id, false)) + .Returns(component); + + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId1.Id + })); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Single(components); + Assert.Same(component.SchemaDef, components[componentId1.Id]); + } + + [Fact] + public async Task Should_resolve_component_of_component() + { + var component1 = + Mocks.Schema(appId, componentId1, + new Schema(componentId1.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId2.Id + })); + + var component2 = + Mocks.Schema(appId, componentId2, + new Schema(componentId2.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId2.Id + })); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId1.Id, false)) + .Returns(component1); + + A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, componentId2.Id, false)) + .Returns(component2); + + var schema = + Mocks.Schema(appId, schemaId, + new Schema(schemaId.Name) + .AddComponent(1, "1", Partitioning.Invariant, new ComponentFieldProperties + { + SchemaId = componentId1.Id + })); + + var components = await appProvider.GetComponentsAsync(schema); + + Assert.Equal(2, components.Count); + Assert.Same(component1.SchemaDef, components[componentId1.Id]); + Assert.Same(component2.SchemaDef, components[componentId2.Id]); + } + } +}