From 7a6a188e4d5675550d5b636ff32f8454fd57332b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 14 Jul 2018 20:48:07 +0200 Subject: [PATCH] Query services improved. --- .../Assets/AssetQueryService.cs | 101 ++++++++++++++++++ .../Assets/IAssetEntity.cs | 2 +- .../Assets/IAssetQueryService.cs | 20 ++++ .../Assets/State/AssetState.cs | 5 + .../Contents/ContentQueryContext.cs | 46 ++++++++ .../Contents/ContentQueryService.cs | 54 +++++----- .../Contents/GraphQL/CachingGraphQLService.cs | 11 +- .../GraphQL/GraphQLExecutionContext.cs | 6 +- .../Contents/IContentQueryService.cs | 9 +- .../Contents/QueryExecutionContext.cs | 23 ++-- src/Squidex.Domain.Apps.Entities/Query.cs | 54 ++++++++++ .../{Contents => }/QueryContext.cs | 55 +++++----- .../Controllers/Assets/AssetsController.cs | 36 +++---- .../Contents/ContentsController.cs | 35 ++---- .../Controllers/Contents/Models/ContentDto.cs | 1 + .../angular/forms/tag-editor.component.scss | 3 + .../angular/forms/tag-editor.component.ts | 2 +- .../shared/components/asset.component.html | 2 +- .../app/shared/components/asset.component.ts | 41 ++++++- .../shared/services/assets.service.spec.ts | 12 ++- .../app/shared/services/assets.service.ts | 9 ++ .../Contents/ContentQueryServiceTests.cs | 10 +- .../Contents/GraphQL/GraphQLQueriesTests.cs | 31 +++--- .../Contents/GraphQL/GraphQLTestBase.cs | 4 +- 24 files changed, 407 insertions(+), 165 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Query.cs rename src/Squidex.Domain.Apps.Entities/{Contents => }/QueryContext.cs (65%) diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs new file mode 100644 index 000000000..62a33ede5 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs @@ -0,0 +1,101 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Entities.Assets.Repositories; +using Squidex.Domain.Apps.Entities.Tags; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Assets +{ + public sealed class AssetQueryService : IAssetQueryService + { + private readonly ITagService tagService; + private readonly IAssetRepository assetRepository; + + public AssetQueryService(ITagService tagService, IAssetRepository assetRepository) + { + Guard.NotNull(tagService, nameof(tagService)); + Guard.NotNull(assetRepository, nameof(assetRepository)); + + this.tagService = tagService; + + this.assetRepository = assetRepository; + } + + public async Task FindAssetAsync(QueryContext context, Guid id) + { + Guard.NotNull(context, nameof(context)); + + var asset = await assetRepository.FindAssetAsync(id); + + if (asset != null) + { + await DenormalizeTagsAsync(context.App.Id, Enumerable.Repeat(asset, 1)); + } + + return asset; + } + + public async Task> QueryAsync(QueryContext context, Query query) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(query, nameof(query)); + + IResultList assets; + + if (query.Ids != null) + { + assets = await assetRepository.QueryAsync(context.App.Id, new HashSet(query.Ids)); + assets = Sort(assets, query.Ids); + } + else + { + assets = await assetRepository.QueryAsync(context.App.Id, query.ODataQuery); + } + + await DenormalizeTagsAsync(context.App.Id, assets); + + return assets; + } + + private IResultList Sort(IResultList assets, IList ids) + { + var sorted = ids.Select(id => assets.FirstOrDefault(x => x.Id == id)).Where(x => x != null); + + return ResultList.Create(sorted, assets.Total); + } + + private async Task DenormalizeTagsAsync(Guid appId, IEnumerable assets) + { + var tags = assets.SelectMany(x => x.Tags).Distinct().ToArray(); + + var tagsById = await tagService.DenormalizeTagsAsync(appId, "Assets", tags); + + foreach (var asset in assets) + { + if (asset.Tags?.Length > 0) + { + var tagNames = new List(); + + foreach (var id in asset.Tags) + { + if (tagsById.TryGetValue(id, out var name)) + { + tagNames.Add(name); + } + } + + asset.Tags = tagNames.ToArray(); + } + } + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs b/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs index c6afe8c57..2f7bffca0 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/IAssetEntity.cs @@ -22,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Assets string MimeType { get; } - string[] Tags { get; } + string[] Tags { get; set; } long FileVersion { get; } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs b/src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs new file mode 100644 index 000000000..e0068df52 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Assets +{ + public interface IAssetQueryService + { + Task> QueryAsync(QueryContext contex, Query query); + + Task FindAssetAsync(QueryContext context, Guid id); + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs index 333bf425b..4f468f65b 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs @@ -73,6 +73,11 @@ namespace Squidex.Domain.Apps.Entities.Assets.State TotalSize += @event.FileSize; } + protected void On(AssetTagged @event) + { + Tags = @event.Tags; + } + protected void On(AssetRenamed @event) { FileName = @event.FileName; diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs new file mode 100644 index 000000000..f7461d617 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs @@ -0,0 +1,46 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Contents +{ + public sealed class ContentQueryContext : Cloneable + { + public string SchemaIdOrName { get; private set; } + + public QueryContext Base { get; private set; } + + public ContentQueryContext(QueryContext @base) + { + Guard.NotNull(@base, nameof(@base)); + + Base = @base; + } + + public ContentQueryContext WithSchemaName(string name) + { + return Clone(c => c.SchemaIdOrName = name); + } + + public ContentQueryContext WithArchived(bool archived) + { + return Clone(c => c.Base = c.Base.WithArchived(archived)); + } + + public ContentQueryContext WithFlatten(bool flatten) + { + return Clone(c => c.Base = c.Base.WithFlatten(flatten)); + } + + public ContentQueryContext WithSchemaId(Guid id) + { + return Clone(c => c.SchemaIdOrName = id.ToString()); + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index 5bcb4c004..39492d884 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -55,12 +55,12 @@ namespace Squidex.Domain.Apps.Entities.Contents this.scriptEngine = scriptEngine; } - public Task ThrowIfSchemaNotExistsAsync(QueryContext context) + public Task ThrowIfSchemaNotExistsAsync(ContentQueryContext context) { return GetSchemaAsync(context); } - public async Task FindContentAsync(QueryContext context, Guid id, long version = -1) + public async Task FindContentAsync(ContentQueryContext context, Guid id, long version = -1) { Guard.NotNull(context, nameof(context)); @@ -70,53 +70,47 @@ namespace Squidex.Domain.Apps.Entities.Contents { var isVersioned = version > EtagVersion.Empty; - var parsedStatus = context.IsFrontendClient ? StatusAll : StatusPublished; + var parsedStatus = context.Base.IsFrontendClient ? StatusAll : StatusPublished; var content = isVersioned ? await FindContentByVersionAsync(id, version) : - await FindContentAsync(context, id, parsedStatus, schema); + await FindContentAsync(context.Base, id, parsedStatus, schema); - if (content == null || (content.Status != Status.Published && !context.IsFrontendClient) || content.SchemaId.Id != schema.Id) + if (content == null || (content.Status != Status.Published && !context.Base.IsFrontendClient) || content.SchemaId.Id != schema.Id) { throw new DomainObjectNotFoundException(id.ToString(), typeof(ISchemaEntity)); } - return Transform(context, schema, true, content); + return Transform(context.Base, schema, true, content); } } - public async Task> QueryAsync(QueryContext context, string query) + public async Task> QueryAsync(ContentQueryContext context, Query query) { Guard.NotNull(context, nameof(context)); var schema = await GetSchemaAsync(context); - using (Profiler.TraceMethod("QueryAsyncByQuery")) + using (Profiler.TraceMethod()) { - var parsedQuery = ParseQuery(context, query, schema); - var parsedStatus = ParseStatus(context); - - var contents = await contentRepository.QueryAsync(context.App, schema, parsedStatus, parsedQuery); - - return Transform(context, schema, true, contents); - } - } - - public async Task> QueryAsync(QueryContext context, IList ids) - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(ids, nameof(ids)); + var parsedStatus = ParseStatus(context.Base); - var schema = await GetSchemaAsync(context); + IResultList contents; - using (Profiler.TraceMethod("QueryAsyncByIds")) - { - var parsedStatus = ParseStatus(context); + if (query.Ids?.Count > 0) + { + contents = await contentRepository.QueryAsync(context.Base.App, schema, parsedStatus, new HashSet(query.Ids)); + contents = Sort(contents, query.Ids); + } + else + { + var parsedQuery = ParseQuery(context.Base, query.ODataQuery, schema); - var contents = await contentRepository.QueryAsync(context.App, schema, parsedStatus, new HashSet(ids)); + contents = await contentRepository.QueryAsync(context.Base.App, schema, parsedStatus, parsedQuery); + } - return Sort(Transform(context, schema, false, contents), ids); + return Transform(context.Base, schema, true, contents); } } @@ -218,18 +212,18 @@ namespace Squidex.Domain.Apps.Entities.Contents } } - public async Task GetSchemaAsync(QueryContext context) + public async Task GetSchemaAsync(ContentQueryContext context) { ISchemaEntity schema = null; if (Guid.TryParse(context.SchemaIdOrName, out var id)) { - schema = await appProvider.GetSchemaAsync(context.App.Id, id); + schema = await appProvider.GetSchemaAsync(context.Base.App.Id, id); } if (schema == null) { - schema = await appProvider.GetSchemaAsync(context.App.Id, context.SchemaIdOrName); + schema = await appProvider.GetSchemaAsync(context.Base.App.Id, context.SchemaIdOrName); } if (schema == null) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs index 68bad5840..9ec78bc7c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; using Squidex.Domain.Apps.Entities.Apps; +using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Infrastructure; @@ -20,23 +21,23 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(10); private readonly IContentQueryService contentQuery; private readonly IGraphQLUrlGenerator urlGenerator; - private readonly IAssetRepository assetRepository; + private readonly IAssetQueryService assetQuery; private readonly IAppProvider appProvider; public CachingGraphQLService(IMemoryCache cache, IAppProvider appProvider, - IAssetRepository assetRepository, + IAssetQueryService assetQuery, IContentQueryService contentQuery, IGraphQLUrlGenerator urlGenerator) : base(cache) { Guard.NotNull(appProvider, nameof(appProvider)); - Guard.NotNull(assetRepository, nameof(assetRepository)); + Guard.NotNull(assetQuery, nameof(assetQuery)); Guard.NotNull(contentQuery, nameof(contentQuery)); Guard.NotNull(urlGenerator, nameof(urlGenerator)); this.appProvider = appProvider; - this.assetRepository = assetRepository; + this.assetQuery = assetQuery; this.contentQuery = contentQuery; this.urlGenerator = urlGenerator; } @@ -53,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var modelContext = await GetModelAsync(context.App); - var ctx = new GraphQLExecutionContext(context, assetRepository, contentQuery, urlGenerator); + var ctx = new GraphQLExecutionContext(context, assetQuery, contentQuery, urlGenerator); return await modelContext.ExecuteAsync(ctx, query); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs index 31acd1523..2c1242877 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs @@ -10,8 +10,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Entities.Assets; -using Squidex.Domain.Apps.Entities.Assets.Repositories; - namespace Squidex.Domain.Apps.Entities.Contents.GraphQL { public sealed class GraphQLExecutionContext : QueryExecutionContext @@ -19,10 +17,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL public IGraphQLUrlGenerator UrlGenerator { get; } public GraphQLExecutionContext(QueryContext context, - IAssetRepository assetRepository, + IAssetQueryService assetQueryService, IContentQueryService contentQuery, IGraphQLUrlGenerator urlGenerator) - : base(context, assetRepository, contentQuery) + : base(context, assetQueryService, contentQuery) { UrlGenerator = urlGenerator; } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs index 99658ba3b..55ec74748 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Threading.Tasks; using Squidex.Infrastructure; @@ -14,12 +13,10 @@ namespace Squidex.Domain.Apps.Entities.Contents { public interface IContentQueryService { - Task> QueryAsync(QueryContext context, IList ids); + Task> QueryAsync(ContentQueryContext context, Query query); - Task> QueryAsync(QueryContext context, string query); + Task FindContentAsync(ContentQueryContext context, Guid id, long version = EtagVersion.Any); - Task FindContentAsync(QueryContext context, Guid id, long version = EtagVersion.Any); - - Task ThrowIfSchemaNotExistsAsync(QueryContext context); + Task ThrowIfSchemaNotExistsAsync(ContentQueryContext context); } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs index fa8764f53..fecb6eb5a 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Assets; -using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Contents @@ -21,18 +20,16 @@ namespace Squidex.Domain.Apps.Entities.Contents private readonly ConcurrentDictionary cachedContents = new ConcurrentDictionary(); private readonly ConcurrentDictionary cachedAssets = new ConcurrentDictionary(); private readonly IContentQueryService contentQuery; - private readonly IAssetRepository assetRepository; + private readonly IAssetQueryService assetQuery; private readonly QueryContext context; - public QueryExecutionContext(QueryContext context, - IAssetRepository assetRepository, - IContentQueryService contentQuery) + public QueryExecutionContext(QueryContext context, IAssetQueryService assetQuery, IContentQueryService contentQuery) { - Guard.NotNull(assetRepository, nameof(assetRepository)); + Guard.NotNull(assetQuery, nameof(assetQuery)); Guard.NotNull(contentQuery, nameof(contentQuery)); Guard.NotNull(context, nameof(context)); - this.assetRepository = assetRepository; + this.assetQuery = assetQuery; this.contentQuery = contentQuery; this.context = context; } @@ -43,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (asset == null) { - asset = await assetRepository.FindAssetAsync(id); + asset = await assetQuery.FindAssetAsync(context, id); if (asset != null) { @@ -60,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (content == null) { - content = await contentQuery.FindContentAsync(context.WithSchemaId(schemaId), id); + content = await contentQuery.FindContentAsync(new ContentQueryContext(context).WithSchemaId(schemaId), id); if (content != null) { @@ -73,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public async Task> QueryAssetsAsync(string query) { - var assets = await assetRepository.QueryAsync(context.App.Id, query); + var assets = await assetQuery.QueryAsync(context, Query.Empty.WithODataQuery(query)); foreach (var asset in assets) { @@ -85,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.Contents public async Task> QueryContentsAsync(string schemaIdOrName, string query) { - var result = await contentQuery.QueryAsync(context.WithSchemaName(schemaIdOrName), query); + var result = await contentQuery.QueryAsync(new ContentQueryContext(context).WithSchemaName(schemaIdOrName), Query.Empty.WithODataQuery(query)); foreach (var content in result) { @@ -103,7 +100,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (notLoadedAssets.Count > 0) { - var assets = await assetRepository.QueryAsync(context.App.Id, notLoadedAssets); + var assets = await assetQuery.QueryAsync(context, Query.Empty.WithIds(notLoadedAssets)); foreach (var asset in assets) { @@ -122,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (notLoadedContents.Count > 0) { - var result = await contentQuery.QueryAsync(context.WithSchemaId(schemaId), notLoadedContents); + var result = await contentQuery.QueryAsync(new ContentQueryContext(context).WithSchemaId(schemaId), Query.Empty.WithIds(notLoadedContents)); foreach (var content in result) { diff --git a/src/Squidex.Domain.Apps.Entities/Query.cs b/src/Squidex.Domain.Apps.Entities/Query.cs new file mode 100644 index 000000000..f90d78d84 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Query.cs @@ -0,0 +1,54 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities +{ + public sealed class Query : Cloneable + { + public static readonly Query Empty = new Query(); + + public List Ids { get; private set; } + + public string ODataQuery { get; private set; } + + public Query WithODataQuery(string odataQuery) + { + return Clone(c => c.ODataQuery = odataQuery); + } + + public Query WithIds(IEnumerable ids) + { + return Clone(c => c.Ids = ids.ToList()); + } + + public Query WithIds(string ids) + { + if (string.IsNullOrEmpty(ids)) + { + return Clone(c => + { + c.Ids = new List(); + + foreach (var id in ids.Split(',')) + { + if (Guid.TryParse(id, out var guid)) + { + c.Ids.Add(guid); + } + } + }); + } + + return this; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs b/src/Squidex.Domain.Apps.Entities/QueryContext.cs similarity index 65% rename from src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs rename to src/Squidex.Domain.Apps.Entities/QueryContext.cs index b8754a86b..8c86dbc30 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/QueryContext.cs +++ b/src/Squidex.Domain.Apps.Entities/QueryContext.cs @@ -5,14 +5,13 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; using System.Security.Claims; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.Security; -namespace Squidex.Domain.Apps.Entities.Contents +namespace Squidex.Domain.Apps.Entities { public sealed class QueryContext : Cloneable { @@ -20,38 +19,19 @@ namespace Squidex.Domain.Apps.Entities.Contents public IAppEntity App { get; private set; } - public IEnumerable Languages { get; private set; } - - public string SchemaIdOrName { get; private set; } - public bool Archived { get; private set; } public bool Flatten { get; private set; } + public IEnumerable Languages { get; private set; } + private QueryContext() { } - public static QueryContext Create(IAppEntity app, ClaimsPrincipal user, IEnumerable languageCodes = null) + public static QueryContext Create(IAppEntity app, ClaimsPrincipal user) { - var result = new QueryContext { App = app, User = user }; - - if (languageCodes != null) - { - var languages = new List(); - - foreach (var iso2Code in languageCodes) - { - if (Language.TryGetLanguage(iso2Code, out var language)) - { - languages.Add(language); - } - } - - result.Languages = languages; - } - - return result; + return new QueryContext { App = app, User = user }; } public QueryContext WithArchived(bool archived) @@ -64,14 +44,27 @@ namespace Squidex.Domain.Apps.Entities.Contents return Clone(c => c.Flatten = flatten); } - public QueryContext WithSchemaName(string name) + public QueryContext WithLanguages(IEnumerable languageCodes) { - return Clone(c => c.SchemaIdOrName = name); - } + if (languageCodes != null) + { + return Clone(c => + { + var languages = new List(); - public QueryContext WithSchemaId(Guid id) - { - return Clone(c => c.SchemaIdOrName = id.ToString()); + foreach (var iso2Code in languageCodes) + { + if (Language.TryGetLanguage(iso2Code, out var language)) + { + languages.Add(language); + } + } + + c.Languages = languages; + }); + } + + return this; } public bool IsFrontendClient diff --git a/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs b/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs index a8f064eb3..fd5703d53 100644 --- a/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using NSwag.Annotations; using Squidex.Areas.Api.Controllers.Assets.Models; +using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets.Commands; @@ -34,21 +35,21 @@ namespace Squidex.Areas.Api.Controllers.Assets [SwaggerTag(nameof(Assets))] public sealed class AssetsController : ApiController { - private readonly IAssetRepository assetRepository; + private readonly IAssetQueryService assetQuery; private readonly IAssetStatsRepository assetStatsRepository; private readonly IAppPlansProvider appPlanProvider; private readonly AssetConfig assetsConfig; public AssetsController( ICommandBus commandBus, - IAssetRepository assetRepository, + IAssetQueryService assetQuery, IAssetStatsRepository assetStatsRepository, IAppPlansProvider appPlanProvider, IOptions assetsConfig) : base(commandBus) { this.assetsConfig = assetsConfig.Value; - this.assetRepository = assetRepository; + this.assetQuery = assetQuery; this.assetStatsRepository = assetStatsRepository; this.appPlanProvider = appPlanProvider; } @@ -72,25 +73,9 @@ namespace Squidex.Areas.Api.Controllers.Assets [ApiCosts(1)] public async Task GetAssets(string app, [FromQuery] string ids = null) { - HashSet idsList = null; + var context = Context(); - if (!string.IsNullOrWhiteSpace(ids)) - { - idsList = new HashSet(); - - foreach (var id in ids.Split(',')) - { - if (Guid.TryParse(id, out var guid)) - { - idsList.Add(guid); - } - } - } - - var assets = - idsList?.Count > 0 ? - await assetRepository.QueryAsync(App.Id, idsList) : - await assetRepository.QueryAsync(App.Id, Request.QueryString.ToString()); + var assets = await assetQuery.QueryAsync(context, Query.Empty.WithODataQuery(Request.QueryString.ToString()).WithIds(ids)); var response = AssetsDto.FromAssets(assets); @@ -115,7 +100,9 @@ namespace Squidex.Areas.Api.Controllers.Assets [ApiCosts(1)] public async Task GetAsset(string app, Guid id) { - var entity = await assetRepository.FindAssetAsync(id); + var context = Context(); + + var entity = await assetQuery.FindAssetAsync(context, id); if (entity == null) { @@ -270,5 +257,10 @@ namespace Squidex.Areas.Api.Controllers.Assets return assetFile; } + + private QueryContext Context() + { + return QueryContext.Create(App, User); + } } } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs index dbf2e37e1..75320903c 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs @@ -16,6 +16,7 @@ using NodaTime.Text; using NSwag.Annotations; using Squidex.Areas.Api.Controllers.Contents.Models; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Domain.Apps.Entities.Contents.GraphQL; @@ -65,7 +66,7 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(2)] public async Task PostGraphQL(string app, [FromBody] GraphQLQuery query) { - var result = await graphQl.QueryAsync(Context(), query); + var result = await graphQl.QueryAsync(Context().Base, query); if (result.Errors?.Length > 0) { @@ -97,32 +98,14 @@ namespace Squidex.Areas.Api.Controllers.Contents [ApiCosts(2)] public async Task GetContents(string app, string name, [FromQuery] bool archived = false, [FromQuery] string ids = null) { - List idsList = null; + var context = Context().WithArchived(archived).WithSchemaName(name); - if (!string.IsNullOrWhiteSpace(ids)) - { - idsList = new List(); - - foreach (var id in ids.Split(',')) - { - if (Guid.TryParse(id, out var guid)) - { - idsList.Add(guid); - } - } - } - - var context = Context().WithSchemaName(name).WithArchived(archived); - - var result = - idsList?.Count > 0 ? - await contentQuery.QueryAsync(context, idsList) : - await contentQuery.QueryAsync(context, Request.QueryString.ToString()); + var result = await contentQuery.QueryAsync(context, Query.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString())); var response = new ContentsDto { Total = result.Total, - Items = result.Take(200).Select(x => ContentDto.FromContent(x, context)).ToArray() + Items = result.Take(200).Select(x => ContentDto.FromContent(x, context.Base)).ToArray() }; var options = controllerOptions.Value; @@ -157,7 +140,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var context = Context().WithSchemaName(name); var content = await contentQuery.FindContentAsync(context, id); - var response = ContentDto.FromContent(content, context); + var response = ContentDto.FromContent(content, context.Base); Response.Headers["ETag"] = content.Version.ToString(); @@ -193,7 +176,7 @@ namespace Squidex.Areas.Api.Controllers.Contents var context = Context().WithSchemaName(name); var content = await contentQuery.FindContentAsync(context, id, version); - var response = ContentDto.FromContent(content, context); + var response = ContentDto.FromContent(content, context.Base); Response.Headers["ETag"] = content.Version.ToString(); @@ -498,9 +481,9 @@ namespace Squidex.Areas.Api.Controllers.Contents return new ChangeContentStatus { Status = status, ContentId = id, DueTime = dt }; } - private QueryContext Context() + private ContentQueryContext Context() { - return QueryContext.Create(App, User, Request.Headers["X-Languages"]).WithFlatten(Request.Headers.ContainsKey("X-Flatten")); + return new ContentQueryContext(QueryContext.Create(App, User).WithLanguages(Request.Headers["X-Languages"])).WithFlatten(Request.Headers.ContainsKey("X-Flatten")); } } } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs index 0d3efa9a7..97093cdc2 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs @@ -10,6 +10,7 @@ using System.ComponentModel.DataAnnotations; using NodaTime; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.ConvertContent; +using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents.Commands; using Squidex.Infrastructure; diff --git a/src/Squidex/app/framework/angular/forms/tag-editor.component.scss b/src/Squidex/app/framework/angular/forms/tag-editor.component.scss index 2b5007e7e..f2d65e94e 100644 --- a/src/Squidex/app/framework/angular/forms/tag-editor.component.scss +++ b/src/Squidex/app/framework/angular/forms/tag-editor.component.scss @@ -18,6 +18,7 @@ border: 0; background: transparent; min-width: 40px; + max-width: 100%; } &:focus, @@ -38,6 +39,7 @@ .item { & { @include border-radius(10px); + @include truncate; display: inline-block; color: $color-dark-foreground; cursor: default; @@ -48,6 +50,7 @@ font-size: .8rem; font-weight: normal; line-height: 20px; + vertical-align: middle; } &-container { diff --git a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts index 7b692a6a7..52c8ec6c4 100644 --- a/src/Squidex/app/framework/angular/forms/tag-editor.component.ts +++ b/src/Squidex/app/framework/angular/forms/tag-editor.component.ts @@ -176,7 +176,7 @@ export class TagEditorComponent implements ControlValueAccessor { const value = this.addInput.value; if (!value || value.length === 0) { - this.updateItems(this.items.slice(0, this.items.length - 2)); + this.updateItems(this.items.slice(0, this.items.length - 1)); return false; } diff --git a/src/Squidex/app/shared/components/asset.component.html b/src/Squidex/app/shared/components/asset.component.html index 1e28af161..8b26c3483 100644 --- a/src/Squidex/app/shared/components/asset.component.html +++ b/src/Squidex/app/shared/components/asset.component.html @@ -54,7 +54,7 @@
- +
{{asset.pixelWidth}}x{{asset.pixelHeight}}px, {{asset.fileSize | sqxFileSize}} diff --git a/src/Squidex/app/shared/components/asset.component.ts b/src/Squidex/app/shared/components/asset.component.ts index bc615fa29..4b31c7eaa 100644 --- a/src/Squidex/app/shared/components/asset.component.ts +++ b/src/Squidex/app/shared/components/asset.component.ts @@ -5,8 +5,10 @@ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. */ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { Subscription } from 'rxjs'; +import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { AppsState, @@ -21,6 +23,7 @@ import { Types, Versioned } from '@app/shared/internal'; +import { TagAssetDto } from '@appshared/services/assets.service'; @Component({ selector: 'sqx-asset', @@ -30,7 +33,9 @@ import { fadeAnimation ] }) -export class AssetComponent implements OnInit { +export class AssetComponent implements OnDestroy, OnInit { + private tagSubscription: Subscription; + @Input() public initFile: File; @@ -72,6 +77,8 @@ export class AssetComponent implements OnInit { public renameForm = new RenameAssetForm(this.formBuilder); + public tagInput = new FormControl(); + public progress = 0; constructor( @@ -102,6 +109,18 @@ export class AssetComponent implements OnInit { } else { this.updateAsset(this.asset, false); } + + this.tagSubscription = + this.tagInput.valueChanges.pipe( + distinctUntilChanged(), + debounceTime(2000) + ).subscribe(tags => { + this.tagAsset(tags); + }); + } + + public ngOnDestroy() { + this.tagSubscription.unsubscribe(); } public updateFile(files: FileList) { @@ -140,6 +159,19 @@ export class AssetComponent implements OnInit { } } + public tagAsset(tags: string[]) { + if (tags) { + const requestDto = new TagAssetDto(tags); + + this.assetsService.putAsset(this.appsState.appName, this.asset.id, requestDto, this.asset.version) + .subscribe(dto => { + this.updateAsset(this.asset.tag(tags, this.authState.user!.token, dto.version), true); + }, error => { + this.dialogs.notifyError(error); + }); + } + } + public renameStart() { if (!this.isDisabled) { this.renameForm.load(this.asset); @@ -176,9 +208,10 @@ export class AssetComponent implements OnInit { private updateAsset(asset: AssetDto, emitEvent: boolean) { this.asset = asset; - this.progress = 0; + this.tagInput.setValue(asset.tags); + if (emitEvent) { this.emitUpdated(asset); } diff --git a/src/Squidex/app/shared/services/assets.service.spec.ts b/src/Squidex/app/shared/services/assets.service.spec.ts index 9855571bd..258da493b 100644 --- a/src/Squidex/app/shared/services/assets.service.spec.ts +++ b/src/Squidex/app/shared/services/assets.service.spec.ts @@ -17,10 +17,10 @@ import { AssetsService, DateTime, RenameAssetDto, + TagAssetDto, Version, Versioned } from './../'; -import { TagAssetDto } from '@appshared/services/assets.service'; describe('AssetDto', () => { const creation = DateTime.today(); @@ -40,6 +40,16 @@ describe('AssetDto', () => { expect(asset_2.version).toEqual(newVersion); }); + it('should update tag property and user info when tagged', () => { + const asset_1 = new AssetDto('1', creator, creator, creation, creation, 'name.png', 'png', 1, 1, 'image/png', false, 1, 1, [], 'url', version); + const asset_2 = asset_1.tag(['tag1', 'tag2'], modifier, newVersion, modified); + + expect(asset_2.tags).toEqual(['tag1', 'tag2']); + expect(asset_2.lastModified).toEqual(modified); + expect(asset_2.lastModifiedBy).toEqual(modifier); + expect(asset_2.version).toEqual(newVersion); + }); + it('should update file properties when uploading', () => { const update = new AssetReplacedDto(2, 2, 'image/jpeg', true, 2, 2); diff --git a/src/Squidex/app/shared/services/assets.service.ts b/src/Squidex/app/shared/services/assets.service.ts index a3a2577fa..7219228d4 100644 --- a/src/Squidex/app/shared/services/assets.service.ts +++ b/src/Squidex/app/shared/services/assets.service.ts @@ -70,6 +70,15 @@ export class AssetDto extends Model { }); } + public tag(tags: string[], user: string, version: Version, now?: DateTime): AssetDto { + return this.with({ + tags, + lastModified: now || DateTime.now(), + lastModifiedBy: user, + version + }); + } + public rename(fileName: string, user: string, version: Version, now?: DateTime): AssetDto { return this.with({ fileName, diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs index c83a35191..8911861be 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs @@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private readonly ClaimsPrincipal user; private readonly ClaimsIdentity identity = new ClaimsIdentity(); private readonly EdmModelBuilder modelBuilder = A.Fake(); - private readonly QueryContext context; + private readonly ContentQueryContext context; private readonly ContentQueryService sut; public ContentQueryServiceTests() @@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => schema.SchemaDef).Returns(new Schema("my-schema")); - context = QueryContext.Create(app, user); + context = new ContentQueryContext(QueryContext.Create(app, user)); sut = new ContentQueryService(contentRepository, contentVersionLoader, appProvider, scriptEngine, modelBuilder); } @@ -187,7 +187,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), A.Ignored)) .Returns(ResultList.Create(Enumerable.Repeat(content, count), total)); - var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), string.Empty); + var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), A.That.Matches(x => x.ODataQuery == string.Empty)); Assert.Equal(contentData, result[0].Data); Assert.Equal(content.Id, result[0].Id); @@ -215,7 +215,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => modelBuilder.BuildEdmModel(schema, app)) .Throws(new ODataException()); - return Assert.ThrowsAsync(() => sut.QueryAsync(context.WithSchemaId(schemaId), "query")); + return Assert.ThrowsAsync(() => sut.QueryAsync(context.WithSchemaId(schemaId), A.That.Matches(x => x.ODataQuery == "query"))); } public static IEnumerable ManyIdRequestData = new[] @@ -241,7 +241,7 @@ namespace Squidex.Domain.Apps.Entities.Contents A.CallTo(() => contentRepository.QueryAsync(app, schema, A.That.IsSameSequenceAs(status), A>.Ignored)) .Returns(ResultList.Create(ids.Select(x => CreateContent(x)).Shuffle(), total)); - var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), ids); + var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), A.Ignored); Assert.Equal(ids, result.Select(x => x.Id).ToList()); Assert.Equal(total, result.Total); diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs index 287278209..52d6141ee 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs @@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var assets = new List { asset }; - A.CallTo(() => assetRepository.QueryAsync(app.Id, "?$take=30&$skip=5&$search=my-query")) + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), A.That.Matches(x => x.ODataQuery == "?$take=30&$skip=5&$search=my-query"))) .Returns(ResultList.Create(assets, 0)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -134,7 +134,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var assets = new List { asset }; - A.CallTo(() => assetRepository.QueryAsync(app.Id, "?$take=30&$skip=5&$search=my-query")) + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), A.That.Matches(x => x.ODataQuery == "?$take=30&$skip=5&$search=my-query"))) .Returns(ResultList.Create(assets, 10)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -203,7 +203,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => assetRepository.FindAssetAsync(assetId)) + A.CallTo(() => assetQuery.FindAssetAsync(MatchsAssetContext(), assetId)) .Returns(asset); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -287,7 +287,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var contents = new List { content }; - A.CallTo(() => contentQuery.QueryAsync(ContextMatch(), "?$top=30&$skip=5")) + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5"))) .Returns(ResultList.Create(contents, 0)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -421,7 +421,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var contents = new List { content }; - A.CallTo(() => contentQuery.QueryAsync(ContextMatch(), "?$top=30&$skip=5")) + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5"))) .Returns(ResultList.Create(contents, 10)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -539,7 +539,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(ContextMatch(), contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any)) .Returns(content); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -632,10 +632,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refContents = new List { contentRef }; - A.CallTo(() => contentQuery.FindContentAsync(ContextMatch(), contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any)) .Returns(content); - A.CallTo(() => contentQuery.QueryAsync(ContextMatch(), A>.That.IsSameSequenceAs(new[] { contentRefId }))) + A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A.Ignored)) .Returns(ResultList.Create(refContents, 0)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -692,10 +692,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL var refAssets = new List { assetRef }; - A.CallTo(() => contentQuery.FindContentAsync(ContextMatch(), contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any)) .Returns(content); - A.CallTo(() => assetRepository.QueryAsync(app.Id, A>.That.Matches(x => x.Contains(assetRefId)))) + A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), A.Ignored)) .Returns(ResultList.Create(refAssets, 0)); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -751,7 +751,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL }} }}"; - A.CallTo(() => contentQuery.FindContentAsync(ContextMatch(), contentId, EtagVersion.Any)) + A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any)) .Returns(content); var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); @@ -764,9 +764,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL AssertResult(expected, result, false); } - private QueryContext ContextMatch() + private QueryContext MatchsAssetContext() { - return A.That.Matches(x => x.App == app && x.SchemaIdOrName == schema.Id.ToString() && x.User == user && !x.Archived); + return A.That.Matches(x => x.App == app && x.User == user && !x.Archived); + } + + private ContentQueryContext MatchsContentContext() + { + return A.That.Matches(x => x.Base.App == app && x.Base.User == user && !x.Base.Archived && x.SchemaIdOrName == schema.Id.ToString()); } } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs index 174f1f477..022622e98 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL protected static readonly string appName = "my-app"; protected readonly Schema schemaDef; protected readonly IContentQueryService contentQuery = A.Fake(); - protected readonly IAssetRepository assetRepository = A.Fake(); + protected readonly IAssetQueryService assetQuery = A.Fake(); protected readonly ISchemaEntity schema = A.Fake(); protected readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions())); protected readonly IAppProvider appProvider = A.Fake(); @@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL A.CallTo(() => appProvider.GetSchemasAsync(appId)).Returns(allSchemas); - sut = new CachingGraphQLService(cache, appProvider, assetRepository, contentQuery, new FakeUrlGenerator()); + sut = new CachingGraphQLService(cache, appProvider, assetQuery, contentQuery, new FakeUrlGenerator()); } protected static IContentEntity CreateContent(Guid id, Guid refId, Guid assetId, NamedContentData data = null)