Browse Source

Several fixes

pull/65/head
Sebastian Stehle 9 years ago
parent
commit
3b85fedd2b
  1. 116
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs
  2. 4
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs
  3. 16
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentDataGraphType.cs
  4. 17
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentGraphType.cs
  5. 12
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs
  6. 37
      src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/NoopGraphType.cs
  7. 2
      src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts
  8. 607
      tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs
  9. 48
      tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeAssetEntity.cs
  10. 36
      tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeContentEntity.cs

116
src/Squidex.Domain.Apps.Read/Contents/GraphQL/GraphQLModel.cs

@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
public sealed class GraphQLModel : IGraphQLContext public sealed class GraphQLModel : IGraphQLContext
{ {
private readonly Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>> fieldInfos; private readonly Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>> fieldInfos;
private readonly Dictionary<Guid, IGraphType> schemaTypes = new Dictionary<Guid, IGraphType>(); private readonly Dictionary<Guid, ContentGraphType> schemaTypes = new Dictionary<Guid, ContentGraphType>();
private readonly Dictionary<Guid, ISchemaEntity> schemas; private readonly Dictionary<Guid, ISchemaEntity> schemas;
private readonly PartitionResolver partitionResolver; private readonly PartitionResolver partitionResolver;
private readonly IGraphType assetType = new AssetGraphType(); private readonly IGraphType assetType = new AssetGraphType();
@ -45,97 +45,93 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
IGraphType assetListType = new ListGraphType(new NonNullGraphType(assetType)); IGraphType assetListType = new ListGraphType(new NonNullGraphType(assetType));
var stringInfos =
(new StringGraphType(), defaultResolver);
var booleanInfos =
(new BooleanGraphType(), defaultResolver);
var numberInfos =
(new FloatGraphType(), defaultResolver);
var dateTimeInfos =
(new DateGraphType(), defaultResolver);
var jsonInfos =
(new JsonScalarType(), defaultResolver);
var geolocationInfos =
(new GeolocationScalarType(), defaultResolver);
fieldInfos = new Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>> fieldInfos = new Dictionary<Type, Func<Field, (IGraphType ResolveType, IFieldResolver Resolver)>>
{ {
{ {
typeof(StringField), typeof(StringField),
field => stringInfos field => ResolveDefault("String")
}, },
{ {
typeof(BooleanField), typeof(BooleanField),
field => booleanInfos field => ResolveDefault("Boolean")
}, },
{ {
typeof(NumberField), typeof(NumberField),
field => numberInfos field => ResolveDefault("Float")
}, },
{ {
typeof(DateTimeField), typeof(DateTimeField),
field => dateTimeInfos field => ResolveDefault("Date")
}, },
{ {
typeof(JsonField), typeof(JsonField),
field => jsonInfos field => ResolveDefault("Json")
}, },
{ {
typeof(GeolocationField), typeof(GeolocationField),
field => geolocationInfos field => ResolveDefault("Geolocation")
}, },
{ {
typeof(AssetsField), typeof(AssetsField),
field => field => { return ResolveAssets(assetListType); }
{
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
{
var context = (QueryContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
return context.GetReferencedAssets(contentIds);
});
return (assetListType, resolver);
}
}, },
{ {
typeof(ReferencesField), typeof(ReferencesField),
field => field => { return ResolveReferences(field); }
{ }
var schemaId = ((ReferencesField)field).Properties.SchemaId; };
var schemaType = GetSchemaType(schemaId);
if (schemaType == null) this.schemas = schemas.ToDictionary(x => x.Id);
{
return (null, null); graphQLSchema = new GraphQLSchema { Query = new ContentQueryGraphType(this, this.schemas.Values) };
}
var resolver = new FuncFieldResolver<ContentFieldData, object>(c => foreach (var schemaType in schemaTypes.Values)
{ {
var context = (QueryContext)c.UserContext; schemaType.Initialize();
var contentIds = c.Source.GetOrDefault(c.FieldName); }
}
return context.GetReferencedContents(schemaId, contentIds); private (IGraphType ResolveType, IFieldResolver Resolver) ResolveDefault(string name)
}); {
return (new NoopGraphType(name), new FuncFieldResolver<ContentFieldData, object>(c => c.Source.GetOrDefault(c.FieldName)));
}
var schemaFieldType = new ListGraphType(new NonNullGraphType(GetSchemaType(schemaId))); private static ValueTuple<IGraphType, IFieldResolver> ResolveAssets(IGraphType assetListType)
{
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
{
var context = (QueryContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
return (schemaFieldType, resolver); return context.GetReferencedAssets(contentIds);
} });
}
};
this.schemas = schemas.ToDictionary(x => x.Id); return (assetListType, resolver);
graphQLSchema = new GraphQLSchema { Query = new ContentQueryType(this, this.schemas.Values) };
} }
private ValueTuple<IGraphType, IFieldResolver> ResolveReferences(Field field)
{
var schemaId = ((ReferencesField)field).Properties.SchemaId;
var schemaType = GetSchemaType(schemaId);
if (schemaType == null)
{
return (null, null);
}
var resolver = new FuncFieldResolver<ContentFieldData, object>(c =>
{
var context = (QueryContext)c.UserContext;
var contentIds = c.Source.GetOrDefault(c.FieldName);
return context.GetReferencedContents(schemaId, contentIds);
});
var schemaFieldType = new ListGraphType(new NonNullGraphType(GetSchemaType(schemaId)));
return (schemaFieldType, resolver);
}
public async Task<object> ExecuteAsync(QueryContext context, GraphQLQuery query) public async Task<object> ExecuteAsync(QueryContext context, GraphQLQuery query)
{ {
Guard.NotNull(context, nameof(context)); Guard.NotNull(context, nameof(context));

4
src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs

@ -117,7 +117,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{ {
Task.Run(async () => Task.Run(async () =>
{ {
var assets = await assetRepository.QueryAsync(app.Id, null, notLoadedAssets, string.Empty, int.MaxValue).ConfigureAwait(false); var assets = await assetRepository.QueryAsync(app.Id, null, notLoadedAssets, null, int.MaxValue).ConfigureAwait(false);
foreach (var asset in assets) foreach (var asset in assets)
{ {
@ -146,7 +146,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL
{ {
Task.Run(async () => Task.Run(async () =>
{ {
var contents = await contentRepository.QueryAsync(app, schemaId, false, notLoadedContents, string.Empty).ConfigureAwait(false); var contents = await contentRepository.QueryAsync(app, schemaId, false, notLoadedContents, null).ConfigureAwait(false);
foreach (var content in contents) foreach (var content in contents)
{ {

16
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -19,10 +19,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{ {
public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData> public sealed class ContentDataGraphType : ObjectGraphType<NamedContentData>
{ {
private static readonly IFieldResolver FieldResolver = public ContentDataGraphType(Schema schema, IGraphQLContext context)
new FuncFieldResolver<NamedContentData, ContentFieldData>(c => c.Source.GetOrDefault(c.FieldName));
public ContentDataGraphType(Schema schema, IGraphQLContext graphQLContext)
{ {
var schemaName = schema.Properties.Label.WithFallback(schema.Name); var schemaName = schema.Properties.Label.WithFallback(schema.Name);
@ -30,7 +27,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
foreach (var field in schema.Fields.Where(x => !x.IsHidden)) foreach (var field in schema.Fields.Where(x => !x.IsHidden))
{ {
var fieldInfo = graphQLContext.GetGraphType(field); var fieldInfo = context.GetGraphType(field);
if (fieldInfo.ResolveType != null) if (fieldInfo.ResolveType != null)
{ {
@ -40,7 +37,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
Name = $"{schema.Name.ToPascalCase()}Data{field.Name.ToPascalCase()}Dto" Name = $"{schema.Name.ToPascalCase()}Data{field.Name.ToPascalCase()}Dto"
}; };
var partition = graphQLContext.ResolvePartition(field.Paritioning); var partition = context.ResolvePartition(field.Paritioning);
foreach (var partitionItem in partition) foreach (var partitionItem in partition)
{ {
@ -55,10 +52,13 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
fieldGraphType.Description = $"The structure of the {fieldName} of a {schemaName} content type."; fieldGraphType.Description = $"The structure of the {fieldName} of a {schemaName} content type.";
var fieldResolver =
new FuncFieldResolver<NamedContentData, ContentFieldData>(c => c.Source.GetOrDefault(field.Name));
AddField(new FieldType AddField(new FieldType
{ {
Name = field.Name, Name = field.Name.ToCamelCase(),
Resolver = FieldResolver, Resolver = fieldResolver,
ResolvedType = fieldGraphType ResolvedType = fieldGraphType
}); });
} }

17
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentGraphType.cs

@ -16,11 +16,20 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{ {
public sealed class ContentGraphType : ObjectGraphType<IContentEntity> public sealed class ContentGraphType : ObjectGraphType<IContentEntity>
{ {
public ContentGraphType(Schema schema, IGraphQLContext graphQLContext) private readonly Schema schema;
private readonly IGraphQLContext context;
public ContentGraphType(Schema schema, IGraphQLContext context)
{ {
var schemaName = schema.Properties.Label.WithFallback(schema.Name); this.schema = schema;
this.context = context;
Name = $"{schema.Name.ToPascalCase()}Dto"; Name = $"{schema.Name.ToPascalCase()}Dto";
}
public void Initialize()
{
var schemaName = schema.Properties.Label.WithFallback(schema.Name);
AddField(new FieldType AddField(new FieldType
{ {
@ -69,12 +78,12 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
ResolvedType = new NonNullGraphType(new StringGraphType()), ResolvedType = new NonNullGraphType(new StringGraphType()),
Description = $"The user that has updated the {schemaName} content last." Description = $"The user that has updated the {schemaName} content last."
}); });
AddField(new FieldType AddField(new FieldType
{ {
Name = "data", Name = "data",
Resolver = Resolver(x => x.Data), Resolver = Resolver(x => x.Data),
ResolvedType = new NonNullGraphType(new ContentDataGraphType(schema, graphQLContext)), ResolvedType = new NonNullGraphType(new ContentDataGraphType(schema, context)),
Description = $"The data of the {schemaName} content." Description = $"The data of the {schemaName} content."
}); });

12
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryType.cs → src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/ContentQueryGraphType.cs

@ -16,9 +16,9 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{ {
public sealed class ContentQueryType : ObjectGraphType public sealed class ContentQueryGraphType : ObjectGraphType
{ {
public ContentQueryType(IGraphQLContext graphQLContext, IEnumerable<ISchemaEntity> schemaEntities) public ContentQueryGraphType(IGraphQLContext graphQLContext, IEnumerable<ISchemaEntity> schemaEntities)
{ {
AddAssetFind(graphQLContext); AddAssetFind(graphQLContext);
AddAssetsQuery(graphQLContext); AddAssetsQuery(graphQLContext);
@ -26,9 +26,9 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
foreach (var schemaEntity in schemaEntities) foreach (var schemaEntity in schemaEntities)
{ {
var schemaName = schemaEntity.Schema.Properties.Label.WithFallback(schemaEntity.Schema.Name); var schemaName = schemaEntity.Schema.Properties.Label.WithFallback(schemaEntity.Schema.Name);
var schemaType = new ContentGraphType(schemaEntity.Schema, graphQLContext); var schemaType = graphQLContext.GetSchemaType(schemaEntity.Id);
AddContentFind(schemaEntity, schemaName, schemaType); AddContentFind(schemaEntity, schemaType, schemaName);
AddContentQuery(schemaEntity, schemaType, schemaName); AddContentQuery(schemaEntity, schemaType, schemaName);
} }
@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
}); });
} }
private void AddContentFind(ISchemaEntity schemaEntity, string schemaName, IGraphType schemaType) private void AddContentFind(ISchemaEntity schemaEntity, IGraphType schemaType, string schemaName)
{ {
AddField(new FieldType AddField(new FieldType
{ {
@ -120,7 +120,7 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
var argTop = c.GetArgument("top", 20); var argTop = c.GetArgument("top", 20);
var argSkip = c.GetArgument("skip", 0); var argSkip = c.GetArgument("skip", 0);
var argQuery = c.GetArgument("query", string.Empty); var argQuery = c.GetArgument("search", string.Empty);
return context.QueryAssetsAsync(argQuery, argSkip, argTop); return context.QueryAssetsAsync(argQuery, argSkip, argTop);
}), }),

37
src/Squidex.Domain.Apps.Read/Contents/GraphQL/Types/NoopGraphType.cs

@ -0,0 +1,37 @@
// ==========================================================================
// NoopGraphType.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using GraphQL.Language.AST;
using GraphQL.Types;
namespace Squidex.Domain.Apps.Read.Contents.GraphQL.Types
{
public sealed class NoopGraphType : ScalarGraphType
{
public NoopGraphType(string name)
{
Name = name;
}
public override object Serialize(object value)
{
return value;
}
public override object ParseValue(object value)
{
return value;
}
public override object ParseLiteral(IValue value)
{
throw new NotSupportedException();
}
}
}

2
src/Squidex/app/features/schemas/pages/schema/schema-page.component.ts

@ -71,7 +71,7 @@ export class SchemaPageComponent extends AppComponentBase implements OnInit {
name: ['', name: ['',
[ [
Validators.maxLength(40), Validators.maxLength(40),
ValidatorsEx.pattern('[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*', 'Name must be a valid javascript name in camel case.') ValidatorsEx.pattern('[a-z0-9]+(\\-[a-zA-Z0-9]+)*', 'Name must be a valid javascript name in camel case.')
]], ]],
isLocalizable: [false] isLocalizable: [false]
}); });

607
tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs

@ -0,0 +1,607 @@
// ==========================================================================
// GraphQLTests.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Read.Apps;
using Squidex.Domain.Apps.Read.Assets.Repositories;
using Squidex.Domain.Apps.Read.Contents.GraphQL;
using Squidex.Domain.Apps.Read.Contents.Repositories;
using Squidex.Domain.Apps.Read.Contents.TestData;
using Squidex.Domain.Apps.Read.Schemas;
using Squidex.Domain.Apps.Read.Schemas.Repositories;
using Xunit;
using NodaTime.Extensions;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents
{
public class GraphQLTests
{
private static readonly Guid schemaId = Guid.NewGuid();
private static readonly Guid appId = Guid.NewGuid();
private readonly Schema schema =
Schema.Create("my-schema", new SchemaProperties())
.AddOrUpdateField(new JsonField(1, "my-json", Partitioning.Invariant,
new JsonFieldProperties()))
.AddOrUpdateField(new StringField(2, "my-string", Partitioning.Language,
new StringFieldProperties()))
.AddOrUpdateField(new NumberField(3, "my-number", Partitioning.Invariant,
new NumberFieldProperties()))
.AddOrUpdateField(new AssetsField(4, "my-assets", Partitioning.Invariant,
new AssetsFieldProperties()))
.AddOrUpdateField(new BooleanField(5, "my-boolean", Partitioning.Invariant,
new BooleanFieldProperties()))
.AddOrUpdateField(new DateTimeField(6, "my-datetime", Partitioning.Invariant,
new DateTimeFieldProperties()))
.AddOrUpdateField(new ReferencesField(7, "my-references", Partitioning.Invariant,
new ReferencesFieldProperties { SchemaId = schemaId }))
.AddOrUpdateField(new GeolocationField(8, "my-geolocation", Partitioning.Invariant,
new GeolocationFieldProperties()));
private readonly Mock<ISchemaRepository> schemaRepository = new Mock<ISchemaRepository>();
private readonly Mock<IContentRepository> contentRepository = new Mock<IContentRepository>();
private readonly Mock<IAssetRepository> assetRepository = new Mock<IAssetRepository>();
private readonly IAppEntity app;
private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
private readonly IGraphQLInvoker sut;
public GraphQLTests()
{
var appEntity = new Mock<IAppEntity>();
appEntity.Setup(x => x.Id).Returns(appId);
appEntity.Setup(x => x.PartitionResolver).Returns(x => InvariantPartitioning.Instance);
app = appEntity.Object;
var schemaEntity = new Mock<ISchemaEntity>();
schemaEntity.Setup(x => x.Id).Returns(schemaId);
schemaEntity.Setup(x => x.Name).Returns(schema.Name);
schemaEntity.Setup(x => x.Schema).Returns(schema);
schemaEntity.Setup(x => x.IsPublished).Returns(true);
var schemas = new List<ISchemaEntity> { schemaEntity.Object };
schemaRepository.Setup(x => x.QueryAllAsync(appId)).Returns(Task.FromResult<IReadOnlyList<ISchemaEntity>>(schemas));
sut = new CachingGraphQLInvoker(cache, schemaRepository.Object, assetRepository.Object, contentRepository.Object);
}
[Fact]
public async Task Should_make_assets_request()
{
const string query = @"
query {
queryAssets(search: ""my-query"", top: 30, skip: 5) {
id
version
created
createdBy
lastModified
lastModifiedBy
mimeType
fileName
fileSize
fileVersion
isImage
pixelWidth,
pixelHeight
}
}";
var assetEntity = CreateAsset(Guid.NewGuid());
var assets = new List<IAssetEntity> { assetEntity };
assetRepository.Setup(x => x.QueryAsync(app.Id, null, null, "my-query", 30, 5))
.Returns(Task.FromResult<IReadOnlyList<IAssetEntity>>(assets))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
queryAssets = new dynamic[]
{
new
{
id = assetEntity.Id,
version = 1,
created = assetEntity.Created.ToDateTimeUtc(),
createdBy = "subject:user1",
lastModified = assetEntity.LastModified.ToDateTimeUtc(),
lastModifiedBy = "subject:user2",
mimeType = "image/png",
fileName = "MyFile.png",
fileSize = 1024,
fileVersion = 123,
isImage = true,
pixelWidth = 800,
pixelHeight = 600
}
}
}
};
AssertJson(expected, result);
assetRepository.VerifyAll();
}
[Fact]
public async Task Should_make_asset_request()
{
var assetId = Guid.NewGuid();
var assetEntity = CreateAsset(Guid.NewGuid());
var query = $@"
query {{
findAsset(id: ""{assetId}"") {{
id
version
created
createdBy
lastModified
lastModifiedBy
mimeType
fileName
fileSize
fileVersion
isImage
pixelWidth,
pixelHeight
}}
}}";
assetRepository.Setup(x => x.FindAssetAsync(assetId))
.Returns(Task.FromResult(assetEntity))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findAsset = new
{
id = assetEntity.Id,
version = 1,
created = assetEntity.Created.ToDateTimeUtc(),
createdBy = "subject:user1",
lastModified = assetEntity.LastModified.ToDateTimeUtc(),
lastModifiedBy = "subject:user2",
mimeType = "image/png",
fileName = "MyFile.png",
fileSize = 1024,
fileVersion = 123,
isImage = true,
pixelWidth = 800,
pixelHeight = 600
}
}
};
AssertJson(expected, result);
assetRepository.VerifyAll();
}
[Fact]
public async Task Should_make_contents_request()
{
const string query = @"
query {
queryMySchemaContents(top: 30, skip: 5) {
id
version
created
createdBy
lastModified
lastModifiedBy
data {
myString {
iv
}
myNumber {
iv
}
myBoolean {
iv
}
myDatetime {
iv
}
myJson {
iv
}
myGeolocation {
iv
}
}
}
}";
var contentEntity = CreateContent(Guid.NewGuid(), Guid.Empty, Guid.Empty);
var contents = new List<IContentEntity> { contentEntity };
contentRepository.Setup(x => x.QueryAsync(app, schemaId, false, null, "?$top=30&$skip=5"))
.Returns(Task.FromResult<IReadOnlyList<IContentEntity>>(contents))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
queryMySchemaContents = new dynamic[]
{
new
{
id = contentEntity.Id,
version = 1,
created = contentEntity.Created.ToDateTimeUtc(),
createdBy = "subject:user1",
lastModified = contentEntity.LastModified.ToDateTimeUtc(),
lastModifiedBy = "subject:user2",
data = new
{
myString = new
{
iv = "value"
},
myNumber = new
{
iv = 1
},
myBoolean = new
{
iv = true
},
myDatetime = new
{
iv = contentEntity.LastModified.ToDateTimeUtc()
},
myJson = new
{
iv = new
{
value = 1
}
},
myGeolocation = new
{
iv = new
{
latitude = 10,
longitude = 20
}
}
}
}
}
}
};
AssertJson(expected, result);
contentRepository.VerifyAll();
}
[Fact]
public async Task Should_make_content_request()
{
var contentId = Guid.NewGuid();
var contentEntity = CreateContent(contentId, Guid.Empty, Guid.Empty);
var query = $@"
query {{
findMySchemaContent(id: ""{contentId}"") {{
id
version
created
createdBy
lastModified
lastModifiedBy
data {{
myString {{
iv
}}
myNumber {{
iv
}}
myBoolean {{
iv
}}
myDatetime {{
iv
}}
myJson {{
iv
}}
myGeolocation {{
iv
}}
}}
}}
}}";
contentRepository.Setup(x => x.FindContentAsync(app, schemaId, contentId))
.Returns(Task.FromResult(contentEntity))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findMySchemaContent = new
{
id = contentEntity.Id,
version = 1,
created = contentEntity.Created.ToDateTimeUtc(),
createdBy = "subject:user1",
lastModified = contentEntity.LastModified.ToDateTimeUtc(),
lastModifiedBy = "subject:user2",
data = new
{
myString = new
{
iv = "value"
},
myNumber = new
{
iv = 1
},
myBoolean = new
{
iv = true
},
myDatetime = new
{
iv = contentEntity.LastModified.ToDateTimeUtc()
},
myJson = new
{
iv = new
{
value = 1
}
},
myGeolocation = new
{
iv = new
{
latitude = 10,
longitude = 20
}
}
}
}
}
};
AssertJson(expected, result);
contentRepository.VerifyAll();
}
[Fact]
public async Task Should_make_content_request_and_resolve_references()
{
var contentRefId = Guid.NewGuid();
var contentRefEntity = CreateContent(contentRefId, Guid.Empty, Guid.Empty);
var contentId = Guid.NewGuid();
var contentEntity = CreateContent(contentId, contentRefId, Guid.Empty);
var query = $@"
query {{
findMySchemaContent(id: ""{contentId}"") {{
id
data {{
myReferences {{
iv {{
id
}}
}}
}}
}}
}}";
var refContents = new List<IContentEntity> { contentRefEntity };
contentRepository.Setup(x => x.FindContentAsync(app, schemaId, contentId))
.Returns(Task.FromResult(contentEntity))
.Verifiable();
contentRepository.Setup(x => x.QueryAsync(app, schemaId, false, new HashSet<Guid> {contentRefId }, null))
.Returns(Task.FromResult<IReadOnlyList<IContentEntity>>(refContents))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findMySchemaContent = new
{
id = contentEntity.Id,
data = new
{
myReferences = new
{
iv = new[]
{
new
{
id = contentRefId
}
}
}
}
}
}
};
AssertJson(expected, result);
contentRepository.VerifyAll();
}
[Fact]
public async Task Should_make_content_request_and_resolve_assets()
{
var assetRefId = Guid.NewGuid();
var assetRefEntity = CreateAsset(assetRefId);
var contentId = Guid.NewGuid();
var contentEntity = CreateContent(contentId, Guid.Empty, assetRefId);
var query = $@"
query {{
findMySchemaContent(id: ""{contentId}"") {{
id
data {{
myAssets {{
iv {{
id
}}
}}
}}
}}
}}";
var refAssets = new List<IAssetEntity> { assetRefEntity };
contentRepository.Setup(x => x.FindContentAsync(app, schemaId, contentId))
.Returns(Task.FromResult(contentEntity))
.Verifiable();
assetRepository.Setup(x => x.QueryAsync(app.Id, null, new HashSet<Guid> { assetRefId }, null, int.MaxValue, 0))
.Returns(Task.FromResult<IReadOnlyList<IAssetEntity>>(refAssets))
.Verifiable();
dynamic result = await sut.QueryAsync(app, new GraphQLQuery { Query = query });
var expected = new
{
data = new
{
findMySchemaContent = new
{
id = contentEntity.Id,
data = new
{
myAssets = new
{
iv = new[]
{
new
{
id = assetRefId
}
}
}
}
}
}
};
AssertJson(expected, result);
contentRepository.VerifyAll();
}
private static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId)
{
var now = DateTime.UtcNow.ToInstant();
var data =
new NamedContentData()
.AddField("my-json",
new ContentFieldData().AddValue("iv", JToken.FromObject(new { value = 1 })))
.AddField("my-string",
new ContentFieldData().AddValue("iv", "value"))
.AddField("my-assets",
new ContentFieldData().AddValue("iv", JToken.FromObject(new[] { assetId })))
.AddField("my-number",
new ContentFieldData().AddValue("iv", 1))
.AddField("my-boolean",
new ContentFieldData().AddValue("iv", true))
.AddField("my-datetime",
new ContentFieldData().AddValue("iv", now.ToDateTimeUtc()))
.AddField("my-references",
new ContentFieldData().AddValue("iv", JToken.FromObject(new[] { refId })))
.AddField("my-geolocation",
new ContentFieldData().AddValue("iv", JToken.FromObject(new { latitude = 10, longitude = 20 })));
var contentEntity = new FakeContentEntity
{
Id = id,
Version = 1,
Created = now,
CreatedBy = new RefToken("subject", "user1"),
LastModified = now,
LastModifiedBy = new RefToken("subject", "user2"),
Data = data
};
return contentEntity;
}
private static IAssetEntity CreateAsset(Guid id)
{
var now = DateTime.UtcNow.ToInstant();
var assetEntity = new FakeAssetEntity
{
Id = id,
Version = 1,
Created = now,
CreatedBy = new RefToken("subject", "user1"),
LastModified = now,
LastModifiedBy = new RefToken("subject", "user2"),
FileName = "MyFile.png",
FileSize = 1024,
FileVersion = 123,
MimeType = "image/png",
IsImage = true,
PixelWidth = 800,
PixelHeight = 600
};
return assetEntity;
}
private static void AssertJson(object expected, object result)
{
var resultJson = JsonConvert.SerializeObject(result, Formatting.Indented);
var expectJson = JsonConvert.SerializeObject(expected, Formatting.Indented);
Assert.Equal(expectJson, resultJson);
}
}
}

48
tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeAssetEntity.cs

@ -0,0 +1,48 @@
// ==========================================================================
// MockupAssetEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Read.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.TestData
{
public sealed class FakeAssetEntity : IAssetEntity
{
public Guid Id { get; set; }
public Guid AppId { get; set; }
public Instant Created { get; set; }
public Instant LastModified { get; set; }
public RefToken CreatedBy { get; set; }
public RefToken LastModifiedBy { get; set; }
public long Version { get; set; }
public string MimeType { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public long FileVersion { get; set; }
public bool IsImage { get; set; }
public bool IsDeleted { get; set; }
public int? PixelWidth { get; set; }
public int? PixelHeight { get; set; }
}
}

36
tests/Squidex.Domain.Apps.Read.Tests/Contents/TestData/FakeContentEntity.cs

@ -0,0 +1,36 @@
// ==========================================================================
// FakeContentEntity.cs
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex Group
// All rights reserved.
// ==========================================================================
using System;
using NodaTime;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Read.Contents.TestData
{
public sealed class FakeContentEntity : IContentEntity
{
public Guid Id { get; set; }
public Guid AppId { get; set; }
public Instant Created { get; set; }
public Instant LastModified { get; set; }
public RefToken CreatedBy { get; set; }
public RefToken LastModifiedBy { get; set; }
public long Version { get; set; }
public bool IsPublished { get; set; }
public NamedContentData Data { get; set; }
}
}
Loading…
Cancel
Save