Browse Source

Graphql query by ids (#985)

* Improve handling of default values.

* Open Api fixes and parent id.

* Query by IDs

* Introspection fixes.
pull/987/head
Sebastian Stehle 4 years ago
committed by GitHub
parent
commit
175711109d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.Designer.cs
  2. 3
      backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.resx
  3. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  4. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs
  5. 32
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs
  6. 20
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs
  7. 8
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs
  8. 1
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs
  9. 1
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentInterfaceGraphType.cs
  10. 6
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentUnionGraphType.cs
  11. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs
  12. 2
      backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs
  13. 4
      backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs
  14. 24
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs
  15. 2
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs
  16. 186
      backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  17. 50
      tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs
  18. 64
      tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

9
backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.Designer.cs

@ -672,6 +672,15 @@ namespace Squidex.Domain.Apps.Core {
} }
} }
/// <summary>
/// Looks up a localized string similar to The list of IDs..
/// </summary>
public static string EntityIds {
get {
return ResourceManager.GetString("EntityIds", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to True when deleted.. /// Looks up a localized string similar to True when deleted..
/// </summary> /// </summary>

3
backend/src/Squidex.Domain.Apps.Core.Model/FieldDescriptions.resx

@ -321,6 +321,9 @@
<data name="EntityId" xml:space="preserve"> <data name="EntityId" xml:space="preserve">
<value>The ID of the object (usually GUID).</value> <value>The ID of the object (usually GUID).</value>
</data> </data>
<data name="EntityIds" xml:space="preserve">
<value>The list of IDs.</value>
</data>
<data name="EntityIsDeleted" xml:space="preserve"> <data name="EntityIsDeleted" xml:space="preserve">
<value>True when deleted.</value> <value>True when deleted.</value>
</data> </data>

4
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -154,7 +154,7 @@ public sealed class GraphQLExecutionContext : QueryExecutionContext
return dataLoaders.Context!.GetOrAddBatchLoader<DomainId, IEnrichedAssetEntity>(nameof(GetAssetsLoader), return dataLoaders.Context!.GetOrAddBatchLoader<DomainId, IEnrichedAssetEntity>(nameof(GetAssetsLoader),
async (batch, ct) => async (batch, ct) =>
{ {
var result = await GetReferencedAssetsAsync(new List<DomainId>(batch), ct); var result = await QueryAssetsByIdsAsync(new List<DomainId>(batch), ct);
return result.ToDictionary(x => x.Id); return result.ToDictionary(x => x.Id);
}); });
@ -165,7 +165,7 @@ public sealed class GraphQLExecutionContext : QueryExecutionContext
return dataLoaders.Context!.GetOrAddBatchLoader<DomainId, IEnrichedContentEntity>(nameof(GetContentsLoader), return dataLoaders.Context!.GetOrAddBatchLoader<DomainId, IEnrichedContentEntity>(nameof(GetContentsLoader),
async (batch, ct) => async (batch, ct) =>
{ {
var result = await GetReferencedContentsAsync(new List<DomainId>(batch), ct); var result = await QueryContentsByIdsAsync(new List<DomainId>(batch), ct);
return result.ToDictionary(x => x.Id); return result.ToDictionary(x => x.Id);
}); });

20
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ApplicationQueries.cs

@ -17,6 +17,7 @@ internal sealed class ApplicationQueries : ObjectGraphType
AddField(SharedTypes.FindAsset); AddField(SharedTypes.FindAsset);
AddField(SharedTypes.QueryAssets); AddField(SharedTypes.QueryAssets);
AddField(SharedTypes.QueryAssetsWithTotal); AddField(SharedTypes.QueryAssetsWithTotal);
AddContentQuery(builder);
foreach (var schemaInfo in schemaInfos) foreach (var schemaInfo in schemaInfos)
{ {
@ -73,4 +74,23 @@ internal sealed class ApplicationQueries : ObjectGraphType
Description = $"Query {schemaInfo.DisplayName} content items with total count." Description = $"Query {schemaInfo.DisplayName} content items with total count."
}).WithSchemaId(schemaInfo); }).WithSchemaId(schemaInfo);
} }
private void AddContentQuery(Builder builder)
{
var unionType = builder.GetContentUnion("AllContents", null);
if (!unionType.HasType)
{
return;
}
AddField(new FieldType
{
Name = "queryContents",
Arguments = ContentActions.QueryByIds.Arguments,
ResolvedType = new NonNullGraphType(new ListGraphType(new NonNullGraphType(unionType))),
Resolver = ContentActions.QueryByIds.Resolver,
Description = "Query content items by IDs across schemeas."
});
}
} }

