Browse Source

Started with asset enricher.

pull/377/head
Sebastian Stehle 7 years ago
parent
commit
0cff67d2cc
  1. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs
  2. 27
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  3. 11
      src/Squidex.Domain.Apps.Entities/Assets/AssetCreatedResult.cs
  4. 78
      src/Squidex.Domain.Apps.Entities/Assets/AssetEnricher.cs
  5. 9
      src/Squidex.Domain.Apps.Entities/Assets/AssetEntity.cs
  6. 69
      src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs
  7. 19
      src/Squidex.Domain.Apps.Entities/Assets/IAssetEnricher.cs
  8. 6
      src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs
  9. 13
      src/Squidex.Domain.Apps.Entities/Assets/IEnrichedAssetEntity.cs
  10. 2
      src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs
  11. 54
      src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs
  12. 6
      src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs
  13. 10
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  14. 8
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs
  15. 17
      src/Squidex.Domain.Apps.Entities/Contents/IContentEnricher.cs
  16. 18
      src/Squidex.Domain.Apps.Entities/Contents/IEnrichedContentEntity.cs
  17. 8
      src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs
  18. 10
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  19. 7
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  20. 2
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs
  21. 3
      src/Squidex/Config/Domain/StoreServices.cs
  22. 23
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs
  23. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetQueryServiceTests.cs
  24. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

2
src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs

