From ac10257a83a84eba362110a48bdabfaa115bb959 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sun, 3 Sep 2017 23:25:54 +0200 Subject: [PATCH] Correct usage of publish state for schemas. --- .../Contents/MongoContentEntity.cs | 4 + .../Contents/MongoContentRepository.cs | 2 +- .../Schemas/MongoSchemaRepository.cs | 4 +- .../Contents/GraphQL/CachingGraphQLService.cs | 31 +++++-- .../Contents/GraphQL/IGraphQLService.cs | 3 +- .../Contents/GraphQL/QueryContext.cs | 65 +++++++++++--- .../Contents/IContentEntity.cs | 2 +- .../Schemas/Repositories/ISchemaRepository.cs | 2 +- .../Implementations/CachingSchemaProvider.cs | 2 +- .../ContentApi/ContentsController.cs | 39 +++++--- .../Contents/GraphQLTests.cs | 88 ++++++++++++++++--- .../Schemas/CachingSchemaProviderTests.cs | 12 +-- .../Contents/ContentCommandHandlerTests.cs | 6 +- .../Schemas/SchemaCommandHandlerTests.cs | 12 +-- .../Webhooks/WebhookCommandMiddlewareTests.cs | 28 +++--- 15 files changed, 221 insertions(+), 79 deletions(-) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs index b282972f2..4afac77cb 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentEntity.cs @@ -94,6 +94,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents { return contentData; } + set + { + contentData = value; + } } public void ParseData(Schema schema) diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs index 152cffae0..c2b169aef 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository.cs @@ -189,7 +189,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Contents { var collection = GetCollection(appId); - var schemaEntity = await schemas.FindSchemaByIdAsync(schemaId, true); + var schemaEntity = await schemas.FindSchemaByIdAsync(schemaId); if (schemaEntity == null) { diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs index 459e81945..361056b7f 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs @@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas return schemaEntity; } - public async Task FindSchemaAsync(Guid schemaId) + public async Task FindSchemaAsync(Guid schemaId, bool provideDeleted = false) { var schemaEntity = await Collection.Find(s => s.Id == schemaId) @@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas schemaEntity?.DeserializeSchema(serializer); - return schemaEntity; + return schemaEntity != null && (provideDeleted || !schemaEntity.IsDeleted) ? schemaEntity : null; } } } diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs index 5a2e32684..7e4fe2026 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/CachingGraphQLService.cs @@ -8,13 +8,16 @@ using System; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Assets.Repositories; using Squidex.Domain.Apps.Read.Contents.Repositories; using Squidex.Domain.Apps.Read.Schemas.Repositories; +using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Read.Utils; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; @@ -30,6 +33,8 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL private readonly IGraphQLUrlGenerator urlGenerator; private readonly IAssetRepository assetRepository; private readonly ISchemaRepository schemaRepository; + private readonly ISchemaProvider schemas; + private readonly IScriptEngine scriptEngine; public string Name { @@ -41,18 +46,28 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL get { return "^(schema-)|(apps-)"; } } - public CachingGraphQLService(IMemoryCache cache, ISchemaRepository schemaRepository, IAssetRepository assetRepository, IContentRepository contentRepository, IGraphQLUrlGenerator urlGenerator) + public CachingGraphQLService(IMemoryCache cache, + IAssetRepository assetRepository, + IContentRepository contentRepository, + IGraphQLUrlGenerator urlGenerator, + ISchemaRepository schemaRepository, + ISchemaProvider schemas, + IScriptEngine scriptEngine) : base(cache) { + Guard.NotNull(contentRepository, nameof(contentRepository)); Guard.NotNull(schemaRepository, nameof(schemaRepository)); Guard.NotNull(assetRepository, nameof(assetRepository)); - Guard.NotNull(contentRepository, nameof(contentRepository)); Guard.NotNull(urlGenerator, nameof(urlGenerator)); + Guard.NotNull(scriptEngine, nameof(scriptEngine)); + Guard.NotNull(schemas, nameof(schemas)); - this.contentRepository = contentRepository; - this.schemaRepository = schemaRepository; this.assetRepository = assetRepository; + this.contentRepository = contentRepository; this.urlGenerator = urlGenerator; + this.schemaRepository = schemaRepository; + this.schemas = schemas; + this.scriptEngine = scriptEngine; } public Task ClearAsync() @@ -70,13 +85,13 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL return TaskHelper.Done; } - public async Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, GraphQLQuery query) + public async Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, ClaimsPrincipal user, GraphQLQuery query) { Guard.NotNull(app, nameof(app)); Guard.NotNull(query, nameof(query)); var modelContext = await GetModelAsync(app); - var queryContext = new QueryContext(app, contentRepository, assetRepository, urlGenerator); + var queryContext = new QueryContext(app, assetRepository, contentRepository, urlGenerator, schemas, scriptEngine, user); return await modelContext.ExecuteAsync(queryContext, query); } @@ -89,9 +104,9 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL if (modelContext == null) { - var schemas = await schemaRepository.QueryAllAsync(app.Id); + var allSchemas = await schemaRepository.QueryAllAsync(app.Id); - modelContext = new GraphQLModel(app, schemas.Where(x => x.IsPublished), urlGenerator); + modelContext = new GraphQLModel(app, allSchemas.Where(x => x.IsPublished), urlGenerator); Cache.Set(cacheKey, modelContext); } diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs index 4c040ec6f..1181813c3 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/IGraphQLService.cs @@ -6,6 +6,7 @@ // All rights reserved. // ========================================================================== +using System.Security.Claims; using System.Threading.Tasks; using Squidex.Domain.Apps.Read.Apps; @@ -13,6 +14,6 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL { public interface IGraphQLService { - Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, GraphQLQuery query); + Task<(object Data, object[] Errors)> QueryAsync(IAppEntity app, ClaimsPrincipal user, GraphQLQuery query); } } diff --git a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs index 87a45c509..17cfca761 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/GraphQL/QueryContext.cs @@ -13,10 +13,13 @@ using Squidex.Domain.Apps.Read.Contents.Repositories; using Squidex.Infrastructure; using System.Collections.Concurrent; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; +using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Assets; using Squidex.Domain.Apps.Read.Assets.Repositories; +using Squidex.Domain.Apps.Read.Schemas.Services; // ReSharper disable InvertIf @@ -29,24 +32,41 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL private readonly IContentRepository contentRepository; private readonly IAssetRepository assetRepository; private readonly IGraphQLUrlGenerator urlGenerator; + private readonly IScriptEngine scriptEngine; + private readonly ISchemaProvider schemas; private readonly IAppEntity app; + private readonly ClaimsPrincipal user; public IGraphQLUrlGenerator UrlGenerator { get { return urlGenerator; } } - public QueryContext(IAppEntity app, IContentRepository contentRepository, IAssetRepository assetRepository, IGraphQLUrlGenerator urlGenerator) + public QueryContext( + IAppEntity app, + IAssetRepository assetRepository, + IContentRepository contentRepository, + IGraphQLUrlGenerator urlGenerator, + ISchemaProvider schemas, + IScriptEngine scriptEngine, + ClaimsPrincipal user) { Guard.NotNull(contentRepository, nameof(contentRepository)); Guard.NotNull(assetRepository, nameof(assetRepository)); + Guard.NotNull(schemas, nameof(schemas)); + Guard.NotNull(scriptEngine, nameof(scriptEngine)); Guard.NotNull(urlGenerator, nameof(urlGenerator)); + Guard.NotNull(user, nameof(user)); Guard.NotNull(app, nameof(app)); this.contentRepository = contentRepository; this.assetRepository = assetRepository; + this.schemas = schemas; + this.scriptEngine = scriptEngine; this.urlGenerator = urlGenerator; + this.user = user; + this.app = app; } @@ -73,11 +93,18 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL if (content == null) { - content = await contentRepository.FindContentAsync(app, schemaId, id).ConfigureAwait(false); + var schema = await schemas.FindSchemaByIdAsync(schemaId).ConfigureAwait(false); - if (content != null) + if (schema != null) { - cachedContents[content.Id] = content; + content = await contentRepository.FindContentAsync(app, schemaId, id).ConfigureAwait(false); + + if (content != null) + { + content.Data = scriptEngine.Transform(new ScriptContext { Data = content.Data, ContentId = content.Id, User = user }, schema.ScriptQuery); + + cachedContents[content.Id] = content; + } } } @@ -98,14 +125,23 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL public async Task> QueryContentsAsync(Guid schemaId, string query) { - var contents = await contentRepository.QueryAsync(app, schemaId, false, null, query).ConfigureAwait(false); + var result = new List(); + + var schema = await schemas.FindSchemaByIdAsync(schemaId).ConfigureAwait(false); - foreach (var content in contents) + if (schema != null) { - cachedContents[content.Id] = content; + result.AddRange(await contentRepository.QueryAsync(app, schemaId, false, null, query).ConfigureAwait(false)); + + foreach (var content in result) + { + content.Data = scriptEngine.Transform(new ScriptContext { Data = content.Data, ContentId = content.Id, User = user }, schema.ScriptQuery); + + cachedContents[content.Id] = content; + } } - return contents; + return result; } public Task> GetReferencedAssetsAsync(JToken value) @@ -149,11 +185,18 @@ namespace Squidex.Domain.Apps.Read.Contents.GraphQL if (notLoadedContents.Count > 0) { - var contents = await contentRepository.QueryAsync(app, schemaId, false, notLoadedContents, null).ConfigureAwait(false); + var schema = await schemas.FindSchemaByIdAsync(schemaId).ConfigureAwait(false); - foreach (var content in contents) + if (schema != null) { - cachedContents[content.Id] = content; + var contents = await contentRepository.QueryAsync(app, schemaId, false, notLoadedContents, null).ConfigureAwait(false); + + foreach (var content in contents) + { + content.Data = scriptEngine.Transform(new ScriptContext { Data = content.Data, ContentId = content.Id, User = user }, schema.ScriptQuery); + + cachedContents[content.Id] = content; + } } } diff --git a/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs b/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs index 4e1f355a5..6cd71fe70 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/IContentEntity.cs @@ -14,6 +14,6 @@ namespace Squidex.Domain.Apps.Read.Contents { bool IsPublished { get; } - NamedContentData Data { get; } + NamedContentData Data { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs b/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs index 147e4d1a9..fd46a2d0f 100644 --- a/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read/Schemas/Repositories/ISchemaRepository.cs @@ -18,6 +18,6 @@ namespace Squidex.Domain.Apps.Read.Schemas.Repositories Task FindSchemaAsync(Guid appId, string name); - Task FindSchemaAsync(Guid schemaId); + Task FindSchemaAsync(Guid schemaId, bool provideDeleted = false); } } diff --git a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs b/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs index d57aa4ad8..ea46a0914 100644 --- a/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs +++ b/src/Squidex.Domain.Apps.Read/Schemas/Services/Implementations/CachingSchemaProvider.cs @@ -52,7 +52,7 @@ namespace Squidex.Domain.Apps.Read.Schemas.Services.Implementations if (!Cache.TryGetValue(cacheKey, out ISchemaEntity result)) { - result = await repository.FindSchemaAsync(id); + result = await repository.FindSchemaAsync(id, provideDeleted); Cache.Set(cacheKey, result, CacheDuration); diff --git a/src/Squidex/Controllers/ContentApi/ContentsController.cs b/src/Squidex/Controllers/ContentApi/ContentsController.cs index 5beb6b5a2..c2919fe80 100644 --- a/src/Squidex/Controllers/ContentApi/ContentsController.cs +++ b/src/Squidex/Controllers/ContentApi/ContentsController.cs @@ -22,6 +22,7 @@ using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Write.Contents; using Squidex.Domain.Apps.Write.Contents.Commands; +using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.Reflection; using Squidex.Pipeline; @@ -51,8 +52,8 @@ namespace Squidex.Controllers.ContentApi : base(commandBus) { this.graphQL = graphQL; - this.scriptEngine = scriptEngine; this.schemas = schemas; + this.scriptEngine = scriptEngine; this.contentRepository = contentRepository; } @@ -63,7 +64,7 @@ namespace Squidex.Controllers.ContentApi [ApiCosts(2)] public async Task PostGraphQL([FromBody] GraphQLQuery query) { - var result = await graphQL.QueryAsync(App, query); + var result = await graphQL.QueryAsync(App, User, query); if (result.Errors?.Length > 0) { @@ -143,11 +144,6 @@ namespace Squidex.Controllers.ContentApi { var schemaEntity = await FindSchemaAsync(name); - if (schemaEntity == null) - { - return NotFound(); - } - var entity = await contentRepository.FindContentAsync(App, schemaEntity.Id, id); if (entity == null) @@ -187,8 +183,10 @@ namespace Squidex.Controllers.ContentApi [HttpPost] [Route("content/{app}/{name}/")] [ApiCosts(1)] - public async Task PostContent([FromBody] NamedContentData request, [FromQuery] bool publish = false) + public async Task PostContent(string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false) { + await FindSchemaAsync(name); + var command = new CreateContent { ContentId = Guid.NewGuid(), User = User, Data = request.ToCleaned(), Publish = publish }; var context = await CommandBus.PublishAsync(command); @@ -203,8 +201,10 @@ namespace Squidex.Controllers.ContentApi [HttpPut] [Route("content/{app}/{name}/{id}")] [ApiCosts(1)] - public async Task PutContent(Guid id, [FromBody] NamedContentData request) + public async Task PutContent(string name, Guid id, [FromBody] NamedContentData request) { + await FindSchemaAsync(name); + var command = new UpdateContent { ContentId = id, User = User, Data = request.ToCleaned() }; var context = await CommandBus.PublishAsync(command); @@ -219,8 +219,10 @@ namespace Squidex.Controllers.ContentApi [HttpPatch] [Route("content/{app}/{name}/{id}")] [ApiCosts(1)] - public async Task PatchContent(Guid id, [FromBody] NamedContentData request) + public async Task PatchContent(string name, Guid id, [FromBody] NamedContentData request) { + await FindSchemaAsync(name); + var command = new PatchContent { ContentId = id, User = User, Data = request.ToCleaned() }; var context = await CommandBus.PublishAsync(command); @@ -235,8 +237,10 @@ namespace Squidex.Controllers.ContentApi [HttpPut] [Route("content/{app}/{name}/{id}/publish")] [ApiCosts(1)] - public async Task PublishContent(Guid id) + public async Task PublishContent(string name, Guid id) { + await FindSchemaAsync(name); + var command = new PublishContent { ContentId = id, User = User }; await CommandBus.PublishAsync(command); @@ -248,8 +252,10 @@ namespace Squidex.Controllers.ContentApi [HttpPut] [Route("content/{app}/{name}/{id}/unpublish")] [ApiCosts(1)] - public async Task UnpublishContent(Guid id) + public async Task UnpublishContent(string name, Guid id) { + await FindSchemaAsync(name); + var command = new UnpublishContent { ContentId = id, User = User }; await CommandBus.PublishAsync(command); @@ -261,8 +267,10 @@ namespace Squidex.Controllers.ContentApi [HttpDelete] [Route("content/{app}/{name}/{id}")] [ApiCosts(1)] - public async Task PutContent(Guid id) + public async Task DeleteContent(string name, Guid id) { + await FindSchemaAsync(name); + var command = new DeleteContent { ContentId = id, User = User }; await CommandBus.PublishAsync(command); @@ -283,6 +291,11 @@ namespace Squidex.Controllers.ContentApi schemaEntity = await schemas.FindSchemaByNameAsync(AppId, name); } + if (schemaEntity == null || !schemaEntity.IsPublished) + { + throw new DomainObjectNotFoundException(name, typeof(ISchemaEntity)); + } + return schemaEntity; } } diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs b/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs index 56dd1acf4..d8e020ddd 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs +++ b/tests/Squidex.Domain.Apps.Read.Tests/Contents/GraphQLTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Security.Claims; using System.Threading.Tasks; using FakeItEasy; using Microsoft.Extensions.Caching.Memory; @@ -26,7 +27,9 @@ using Squidex.Domain.Apps.Read.Schemas.Repositories; using Xunit; using NodaTime.Extensions; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Read.Assets; +using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Infrastructure; // ReSharper disable SimilarAnonymousTypeNearby @@ -60,11 +63,14 @@ namespace Squidex.Domain.Apps.Read.Contents new GeolocationFieldProperties())); private readonly ISchemaRepository schemaRepository = A.Fake(); + private readonly ISchemaProvider schemas = A.Fake(); private readonly ISchemaEntity schemaEntity = A.Fake(); + private readonly IScriptEngine scriptEngine = A.Fake(); private readonly IContentRepository contentRepository = A.Fake(); private readonly IAssetRepository assetRepository = A.Fake(); private readonly IAppEntity appEntity = A.Dummy(); private readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); + private readonly ClaimsPrincipal user = new ClaimsPrincipal(); private readonly IGraphQLService sut; public GraphQLTests() @@ -76,12 +82,19 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => schemaEntity.Name).Returns(schema.Name); A.CallTo(() => schemaEntity.Schema).Returns(schema); A.CallTo(() => schemaEntity.IsPublished).Returns(true); + A.CallTo(() => schemaEntity.ScriptQuery).Returns(""); - var schemas = new List { schemaEntity }; + A.CallTo(() => scriptEngine.Transform(A.That.Matches(x => x.User == user), "")) + .ReturnsLazily((ScriptContext c, string s) => c.Data); - A.CallTo(() => schemaRepository.QueryAllAsync(appId)).Returns(Task.FromResult>(schemas)); + A.CallTo(() => schemas.FindSchemaByIdAsync(A.Ignored, false)) + .Returns(Task.FromResult(schemaEntity)); - sut = new CachingGraphQLService(cache, schemaRepository, assetRepository, contentRepository, new FakeUrlGenerator()); + var allSchemas = new List { schemaEntity }; + + A.CallTo(() => schemaRepository.QueryAllAsync(appId)).Returns(Task.FromResult>(allSchemas)); + + sut = new CachingGraphQLService(cache, assetRepository, contentRepository, new FakeUrlGenerator(), schemaRepository, schemas, scriptEngine); } [Fact] @@ -116,7 +129,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => assetRepository.QueryAsync(appEntity.Id, null, null, "my-query", 30, 5)) .Returns(Task.FromResult>(assets)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -181,7 +194,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => assetRepository.FindAssetAsync(assetId)) .Returns(Task.FromResult(assetEntity)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -255,7 +268,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => contentRepository.QueryAsync(appEntity, schemaId, false, null, "?$top=30&$skip=5")) .Returns(Task.FromResult>(contents)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -356,7 +369,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => contentRepository.FindContentAsync(appEntity, schemaId, contentId)) .Returns(Task.FromResult(contentEntity)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -443,7 +456,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => contentRepository.QueryAsync(appEntity, schemaId, false, A>.That.Matches(x => x.Contains(contentRefId)), null)) .Returns(Task.FromResult>(refContents)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -503,7 +516,7 @@ namespace Squidex.Domain.Apps.Read.Contents A.CallTo(() => assetRepository.QueryAsync(appEntity.Id, null, A>.That.Matches(x => x.Contains(assetRefId)), null, int.MaxValue, 0)) .Returns(Task.FromResult>(refAssets)); - var result = await sut.QueryAsync(appEntity, new GraphQLQuery { Query = query }); + var result = await sut.QueryAsync(appEntity, user, new GraphQLQuery { Query = query }); var expected = new { @@ -532,11 +545,64 @@ namespace Squidex.Domain.Apps.Read.Contents AssertJson(expected, new { data = result.Data }); } - private static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId) + [Fact] + public async Task Should_not_return_value_when_field_not_part_of_content() + { + var contentId = Guid.NewGuid(); + var contentEntity = CreateContent(contentId, Guid.Empty, Guid.Empty, new NamedContentData()); + + var query = $@" + query {{ + findMySchemaContent(id: ""{contentId}"") {{ + id + version + created + createdBy + lastModified + lastModifiedBy + url + data {{ + myString {{ + iv + }} + }} + }} + }}"; + + A.CallTo(() => contentRepository.FindContentAsync(appEntity, schemaId, contentId)) + .Returns(Task.FromResult(contentEntity)); + + var result = await sut.QueryAsync(appEntity, user, 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", + url = $"contents/my-schema/{contentEntity.Id}", + data = new + { + myString = (object)null + } + } + } + }; + + AssertJson(expected, new { data = result.Data }); + } + + private static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null) { var now = DateTime.UtcNow.ToInstant(); - var data = + data = data ?? new NamedContentData() .AddField("my-json", new ContentFieldData().AddValue("iv", JToken.FromObject(new { value = 1 }))) diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs b/tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs index e5f494784..a759f6c9b 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs +++ b/tests/Squidex.Domain.Apps.Read.Tests/Schemas/CachingSchemaProviderTests.cs @@ -67,13 +67,13 @@ namespace Squidex.Domain.Apps.Read.Schemas [Fact] public async Task Should_also_retrieve_schema_by_name_if_retrieved_by_id_before() { - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)) + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)) .Returns(Task.FromResult(schemaV1)); await ProvideSchemaById(schemaV1); await ProvideSchemaByName(schemaV1); - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)).MustHaveHappened(); + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)).MustHaveHappened(); A.CallTo(() => repository.FindSchemaAsync(appId.Id, schemaId.Name)).MustNotHaveHappened(); } @@ -86,16 +86,16 @@ namespace Squidex.Domain.Apps.Read.Schemas await ProvideSchemaByName(schemaV1); await ProvideSchemaById(schemaV1); - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)).MustNotHaveHappened(); + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)).MustNotHaveHappened(); A.CallTo(() => repository.FindSchemaAsync(appId.Id, schemaId.Name)).MustHaveHappened(); } [Fact] public async Task Should_clear_cache_for_id_after_update_event() { - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)) + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)) .Returns(schemaV2); - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)) + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)) .Returns(schemaV1).Once(); await ProvideSchemaById(schemaV1); @@ -104,7 +104,7 @@ namespace Squidex.Domain.Apps.Read.Schemas await ProvideSchemaById(schemaV2); - A.CallTo(() => repository.FindSchemaAsync(schemaId.Id)).MustHaveHappened(Repeated.Exactly.Times(2)); + A.CallTo(() => repository.FindSchemaAsync(schemaId.Id, false)).MustHaveHappened(Repeated.Exactly.Times(2)); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs index a7eb7f8af..78b6f9b5e 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandHandlerTests.cs @@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Write.Contents { private readonly ContentCommandMiddleware sut; private readonly ContentDomainObject content; - private readonly ISchemaProvider schemaProvider = A.Fake(); + private readonly ISchemaProvider schemas = A.Fake(); private readonly ISchemaEntity schemaEntity = A.Fake(); private readonly IScriptEngine scriptEngine = A.Fake(); private readonly IAppProvider appProvider = A.Fake(); @@ -54,14 +54,14 @@ namespace Squidex.Domain.Apps.Write.Contents content = new ContentDomainObject(contentId, -1); - sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy(), schemaProvider, scriptEngine, A.Dummy()); + sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy(), schemas, scriptEngine, A.Dummy()); A.CallTo(() => appEntity.LanguagesConfig).Returns(languagesConfig); A.CallTo(() => appEntity.PartitionResolver).Returns(languagesConfig.ToResolver()); A.CallTo(() => appProvider.FindAppByIdAsync(AppId)).Returns(Task.FromResult(appEntity)); A.CallTo(() => schemaEntity.Schema).Returns(schema); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(SchemaId, false)).Returns(Task.FromResult(schemaEntity)); + A.CallTo(() => schemas.FindSchemaByIdAsync(SchemaId, false)).Returns(Task.FromResult(schemaEntity)); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs index c4452047f..1e7f7f086 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandHandlerTests.cs @@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Write.Schemas { public class SchemaCommandMiddlewareTests : HandlerTestBase { - private readonly ISchemaProvider schemaProvider = A.Fake(); + private readonly ISchemaProvider schemas = A.Fake(); private readonly SchemaCommandMiddleware sut; private readonly SchemaDomainObject schema; private readonly FieldRegistry registry = new FieldRegistry(new TypeNameRegistry()); @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Write.Schemas { schema = new SchemaDomainObject(SchemaId, -1, registry); - sut = new SchemaCommandMiddleware(Handler, schemaProvider); + sut = new SchemaCommandMiddleware(Handler, schemas); } [Fact] @@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Write.Schemas { var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId }); - A.CallTo(() => schemaProvider.FindSchemaByNameAsync(AppId, SchemaName)) + A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)) .Returns(Task.FromResult(A.Dummy())); await TestCreate(schema, async _ => @@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Write.Schemas await Assert.ThrowsAsync(async () => await sut.HandleAsync(context)); }, false); - A.CallTo(() => schemaProvider.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened(); + A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened(); } [Fact] @@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Write.Schemas { var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId }); - A.CallTo(() => schemaProvider.FindSchemaByNameAsync(AppId, SchemaName)) + A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)) .Returns(Task.FromResult(null)); await TestCreate(schema, async _ => @@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Write.Schemas Assert.Equal(SchemaId, context.Result>().IdOrValue); - A.CallTo(() => schemaProvider.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened(); + A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened(); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs index 039a32b38..0478e34cd 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs @@ -26,47 +26,47 @@ namespace Squidex.Domain.Apps.Write.Webhooks { public class WebhookCommandMiddlewareTests : HandlerTestBase { - private readonly ISchemaProvider schemaProvider = A.Fake(); + private readonly ISchemaProvider schemas = A.Fake(); private readonly WebhookCommandMiddleware sut; private readonly WebhookDomainObject webhook; private readonly Uri url = new Uri("http://squidex.io"); private readonly Guid schemaId = Guid.NewGuid(); private readonly Guid webhookId = Guid.NewGuid(); - private readonly List schemas; + private readonly List webhookSchemas; public WebhookCommandMiddlewareTests() { webhook = new WebhookDomainObject(webhookId, -1); - schemas = new List + webhookSchemas = new List { new WebhookSchema { SchemaId = schemaId } }; - sut = new WebhookCommandMiddleware(Handler, schemaProvider); + sut = new WebhookCommandMiddleware(Handler, schemas); } [Fact] public async Task Create_should_create_webhook() { - var context = CreateContextForCommand(new CreateWebhook { Schemas = schemas, Url = url, WebhookId = webhookId }); + var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(A.Fake())); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(A.Fake())); await TestCreate(webhook, async _ => { await sut.HandleAsync(context); }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); } [Fact] public async Task Create_should_throw_exception_when_schema_is_not_found() { - var context = CreateContextForCommand(new CreateWebhook { Schemas = schemas, Url = url, WebhookId = webhookId }); + var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(null)); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(null)); await Assert.ThrowsAsync(async () => { @@ -80,9 +80,9 @@ namespace Squidex.Domain.Apps.Write.Webhooks [Fact] public async Task Update_should_update_domain_object() { - var context = CreateContextForCommand(new UpdateWebhook { Schemas = schemas, Url = url, WebhookId = webhookId }); + var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(A.Fake())); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(A.Fake())); CreateWebhook(); @@ -91,15 +91,15 @@ namespace Squidex.Domain.Apps.Write.Webhooks await sut.HandleAsync(context); }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); } [Fact] public async Task Update_should_throw_exception_when_schema_is_not_found() { - var context = CreateContextForCommand(new UpdateWebhook { Schemas = schemas, Url = url, WebhookId = webhookId }); + var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - A.CallTo(() => schemaProvider.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(null)); + A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(Task.FromResult(null)); CreateWebhook();