32
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Builder.cs

@ -22,15 +22,15 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types;
internal sealed class Builder internal sealed class Builder
{ {
private readonly Dictionary<SchemaInfo, ComponentGraphType> componentTypes = new Dictionary<SchemaInfo, ComponentGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<FieldInfo, ComponentUnionGraphType> componentUnionTypes = new ();
private readonly Dictionary<SchemaInfo, ContentGraphType> contentTypes = new Dictionary<SchemaInfo, ContentGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<FieldInfo, EmbeddableStringGraphType> embeddableStringTypes = new ();
private readonly Dictionary<SchemaInfo, ContentResultGraphType> contentResultTypes = new Dictionary<SchemaInfo, ContentResultGraphType>(ReferenceEqualityComparer.Instance); private readonly Dictionary<FieldInfo, NestedGraphType> nestedTypes = new ();
private readonly Dictionary<FieldInfo, EmbeddableStringGraphType> embeddableStringTypes = new Dictionary<FieldInfo, EmbeddableStringGraphType>(); private readonly Dictionary<SchemaInfo, ComponentGraphType> componentTypes = new ();
private readonly Dictionary<FieldInfo, ReferenceUnionGraphType> referenceUnionTypes = new Dictionary<FieldInfo, ReferenceUnionGraphType>(); private readonly Dictionary<SchemaInfo, ContentGraphType> contentTypes = new ();
private readonly Dictionary<FieldInfo, ComponentUnionGraphType> componentUnionTypes = new Dictionary<FieldInfo, ComponentUnionGraphType>(); private readonly Dictionary<SchemaInfo, ContentResultGraphType> contentResultTypes = new ();
private readonly Dictionary<FieldInfo, NestedGraphType> nestedTypes = new Dictionary<FieldInfo, NestedGraphType>(); private readonly Dictionary<string, ContentUnionGraphType> unionTypes = new ();
private readonly Dictionary<string, EnumerationGraphType?> enumTypes = new Dictionary<string, EnumerationGraphType?>(); private readonly Dictionary<string, EnumerationGraphType?> enumTypes = new ();
private readonly Dictionary<string, IGraphType[]> dynamicTypes = new Dictionary<string, IGraphType[]>(); private readonly Dictionary<string, IGraphType[]> dynamicTypes = new ();
private readonly FieldVisitor fieldVisitor; private readonly FieldVisitor fieldVisitor;
private readonly FieldInputVisitor fieldInputVisitor; private readonly FieldInputVisitor fieldInputVisitor;
private readonly PartitionResolver partitionResolver; private readonly PartitionResolver partitionResolver;
@ -186,19 +186,14 @@ internal sealed class Builder
return dynamicTypes.GetOrAdd(graphQLSchema, x => DynamicSchemaBuilder.ParseTypes(x, typeNames)); return dynamicTypes.GetOrAdd(graphQLSchema, x => DynamicSchemaBuilder.ParseTypes(x, typeNames));
} }
public EmbeddableStringGraphType GetEmbeddableString(FieldInfo fieldInfo, StringFieldProperties properties)
{
return embeddableStringTypes.GetOrAdd(fieldInfo, x => new EmbeddableStringGraphType(this, x, properties));
}
public EnumerationGraphType? GetEnumeration(string name, IEnumerable<string> values) public EnumerationGraphType? GetEnumeration(string name, IEnumerable<string> values)
{ {
return enumTypes.GetOrAdd(name, x => FieldEnumType.TryCreate(name, values)); return enumTypes.GetOrAdd(name, x => FieldEnumType.TryCreate(name, values));
} }
public ReferenceUnionGraphType GetReferenceUnion(FieldInfo fieldInfo, ReadonlyList<DomainId>? schemaIds) public EmbeddableStringGraphType GetEmbeddableString(FieldInfo fieldInfo, StringFieldProperties properties)
{ {
return referenceUnionTypes.GetOrAdd(fieldInfo, x => new ReferenceUnionGraphType(this, x, schemaIds)); return embeddableStringTypes.GetOrAdd(fieldInfo, x => new EmbeddableStringGraphType(this, x, properties));
} }
public ComponentUnionGraphType GetComponentUnion(FieldInfo fieldInfo, ReadonlyList<DomainId>? schemaIds) public ComponentUnionGraphType GetComponentUnion(FieldInfo fieldInfo, ReadonlyList<DomainId>? schemaIds)
@ -206,6 +201,11 @@ internal sealed class Builder
return componentUnionTypes.GetOrAdd(fieldInfo, x => new ComponentUnionGraphType(this, x, schemaIds)); return componentUnionTypes.GetOrAdd(fieldInfo, x => new ComponentUnionGraphType(this, x, schemaIds));
} }
public ContentUnionGraphType GetContentUnion(string name, ReadonlyList<DomainId>? schemaIds)
{
return unionTypes.GetOrAdd(name, x => new ContentUnionGraphType(this, x, schemaIds));
}
public NestedGraphType GetNested(FieldInfo fieldInfo) public NestedGraphType GetNested(FieldInfo fieldInfo)
{ {
return nestedTypes.GetOrAdd(fieldInfo, x => new NestedGraphType(this, x)); return nestedTypes.GetOrAdd(fieldInfo, x => new NestedGraphType(this, x));

20
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentActions.cs

@ -96,6 +96,26 @@ internal static class ContentActions
}); });
} }
public static class QueryByIds
{
public static readonly QueryArguments Arguments = new QueryArguments
{
new QueryArgument(Scalars.Strings)
{
Name = "ids",
Description = FieldDescriptions.EntityIds,
DefaultValue = null
}
};
public static readonly IFieldResolver Resolver = Resolvers.Async<object, object?>(async (_, fieldContext, context) =>
{
var contentIds = fieldContext.GetArgument<DomainId[]>("ids");
return await context.QueryContentsByIdsAsync(contentIds, fieldContext.CancellationToken);
});
}
public static class QueryOrReferencing public static class QueryOrReferencing
{ {
public static readonly QueryArguments Arguments = new QueryArguments public static readonly QueryArguments Arguments = new QueryArguments

8
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentFields.cs

@ -155,6 +155,14 @@ internal static class ContentFields
Description = FieldDescriptions.EditToken Description = FieldDescriptions.EditToken
}; };
public static readonly FieldType DataDynamic = new FieldType
{
Name = "data__dynamic",
ResolvedType = Scalars.JsonNoop,
Resolver = Resolve(x => x.Data),
Description = FieldDescriptions.ContentData
};
public static readonly FieldType StringFieldText = new FieldType public static readonly FieldType StringFieldText = new FieldType
{ {
Name = "text", Name = "text",

1
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentGraphType.cs

@ -42,6 +42,7 @@ internal sealed class ContentGraphType : ObjectGraphType<IEnrichedContentEntity>
AddField(ContentFields.StatusColor); AddField(ContentFields.StatusColor);
AddField(ContentFields.NewStatus); AddField(ContentFields.NewStatus);
AddField(ContentFields.NewStatusColor); AddField(ContentFields.NewStatusColor);
AddField(ContentFields.DataDynamic);
AddField(ContentFields.Url); AddField(ContentFields.Url);
var contentDataType = new DataGraphType(builder, schemaInfo); var contentDataType = new DataGraphType(builder, schemaInfo);

1
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentInterfaceGraphType.cs

@ -26,6 +26,7 @@ internal sealed class ContentInterfaceGraphType : InterfaceGraphType<IEnrichedCo
AddField(ContentFields.StatusColor); AddField(ContentFields.StatusColor);
AddField(ContentFields.NewStatus); AddField(ContentFields.NewStatus);
AddField(ContentFields.NewStatusColor); AddField(ContentFields.NewStatusColor);
AddField(ContentFields.DataDynamic);
AddField(ContentFields.Url); AddField(ContentFields.Url);
Description = "The structure of all content types."; Description = "The structure of all content types.";

6
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ReferenceUnionGraphType.cs → backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/ContentUnionGraphType.cs

@ -11,16 +11,16 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents; namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Contents;
internal sealed class ReferenceUnionGraphType : UnionGraphType internal sealed class ContentUnionGraphType : UnionGraphType
{ {
private readonly Dictionary<DomainId, IObjectGraphType> types = new Dictionary<DomainId, IObjectGraphType>(); private readonly Dictionary<DomainId, IObjectGraphType> types = new Dictionary<DomainId, IObjectGraphType>();
public bool HasType => types.Count > 0; public bool HasType => types.Count > 0;
public ReferenceUnionGraphType(Builder builder, FieldInfo fieldInfo, ReadonlyList<DomainId>? schemaIds) public ContentUnionGraphType(Builder builder, string name, ReadonlyList<DomainId>? schemaIds)
{ {
// The name is used for equal comparison. Therefore it is important to treat it as readonly. // The name is used for equal comparison. Therefore it is important to treat it as readonly.
Name = fieldInfo.UnionReferenceType; Name = name;
if (schemaIds?.Any() == true) if (schemaIds?.Any() == true)
{ {

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/EmbeddableStringGraphType.cs

@ -48,7 +48,7 @@ internal sealed class EmbeddableStringGraphType : ObjectGraphType<string>
if (contentType == null) if (contentType == null)
{ {
var union = builder.GetReferenceUnion(fieldInfo, schemaIds); var union = builder.GetContentUnion(fieldInfo.UnionReferenceType, schemaIds);
if (!union.HasType) if (!union.HasType)
{ {

2
backend/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Contents/FieldVisitor.cs

@ -263,7 +263,7 @@ internal sealed class FieldVisitor : IFieldVisitor<FieldGraphSchema, FieldInfo>
if (contentType == null) if (contentType == null)
{ {
var union = builder.GetReferenceUnion(fieldInfo, schemaIds); var union = builder.GetContentUnion(fieldInfo.UnionReferenceType, schemaIds);
if (!union.HasType) if (!union.HasType)
{ {

4
backend/src/Squidex.Domain.Apps.Entities/Contents/Queries/QueryExecutionContext.cs

@ -90,7 +90,7 @@ public abstract class QueryExecutionContext : Dictionary<string, object?>
return contents; return contents;
} }
public virtual async Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(IEnumerable<DomainId> ids, public virtual async Task<IReadOnlyList<IEnrichedAssetEntity>> QueryAssetsByIdsAsync(IEnumerable<DomainId> ids,
CancellationToken ct) CancellationToken ct)
{ {
Guard.NotNull(ids); Guard.NotNull(ids);
@ -111,7 +111,7 @@ public abstract class QueryExecutionContext : Dictionary<string, object?>
}); });
} }
public virtual async Task<IReadOnlyList<IEnrichedContentEntity>> GetReferencedContentsAsync(IEnumerable<DomainId> ids, public virtual async Task<IReadOnlyList<IEnrichedContentEntity>> QueryContentsByIdsAsync(IEnumerable<DomainId> ids,
CancellationToken ct) CancellationToken ct)
{ {
Guard.NotNull(ids); Guard.NotNull(ids);

24
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLIntrospectionTests.cs

@ -117,7 +117,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_empty_schema() public async Task Should_build_graphql_schema_without_schemas()
{ {
var model = await CreateSut().GetSchemaAsync(TestApp.Default); var model = await CreateSut().GetSchemaAsync(TestApp.Default);
@ -125,7 +125,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_empty_schema_with_empty_schema() public async Task Should_build_graphql_schema_without_schema()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -138,7 +138,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_empty_schema_with_empty_schema_because_ui_field() public async Task Should_build_graphql_schema_on_schema_with_ui_fields_only()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -152,7 +152,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_empty_schema_with_empty_schema_because_invalid_field() public async Task Should_build_graphql_schema_on_schema_with_invalid_fields_only()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -166,7 +166,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_empty_schema_with_unpublished_schema() public async Task Should_build_graphql_schema_on_unpublished_schema()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -180,7 +180,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_schema_with_reserved_schema_name() public async Task Should_build_graphql_schema_on_reserved_schema_name()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -194,7 +194,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_schema_with_reserved_field_name() public async Task Should_build_graphql_schema_with_reserved_field_name()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -210,7 +210,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_schema_with_invalid_field_name() public async Task Should_build_graphql_schema_on_invalid_field_name()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -226,7 +226,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_create_schema_with_duplicate_field_names() public async Task Should_build_graphql_schema_on_duplicate_field_name()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -244,7 +244,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_not_create_schema_With_invalid_component() public async Task Should_not_build_grapqhl_schema_on_invalid_component()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -262,7 +262,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_not_create_schema_With_invalid_components() public async Task Should_not_build_grapqhl_schema_on_invalid_components()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,
@ -280,7 +280,7 @@ public class GraphQLIntrospectionTests : GraphQLTestBase
} }
[Fact] [Fact]
public async Task Should_not_create_schema_With_invalid_references() public async Task Should_not_build_grapqhl_schema_on_invalid_references()
{ {
var schema = var schema =
Mocks.Schema(TestApp.DefaultId, Mocks.Schema(TestApp.DefaultId,

2
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLMutationTests.cs

@ -27,7 +27,7 @@ public class GraphQLMutationTests : GraphQLTestBase
{ {
content = TestContent.Create(contentId, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id, null); content = TestContent.Create(contentId, TestSchemas.Ref1.Id, TestSchemas.Ref2.Id, null);
A.CallTo(() => commandBus.PublishAsync(A<ICommand>.Ignored, A<CancellationToken>._)) A.CallTo(() => commandBus.PublishAsync(A<ICommand>.Ignored,A<CancellationToken>._))
.Returns(commandContext); .Returns(commandContext);
} }

186
backend/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -47,6 +47,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
[Fact] [Fact]
public async Task Should_query_contents_with_full_text() public async Task Should_query_contents_with_full_text()
{ {
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@" var query = CreateQuery(@"
query { query {
queryMySchemaContents(search: ""Hello"") { queryMySchemaContents(search: ""Hello"") {
@ -54,11 +57,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
} }
}"); }");
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$skip=0&$search=\"Hello\"" && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$skip=0&$search=\"Hello\"" && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, content)); .Returns(ResultList.CreateFrom(0, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -77,6 +78,93 @@ public class GraphQLQueriesTests : GraphQLTestBase
AssertResult(expected, actual); AssertResult(expected, actual);
} }
[Fact]
public async Task Should_query_contents_with_ids()
{
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@"
query {
queryContents(ids: [""<ID>""]) {
... on Content {
id
}
... on MySchema {
flatData {
myNumber
}
}
}
}", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIds(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
var expected = new
{
data = new
{
queryContents = new[]
{
new
{
id = contentId,
flatData = new
{
myNumber = 1
}
}
}
}
};
AssertResult(expected, actual);
}
[Fact]
public async Task Should_query_contents_with_ids_and_dynamic_data()
{
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@"
query {
queryContents(ids: [""<ID>""]) {
... on Content {
data: data__dynamic
}
}
}", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIds(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
var expected = new
{
data = new
{
queryContents = new[]
{
new
{
data = content.Data
}
}
}
};
AssertResult(expected, actual);
}
[Fact] [Fact]
public async Task Should_return_multiple_assets_if_querying_assets() public async Task Should_return_multiple_assets_if_querying_assets()
{ {
@ -90,7 +178,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
var asset = TestAsset.Create(DomainId.NewGuid()); var asset = TestAsset.Create(DomainId.NewGuid());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5&$filter=my-query" && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5&$filter=my-query" && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, asset)); .Returns(ResultList.CreateFrom(0, asset));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -125,7 +214,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
var asset = TestAsset.Create(DomainId.NewGuid()); var asset = TestAsset.Create(DomainId.NewGuid());
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5&$filter=my-query" && !x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5&$filter=my-query" && !x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(10, asset)); .Returns(ResultList.CreateFrom(10, asset));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -161,7 +251,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", assetId); }", assetId);
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.HasIdsWithoutTotal(assetId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(assetId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom<IEnrichedAssetEntity>(1)); .Returns(ResultList.CreateFrom<IEnrichedAssetEntity>(1));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -191,7 +282,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", assetId); }", assetId);
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null, A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), null,
A<Q>.That.HasIdsWithoutTotal(assetId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(assetId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, asset)); .Returns(ResultList.CreateFrom(1, asset));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -210,6 +302,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
[Fact] [Fact]
public async Task Should_return_multiple_flat_contents_if_querying_contents() public async Task Should_return_multiple_flat_contents_if_querying_contents()
{ {
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@" var query = CreateQuery(@"
query { query {
queryMySchemaContents(top: 30, skip: 5) { queryMySchemaContents(top: 30, skip: 5) {
@ -217,11 +312,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
} }
}"); }");
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, content)); .Returns(ResultList.CreateFrom(0, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -243,6 +336,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
[Fact] [Fact]
public async Task Should_return_multiple_contents_if_querying_contents() public async Task Should_return_multiple_contents_if_querying_contents()
{ {
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@" var query = CreateQuery(@"
query { query {
queryMySchemaContents(top: 30, skip: 5) { queryMySchemaContents(top: 30, skip: 5) {
@ -250,11 +346,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
} }
}"); }");
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, content)); .Returns(ResultList.CreateFrom(0, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -276,6 +370,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
[Fact] [Fact]
public async Task Should_return_multiple_contents_with_total_if_querying_contents_with_total() public async Task Should_return_multiple_contents_with_total_if_querying_contents_with_total()
{ {
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
var query = CreateQuery(@" var query = CreateQuery(@"
query { query {
queryMySchemaContentsWithTotal(top: 30, skip: 5) { queryMySchemaContentsWithTotal(top: 30, skip: 5) {
@ -286,11 +383,9 @@ public class GraphQLQueriesTests : GraphQLTestBase
} }
}"); }");
var contentId = DomainId.NewGuid();
var content = TestContent.Create(contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && !x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && !x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(10, content)); .Returns(ResultList.CreateFrom(10, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -326,7 +421,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom<IEnrichedContentEntity>(1)); .Returns(ResultList.CreateFrom<IEnrichedContentEntity>(1));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -356,7 +452,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(10, content)); .Returns(ResultList.CreateFrom(10, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -386,7 +483,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -415,7 +513,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
} }
}", contentId); }", contentId);
A.CallTo(() => contentQuery.FindAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), contentId, 3, A<CancellationToken>._)) A.CallTo(() => contentQuery.FindAsync(MatchsContentContext(), TestSchemas.Default.Id.ToString(), contentId, 3,
A<CancellationToken>._))
.Returns(content); .Returns(content);
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -467,11 +566,13 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentRefId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentRefId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, contentRef)); .Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -543,11 +644,13 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentRefId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentRefId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, contentRef)); .Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -608,11 +711,13 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentRefId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentRefId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, contentRef)); .Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -663,11 +768,13 @@ public class GraphQLQueriesTests : GraphQLTestBase
}", contentId); }", contentId);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentRefId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentRefId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(0, contentRef)); .Returns(ResultList.CreateFrom(0, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual1 = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual1 = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -698,7 +805,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
AssertResult(expected, actual2); AssertResult(expected, actual2);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(),
A<Q>.That.HasIdsWithoutTotal(contentRefId), A<CancellationToken>._)) A<Q>.That.HasIdsWithoutTotal(contentRefId),
A<CancellationToken>._))
.MustHaveHappenedOnceExactly(); .MustHaveHappenedOnceExactly();
} }
@ -731,7 +839,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
.Returns(ResultList.CreateFrom(1, contentRef)); .Returns(ResultList.CreateFrom(1, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Reference == contentRefId && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Reference == contentRefId && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -796,7 +905,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
.Returns(ResultList.CreateFrom(1, contentRef)); .Returns(ResultList.CreateFrom(1, contentRef));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), content.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Reference == contentRefId && !x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Reference == contentRefId && !x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(10, content)); .Returns(ResultList.CreateFrom(10, content));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -857,7 +967,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), contentRef.SchemaId.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), contentRef.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Referencing == contentId && x.NoTotal), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Referencing == contentId && x.NoTotal),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(1, contentRef)); .Returns(ResultList.CreateFrom(1, contentRef));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });
@ -910,7 +1021,8 @@ public class GraphQLQueriesTests : GraphQLTestBase
.Returns(ResultList.CreateFrom(1, content)); .Returns(ResultList.CreateFrom(1, content));
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), contentRef.SchemaId.Id.ToString(), A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), contentRef.SchemaId.Id.ToString(),
A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Referencing == contentId), A<CancellationToken>._)) A<Q>.That.Matches(x => x.QueryAsOdata == "?$top=30&$skip=5" && x.Referencing == contentId),
A<CancellationToken>._))
.Returns(ResultList.CreateFrom(10, contentRef)); .Returns(ResultList.CreateFrom(10, contentRef));
var actual = await ExecuteAsync(new ExecutionOptions { Query = query }); var actual = await ExecuteAsync(new ExecutionOptions { Query = query });