@ -137,7 +137,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets
}
}
public async Task<IList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash)
public async Task<IReadOnlyList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash)
{
using (Profiler.TraceMethod<MongoAssetRepository>())
{

27
src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -10,7 +10,6 @@ using System.Collections.Generic;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Orleans;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Tags;
using Squidex.Infrastructure;
@ -22,31 +21,31 @@ namespace Squidex.Domain.Apps.Entities.Assets
public sealed class AssetCommandMiddleware : GrainCommandMiddleware<AssetCommand, IAssetGrain>
{
private readonly IAssetStore assetStore;
private readonly IAssetEnricher assetEnricher;
private readonly IAssetQueryService assetQuery;
private readonly IAssetThumbnailGenerator assetThumbnailGenerator;
private readonly IEnumerable<ITagGenerator<CreateAsset>> tagGenerators;
private readonly ITagService tagService;
public AssetCommandMiddleware(
IGrainFactory grainFactory,
IAssetEnricher assetEnricher,
IAssetQueryService assetQuery,
IAssetStore assetStore,
IAssetThumbnailGenerator assetThumbnailGenerator,
IEnumerable<ITagGenerator<CreateAsset>> tagGenerators,
ITagService tagService)
IEnumerable<ITagGenerator<CreateAsset>> tagGenerators)
: base(grainFactory)
{
Guard.NotNull(assetEnricher, nameof(assetEnricher));
Guard.NotNull(assetStore, nameof(assetStore));
Guard.NotNull(assetQuery, nameof(assetQuery));
Guard.NotNull(assetThumbnailGenerator, nameof(assetThumbnailGenerator));
Guard.NotNull(tagGenerators, nameof(tagGenerators));
Guard.NotNull(tagService, nameof(tagService));
this.assetStore = assetStore;
this.assetEnricher = assetEnricher;
this.assetQuery = assetQuery;
this.assetThumbnailGenerator = assetThumbnailGenerator;
this.tagGenerators = tagGenerators;
this.tagService = tagService;
}
public override async Task HandleAsync(CommandContext context, Func<Task> next)
@ -73,9 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
if (IsDuplicate(createAsset, existing))
{
var denormalizedTags = await tagService.DenormalizeTagsAsync(createAsset.AppId.Id, TagGroups.Assets, existing.Tags);
result = new AssetCreatedResult(existing, true, new HashSet<string>(denormalizedTags.Values));
result = new AssetCreatedResult(existing, true);
}
break;
@ -88,9 +85,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
tagGenerator.GenerateTags(createAsset, createAsset.Tags);
}
var asset = (IAssetEntity)await ExecuteCommandAsync(createAsset);
var asset = (IEnrichedAssetEntity)await ExecuteAndAdjustTagsAsync(createAsset);
result = new AssetCreatedResult(asset, false, createAsset.Tags);
result = new AssetCreatedResult(asset, false);
await assetStore.CopyAsync(context.ContextId.ToString(), createAsset.AssetId.ToString(), asset.FileVersion, null);
}
@ -112,11 +109,11 @@ namespace Squidex.Domain.Apps.Entities.Assets
try
{
var result = (AssetResult)await ExecuteAndAdjustTagsAsync(updateAsset);
var result = (IEnrichedAssetEntity)await ExecuteAndAdjustTagsAsync(updateAsset);
context.Complete(result);
await assetStore.CopyAsync(context.ContextId.ToString(), updateAsset.AssetId.ToString(), result.Asset.FileVersion, null);
await assetStore.CopyAsync(context.ContextId.ToString(), updateAsset.AssetId.ToString(), result.FileVersion, null);
}
finally
{
@ -148,9 +145,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
if (result is IAssetEntity asset)
{
var denormalizedTags = await tagService.DenormalizeTagsAsync(asset.AppId.Id, TagGroups.Assets, asset.Tags);
var enriched = await assetEnricher.EnrichAsync(asset);
return new AssetResult(asset, new HashSet<string>(denormalizedTags.Values));
return enriched;
}
return result;

11
src/Squidex.Domain.Apps.Entities/Assets/AssetCreatedResult.cs

@ -5,17 +5,18 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class AssetCreatedResult : AssetResult
public sealed class AssetCreatedResult
{
public IEnrichedAssetEntity Asset { get; }
public bool IsDuplicate { get; }
public AssetCreatedResult(IAssetEntity asset, bool isDuplicate, HashSet<string> tags)
: base(asset, tags)
public AssetCreatedResult(IEnrichedAssetEntity asset, bool isDuplicate)
{
Asset = asset;
IsDuplicate = isDuplicate;
}
}

78
src/Squidex.Domain.Apps.Entities/Assets/AssetEnricher.cs

@ -0,0 +1,78 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class AssetEnricher : IAssetEnricher
{
private readonly ITagService tagService;
public AssetEnricher(ITagService tagService)
{
Guard.NotNull(tagService, nameof(tagService));
this.tagService = tagService;
}
public async Task<IEnrichedAssetEntity> EnrichAsync(IAssetEntity asset)
{
Guard.NotNull(asset, nameof(asset));
var enriched = await EnrichAsync(Enumerable.Repeat(asset, 1));
return enriched[0];
}
public async Task<IReadOnlyList<IEnrichedAssetEntity>> EnrichAsync(IEnumerable<IAssetEntity> assets)
{
Guard.NotNull(assets, nameof(assets));
var results = new List<IEnrichedAssetEntity>();
foreach (var group in assets.GroupBy(x => x.AppId.Id))
{
var tagsById = await CalculateTags(group);
foreach (var asset in group)
{
var result = SimpleMapper.Map(asset, new AssetEntity());
result.TagNames = new HashSet<string>();
if (asset.Tags != null)
{
foreach (var id in asset.Tags)
{
if (tagsById.TryGetValue(id, out var name))
{
result.TagNames.Add(name);
}
}
}
results.Add(result);
}
}
return results;
}
private async Task<Dictionary<string, string>> CalculateTags(IGrouping<System.Guid, IAssetEntity> group)
{
var uniqueIds = group.Where(x => x.Tags != null).SelectMany(x => x.Tags).ToHashSet();
return await tagService.DenormalizeTagsAsync(group.Key, TagGroups.Assets, uniqueIds);
}
}
}

9
tests/Squidex.Domain.Apps.Entities.Tests/Contents/TestData/FakeAssetEntity.cs → src/Squidex.Domain.Apps.Entities/Assets/AssetEntity.cs