50
tools/TestSuite/TestSuite.ApiTests/GraphQLFixture.cs

@ -14,6 +14,21 @@ namespace TestSuite.ApiTests;
public sealed class GraphQLFixture : ContentFixture public sealed class GraphQLFixture : ContentFixture
{ {
public IContentsClient<DynamicEntity, object> Cities
{
get => Client.Contents<DynamicEntity, object>("cities");
}
public IContentsClient<DynamicEntity, object> Countries
{
get => Client.Contents<DynamicEntity, object>("countries");
}
public IContentsClient<DynamicEntity, object> States
{
get => Client.Contents<DynamicEntity, object>("states");
}
public sealed class DynamicEntity : Content<object> public sealed class DynamicEntity : Content<object>
{ {
} }
@ -121,17 +136,16 @@ public sealed class GraphQLFixture : ContentFixture
private async Task CreateContentsAsync() private async Task CreateContentsAsync()
{ {
var countriesClient = Client.Contents<DynamicEntity, object>("countries"); var countries = await Countries.GetAsync();
var countriesResponse = await countriesClient.GetAsync();
if (countriesResponse.Total > 0) if (countries.Total > 0)
{ {
return; return;
} }
async Task<string> CreateCityAsync(string name) async Task<string> CreateCityAsync(string name)
{ {
var citySAData = new var cityData = new
{ {
name = new name = new
{ {
@ -139,15 +153,14 @@ public sealed class GraphQLFixture : ContentFixture
} }
}; };
var citiesClient = Client.Contents<DynamicEntity, object>("cities"); var city = await Cities.CreateAsync(cityData, ContentCreateOptions.AsPublish);
var cityResponse = await citiesClient.CreateAsync(citySAData, ContentCreateOptions.AsPublish);
return cityResponse.Id; return city.Id;
} }
async Task<string> CreateStateAsync(string name, string cityId) async Task<string> CreateStateAsync(string name, string cityId)
{ {
var citySAData = new var stateData = new
{ {
name = new name = new
{ {
@ -159,20 +172,19 @@ public sealed class GraphQLFixture : ContentFixture
} }
}; };
var statesClient = Client.Contents<DynamicEntity, object>("states"); var state = await States.CreateAsync(stateData, ContentCreateOptions.AsPublish);
var stateResponse = await statesClient.CreateAsync(citySAData, ContentCreateOptions.AsPublish);
return stateResponse.Id; return state.Id;
} }
// STEP 1: Create state. 1 // STEP 1: Create state 1.
var sachsenCapital = await CreateCityAsync("Leipzig"); var saxonyCapital = await CreateCityAsync("Leipzig");
var sachstenState = await CreateStateAsync("Sachsen", sachsenCapital); var saxonyState = await CreateStateAsync("Saxony", saxonyCapital);
// STEP 1: Create state. 2 // STEP 1: Create state 2.
var badenWCapital = await CreateCityAsync("Stuttgart"); var bavariaCapital = await CreateCityAsync("Munich");
var badenWState = await CreateStateAsync("Baden Württemberg", badenWCapital); var bavariaState = await CreateStateAsync("Bavaria", bavariaCapital);
// STEP 3: Create country. // STEP 3: Create country.
@ -184,10 +196,10 @@ public sealed class GraphQLFixture : ContentFixture
}, },
states = new states = new
{ {
iv = new[] { sachstenState, badenWState } iv = new[] { saxonyState, bavariaState }
} }
}; };
await countriesClient.CreateAsync(countryData, ContentCreateOptions.AsPublish); await Countries.CreateAsync(countryData, ContentCreateOptions.AsPublish);
} }
} }

64
tools/TestSuite/TestSuite.ApiTests/GraphQLTests.cs

@ -62,7 +62,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
} }
[Fact] [Fact]
public async Task Should_query_graphql_reference_selectors() public async Task Should_query_graphql_by_reference_selector()
{ {
var query = new var query = new
{ {
@ -94,11 +94,11 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x.Data.Name) .Select(x => x.Data.Name)
.Order(); .Order();
Assert.Equal(new[] { "Leipzig", "Stuttgart" }, cityNames); Assert.Equal(new[] { "Leipzig", "Munich" }, cityNames);
} }
[Fact] [Fact]
public async Task Should_query_graphql_reference_operator() public async Task Should_query_graphql_by_references_function()
{ {
var query = new var query = new
{ {
@ -131,11 +131,11 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]["name"].Value<string>()) .Select(x => x["data"]["name"].Value<string>())
.Order(); .Order();
Assert.Equal(new[] { "Leipzig", "Stuttgart" }, cityNames); Assert.Equal(new[] { "Leipzig", "Munich" }, cityNames);
} }
[Fact] [Fact]
public async Task Should_query_graphql_reference_operator_with_filter() public async Task Should_query_graphql_by_references_function_and_filter()
{ {
var query = new var query = new
{ {
@ -172,7 +172,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
} }
[Fact] [Fact]
public async Task Should_query_graphql_referencing_operator() public async Task Should_query_graphql_by_referencing_function()
{ {
var query = new var query = new
{ {
@ -196,18 +196,18 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]["name"].Value<string>()) .Select(x => x["data"]["name"].Value<string>())
.Order(); .Order();
Assert.Equal(new[] { "Baden Württemberg", "Sachsen" }, stateNames); Assert.Equal(new[] { "Bavaria", "Saxony" }, stateNames);
} }
[Fact] [Fact]
public async Task Should_query_graphql_referencing_operator_with_filter() public async Task Should_query_graphql_by_referencing_function_and_filter()
{ {
var query = new var query = new
{ {
query = @" query = @"
{ {
cities: queryCitiesContents { cities: queryCitiesContents {
states: referencingStatesContents(filter: ""data/name/iv eq 'Sachsen'"") { states: referencingStatesContents(filter: ""data/name/iv eq 'Saxony'"") {
data: flatData { data: flatData {
name name
} }
@ -224,7 +224,7 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
.Select(x => x["data"]["name"].Value<string>()) .Select(x => x["data"]["name"].Value<string>())
.Order(); .Order();
Assert.Equal(new[] { "Sachsen" }, stateNames); Assert.Equal(new[] { "Saxony" }, stateNames);
} }
[Fact] [Fact]
@ -248,6 +248,50 @@ public sealed class GraphQLTests : IClassFixture<GraphQLFixture>
Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType);
} }
[Fact]
public async Task Should_query_graphql_by_ids()
{
var allCities = await _.Cities.GetAsync();
var allStates = await _.States.GetAsync();
var ids = allCities.Items.Select(x => x.Id).Union(allStates.Items.Select(x => x.Id)).ToList();
var query = new
{
query = @"
query ContentsQuery($ids: [String!]!) {
queryContents(ids: $ids) {
... on Content {
id
}
... on Cities {
data: flatData {
name
}
}
... on States {
data: flatData {
name
}
}
}
}",
variables = new
{
ids
}
};
var result = await _.Client.SharedDynamicContents.GraphQlAsync<JToken>(query);
var names =
result["queryContents"]
.Select(x => x["data"]["name"].Value<string>())
.Order();
Assert.Equal(new[] { "Bavaria", "Leipzig", "Munich", "Saxony" }, names);
}
[Fact] [Fact]
public async Task Should_return_correct_vary_headers() public async Task Should_return_correct_vary_headers()
{ {

Loading…
Cancel
Save