@ -1,19 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using NodaTime;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.TestData
namespace Squidex.Domain.Apps.Entities.Assets
{
public sealed class FakeAssetEntity : IAssetEntity
public sealed class AssetEntity : IEnrichedAssetEntity
{
public NamedId<Guid> AppId { get; set; }
@ -31,6 +30,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.TestData
public HashSet<string> Tags { get; set; }
public HashSet<string> TagNames { get; set; }
public long Version { get; set; }
public string MimeType { get; set; }

69
src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs

@ -21,9 +21,10 @@ using Squidex.Infrastructure.Queries.OData;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetQueryService : IAssetQueryService
public sealed class AssetQueryService : IAssetQueryService
{
private readonly ITagService tagService;
private readonly IAssetEnricher assetEnricher;
private readonly IAssetRepository assetRepository;
private readonly AssetOptions options;
@ -32,48 +33,47 @@ namespace Squidex.Domain.Apps.Entities.Assets
get { return options.DefaultPageSizeGraphQl; }
}
public AssetQueryService(ITagService tagService, IAssetRepository assetRepository, IOptions<AssetOptions> options)
public AssetQueryService(
ITagService tagService,
IAssetEnricher assetEnricher,
IAssetRepository assetRepository,
IOptions<AssetOptions> options)
{
Guard.NotNull(tagService, nameof(tagService));
Guard.NotNull(options, nameof(options));
Guard.NotNull(assetEnricher, nameof(assetEnricher));
Guard.NotNull(assetRepository, nameof(assetRepository));
Guard.NotNull(options, nameof(options));
this.tagService = tagService;
this.assetEnricher = assetEnricher;
this.assetRepository = assetRepository;
this.options = options.Value;
this.tagService = tagService;
}
public Task<IAssetEntity> FindAssetAsync(QueryContext context, Guid id)
{
Guard.NotNull(context, nameof(context));
return FindAssetAsync(context.App.Id, id);
}
public async Task<IAssetEntity> FindAssetAsync(Guid appId, Guid id)
public async Task<IEnrichedAssetEntity> FindAssetAsync( Guid id)
{
var asset = await assetRepository.FindAssetAsync(id);
if (asset != null)
{
await DenormalizeTagsAsync(appId, Enumerable.Repeat(asset, 1));
return await assetEnricher.EnrichAsync(asset);
}
return asset;
return null;
}
public async Task<IList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash)
public async Task<IReadOnlyList<IEnrichedAssetEntity>> QueryByHashAsync(Guid appId, string hash)
{
Guard.NotNull(hash, nameof(hash));
var assets = await assetRepository.QueryByHashAsync(appId, hash);
await DenormalizeTagsAsync(appId, assets);
var enriched = await assetEnricher.EnrichAsync(assets);
return assets;
return enriched;
}
public async Task<IResultList<IAssetEntity>> QueryAsync(QueryContext context, Q query)
public async Task<IResultList<IEnrichedAssetEntity>> QueryAsync(QueryContext context, Q query)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(query, nameof(query));
@ -92,9 +92,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
assets = await assetRepository.QueryAsync(context.App.Id, parsedQuery);
}
await DenormalizeTagsAsync(context.App.Id, assets);
var enriched = await assetEnricher.EnrichAsync(assets);
return assets;
return ResultList.Create<IEnrichedAssetEntity>(assets.Total, enriched);
}
private static IResultList<IAssetEntity> Sort(IResultList<IAssetEntity> assets, IReadOnlyList<Guid> ids)
@ -140,34 +140,5 @@ namespace Squidex.Domain.Apps.Entities.Assets
throw new ValidationException($"Failed to parse query: {ex.Message}", ex);
}
}
private async Task DenormalizeTagsAsync(Guid appId, IEnumerable<IAssetEntity> assets)
{
var tags = new HashSet<string>(assets.Where(x => x.Tags != null).SelectMany(x => x.Tags).Distinct());
var tagsById = await tagService.DenormalizeTagsAsync(appId, TagGroups.Assets, tags);
foreach (var asset in assets)
{
if (asset.Tags?.Count > 0)
{
var tagNames = asset.Tags.ToList();
asset.Tags.Clear();
foreach (var id in tagNames)
{
if (tagsById.TryGetValue(id, out var name))
{
asset.Tags.Add(name);
}
}
}
else
{
asset.Tags?.Clear();
}
}
}
}
}

19
src/Squidex.Domain.Apps.Entities/Assets/IAssetEnricher.cs

@ -0,0 +1,19 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Entities.Assets
{
public interface IAssetEnricher
{
Task<IEnrichedAssetEntity> EnrichAsync(IAssetEntity asset);
Task<IReadOnlyList<IEnrichedAssetEntity>> EnrichAsync(IEnumerable<IAssetEntity> assets);
}
}

6
src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs

@ -16,10 +16,10 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
int DefaultPageSizeGraphQl { get; }
Task<IList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash);
Task<IReadOnlyList<IEnrichedAssetEntity>> QueryByHashAsync(Guid appId, string hash);
Task<IResultList<IAssetEntity>> QueryAsync(QueryContext contex, Q query);
Task<IResultList<IEnrichedAssetEntity>> QueryAsync(QueryContext contex, Q query);
Task<IAssetEntity> FindAssetAsync(QueryContext context, Guid id);
Task<IEnrichedAssetEntity> FindAssetAsync(Guid id);
}
}

13
src/Squidex.Domain.Apps.Entities/Assets/AssetResult.cs → src/Squidex.Domain.Apps.Entities/Assets/IEnrichedAssetEntity.cs

@ -9,17 +9,8 @@ using System.Collections.Generic;
namespace Squidex.Domain.Apps.Entities.Assets
{
public class AssetResult
public interface IEnrichedAssetEntity : IAssetEntity
{
public IAssetEntity Asset { get; }
public HashSet<string> Tags { get; }
public AssetResult(IAssetEntity asset, HashSet<string> tags)
{
Asset = asset;
Tags = tags;
}
HashSet<string> TagNames { get; }
}
}

2
src/Squidex.Domain.Apps.Entities/Assets/Repositories/IAssetRepository.cs

@ -15,7 +15,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Repositories
{
public interface IAssetRepository
{
Task<IList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash);
Task<IReadOnlyList<IAssetEntity>> QueryByHashAsync(Guid appId, string hash);
Task<IResultList<IAssetEntity>> QueryAsync(Guid appId, Query query);

54
src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs

@ -0,0 +1,54 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentEnricher : IContentEnricher
{
private readonly IContentWorkflow contentWorkflow;
public ContentEnricher(IContentWorkflow contentWorkflow)
{
this.contentWorkflow = contentWorkflow;
}
public async Task<IReadOnlyList<IEnrichedContentEntity>> EnrichAsync(IEnumerable<IContentEntity> contents)
{
var results = new List<ContentEntity>();
using (Profiler.TraceMethod<ContentEnricher>())
{
var cache = new Dictionary<Status, StatusInfo>();
foreach (var content in contents)
{
var result = SimpleMapper.Map(content, new ContentEntity());
if (!cache.TryGetValue(content.Status, out var info))
{
info = await contentWorkflow.GetInfoAsync(content.Status);
cache[content.Status] = info;
}
result.StatusInfo = info;
result.Nexts = await contentWorkflow.GetNextsAsync(content);
results.Add(result);
}
}
return results;
}
}
}

6
src/Squidex.Domain.Apps.Entities/Contents/ContentEntity.cs

@ -12,7 +12,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class ContentEntity : IContentEntity
public sealed class ContentEntity : IEnrichedContentEntity
{
public Guid Id { get; set; }
@ -38,6 +38,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
public Status Status { get; set; }
public StatusInfo StatusInfo { get; set; }
public StatusInfo[] Nexts { get; set; }
public bool IsPending { get; set; }
}
}

10
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public sealed class GraphQLExecutionContext : QueryExecutionContext
{
private static readonly List<IAssetEntity> EmptyAssets = new List<IAssetEntity>();
private static readonly List<IEnrichedAssetEntity> EmptyAssets = new List<IEnrichedAssetEntity>();
private static readonly List<IContentEntity> EmptyContents = new List<IContentEntity>();
private readonly IDataLoaderContextAccessor dataLoaderContextAccessor;
private readonly IDependencyResolver resolver;
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
execution.UserContext = this;
}
public override Task<IAssetEntity> FindAssetAsync(Guid id)
public override Task<IEnrichedAssetEntity> FindAssetAsync(Guid id)
{
var dataLoader = GetAssetsLoader();
@ -67,7 +67,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return dataLoader.LoadAsync(id);
}
public async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(IJsonValue value)
public async Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(IJsonValue value)
{
var ids = ParseIds(value);
@ -95,9 +95,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return await dataLoader.LoadManyAsync(ids);
}
private IDataLoader<Guid, IAssetEntity> GetAssetsLoader()
private IDataLoader<Guid, IEnrichedAssetEntity> GetAssetsLoader()
{
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<Guid, IAssetEntity>("Assets",
return dataLoaderContextAccessor.Context.GetOrAddBatchLoader<Guid, IEnrichedAssetEntity>("Assets",
async batch =>
{
var result = await GetReferencedAssetsAsync(new List<Guid>(batch));

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

@ -13,7 +13,7 @@ using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class AssetGraphType : ObjectGraphType<IAssetEntity>
public sealed class AssetGraphType : ObjectGraphType<IEnrichedAssetEntity>
{
public AssetGraphType(IGraphModel model)
{
@ -168,7 +168,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Name = "tags",
ResolvedType = null,
Resolver = Resolve(x => x.Tags),
Description = "The height of the image in pixels if the asset is an image.",
Description = "The asset tags.",
Type = AllTypes.NonNullTagsType
});
@ -186,9 +186,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
Description = "An asset";
}
private static IFieldResolver Resolve(Func<IAssetEntity, object> action)
private static IFieldResolver Resolve(Func<IEnrichedAssetEntity, object> action)
{
return new FuncFieldResolver<IAssetEntity, object>(c => action(c.Source));
return new FuncFieldResolver<IEnrichedAssetEntity, object>(c => action(c.Source));
}
}
}

17
src/Squidex.Domain.Apps.Entities/Contents/IContentEnricher.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Squidex.Domain.Apps.Entities.Contents
{
public interface IContentEnricher
{
Task<IReadOnlyList<IEnrichedContentEntity>> EnrichAsync(IEnumerable<IContentEntity> contents);
}
}

18
src/Squidex.Domain.Apps.Entities/Contents/IEnrichedContentEntity.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Entities.Contents
{
public interface IEnrichedContentEntity : IContentEntity
{
StatusInfo StatusInfo { get; }
StatusInfo[] Nexts { get; }
}
}

8
src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs

@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public class QueryExecutionContext
{
private readonly ConcurrentDictionary<Guid, IContentEntity> cachedContents = new ConcurrentDictionary<Guid, IContentEntity>();
private readonly ConcurrentDictionary<Guid, IAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IAssetEntity>();
private readonly ConcurrentDictionary<Guid, IEnrichedAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IEnrichedAssetEntity>();
private readonly IContentQueryService contentQuery;
private readonly IAssetQueryService assetQuery;
private readonly QueryContext context;
@ -34,13 +34,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
this.context = context;
}
public virtual async Task<IAssetEntity> FindAssetAsync(Guid id)
public virtual async Task<IEnrichedAssetEntity> FindAssetAsync(Guid id)
{
var asset = cachedAssets.GetOrDefault(id);
if (asset == null)
{
asset = await assetQuery.FindAssetAsync(context, id);
asset = await assetQuery.FindAssetAsync(id);
if (asset != null)
{
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return result;
}
public virtual async Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids)
public virtual async Task<IReadOnlyList<IEnrichedAssetEntity>> GetReferencedAssetsAsync(ICollection<Guid> ids)
{
Guard.NotNull(ids, nameof(ids));

10
src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs

@ -133,9 +133,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
[ApiCosts(1)]
public async Task<IActionResult> GetAsset(string app, Guid id)
{
var context = Context();
var asset = await assetQuery.FindAssetAsync(context, id);
var asset = await assetQuery.FindAssetAsync(id);
if (asset == null)
{
@ -182,7 +180,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
var context = await CommandBus.PublishAsync(command);
var result = context.Result<AssetCreatedResult>();
var response = AssetDto.FromAsset(result.Asset, this, app, result.Tags, result.IsDuplicate);
var response = AssetDto.FromAsset(result.Asset, this, app, result.IsDuplicate);
return CreatedAtAction(nameof(GetAsset), new { app, id = response.Id }, response);
}
@ -267,8 +265,8 @@ namespace Squidex.Areas.Api.Controllers.Assets
{
var context = await CommandBus.PublishAsync(command);
var result = context.Result<AssetResult>();
var response = AssetDto.FromAsset(result.Asset, this, app, result.Tags);
var result = context.Result<IEnrichedAssetEntity>();
var response = AssetDto.FromAsset(result, this, app);
return response;
}

7
src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs

@ -118,14 +118,11 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
[JsonProperty("_meta")]
public AssetMetadata Metadata { get; set; }
public static AssetDto FromAsset(IAssetEntity asset, ApiController controller, string app, HashSet<string> tags = null, bool isDuplicate = false)
public static AssetDto FromAsset(IEnrichedAssetEntity asset, ApiController controller, string app, bool isDuplicate = false)
{
var response = SimpleMapper.Map(asset, new AssetDto { FileType = asset.FileName.FileType() });
if (tags != null)
{
response.Tags = tags;
}
response.Tags = asset.TagNames;
if (isDuplicate)
{

2
src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs

@ -37,7 +37,7 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
return Items.ToSurrogateKeys();
}
public static AssetsDto FromAssets(IResultList<IAssetEntity> assets, ApiController controller, string app)
public static AssetsDto FromAssets(IResultList<IEnrichedAssetEntity> assets, ApiController controller, string app)
{
var response = new AssetsDto
{

3
src/Squidex/Config/Domain/StoreServices.cs

@ -67,9 +67,6 @@ namespace Squidex.Config.Domain
services.AddTransientAs(c => new RestructureContentCollection(c.GetRequiredService<IMongoClient>().GetDatabase(mongoContentDatabaseName)))
.As<IMigration>();
services.AddTransientAs(c => new CreateStatusColors(c.GetRequiredService<IMongoClient>().GetDatabase(mongoContentDatabaseName)))
.As<IMigration>();
services.AddSingletonAs<MongoMigrationStatus>()
.As<IMigrationStatus>();

23
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetCommandMiddlewareTests.cs

@ -27,6 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
public class AssetCommandMiddlewareTests : HandlerTestBase<AssetState>
{
private readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>();
private readonly IAssetEnricher assetEnricher = A.Fake<IAssetEnricher>();
private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake<IAssetThumbnailGenerator>();
private readonly IAssetStore assetStore = A.Fake<MemoryAssetStore>();
private readonly ITagService tagService = A.Fake<ITagService>();
@ -52,19 +53,16 @@ namespace Squidex.Domain.Apps.Entities.Assets
asset.ActivateAsync(Id).Wait();
A.CallTo(() => assetQuery.QueryByHashAsync(AppId, A<string>.Ignored))
.Returns(new List<IAssetEntity>());
A.CallTo(() => tagService.DenormalizeTagsAsync(AppId, TagGroups.Assets, A<HashSet<string>>.Ignored))
.Returns(new Dictionary<string, string>
{
["1"] = "foundTag1",
["2"] = "foundTag2"
});
.Returns(new List<IEnrichedAssetEntity>());
A.CallTo(() => grainFactory.GetGrain<IAssetGrain>(Id, null))
.Returns(asset);
sut = new AssetCommandMiddleware(grainFactory, assetQuery, assetStore, assetThumbnailGenerator, new[] { tagGenerator }, tagService);
sut = new AssetCommandMiddleware(grainFactory,
assetEnricher,
assetQuery,
assetStore,
assetThumbnailGenerator, new[] { tagGenerator });
}
[Fact]
@ -84,7 +82,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
Assert.Contains("tag1", command.Tags);
Assert.Contains("tag2", command.Tags);
Assert.Equal(new HashSet<string> { "tag1", "tag2" }, result.Tags);
AssertAssetHasBeenUploaded(0, context.ContextId);
AssertAssetImageChecked();
@ -257,15 +254,15 @@ namespace Squidex.Domain.Apps.Entities.Assets
.MustHaveHappened();
}
private void SetupSameHashAsset(string fileName, long fileSize, out IAssetEntity existing)
private void SetupSameHashAsset(string fileName, long fileSize, out IEnrichedAssetEntity existing)
{
var temp = existing = A.Fake<IAssetEntity>();
var temp = existing = A.Fake<IEnrichedAssetEntity>();
A.CallTo(() => temp.FileName).Returns(fileName);
A.CallTo(() => temp.FileSize).Returns(fileSize);
A.CallTo(() => assetQuery.QueryByHashAsync(A<Guid>.Ignored, A<string>.Ignored))
.Returns(new List<IAssetEntity> { existing });
.Returns(new List<IEnrichedAssetEntity> { existing });
}
private void SetupImageInfo()

5
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetQueryServiceTests.cs

@ -25,6 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
public class AssetQueryServiceTests
{
private readonly ITagService tagService = A.Fake<ITagService>();
private readonly IAssetEnricher assetEnricher = A.Fake<IAssetEnricher>();
private readonly IAssetRepository assetRepository = A.Fake<IAssetRepository>();
private readonly IAppEntity app = A.Fake<IAppEntity>();
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app");
@ -52,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
var options = Options.Create(new AssetOptions { DefaultPageSize = 30 });
sut = new AssetQueryService(tagService, assetRepository, options);
sut = new AssetQueryService(tagService, assetEnricher, assetRepository, options);
}
[Fact]
@ -71,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
A.CallTo(() => assetRepository.FindAssetAsync(id, false))
.Returns(CreateAsset(id, "id1", "id2", "id3"));
var result = await sut.FindAssetAsync(context, id);
var result = await sut.FindAssetAsync(id);
Assert.Equal(HashSet.Of("name1", "name2", "name3"), result.Tags);
}

8
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

@ -158,17 +158,17 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
Data = data,
DataDraft = dataDraft,
Status = Status.Draft,
StatusColor = "red"
StatusInfo = new StatusInfo(Status.Draft, "red")
};
return content;
}
protected static IAssetEntity CreateAsset(Guid id)
protected static IEnrichedAssetEntity CreateAsset(Guid id)
{
var now = SystemClock.Instance.GetCurrentInstant();
var asset = new FakeAssetEntity
var asset = new AssetEntity
{
Id = id,
Version = 1,
@ -185,7 +185,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
IsImage = true,
PixelWidth = 800,
PixelHeight = 600,
Tags = new[] { "tag1", "tag2" }.ToHashSet()
TagNames = new[] { "tag1", "tag2" }.ToHashSet()
};
return asset;

Loading…
Cancel
Save