diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs index 6763827a2..8b3167968 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/MongoAssetRepository.cs @@ -14,6 +14,7 @@ using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets.Edm; using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors; +using Squidex.Domain.Apps.Entities.Tags; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.MongoDb; @@ -22,9 +23,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { public sealed partial class MongoAssetRepository : MongoRepositoryBase, IAssetRepository { - public MongoAssetRepository(IMongoDatabase database) + private readonly ITagService tagService; + + public MongoAssetRepository(IMongoDatabase database, ITagService tagService) : base(database) { + Guard.NotNull(tagService, nameof(tagService)); + + this.tagService = tagService; } protected override string CollectionName() @@ -51,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets { var odataQuery = EdmAssetModel.Edm.ParseQuery(query); - var filter = FindExtensions.BuildQuery(odataQuery, appId); + var filter = FindExtensions.BuildQuery(odataQuery, appId, tagService); var contentCount = Collection.Find(filter).CountDocumentsAsync(); var contentItems = diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs index 914c1b1f2..455543d76 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Assets/Visitors/FindExtensions.cs @@ -7,9 +7,11 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.OData.UriParser; using MongoDB.Bson; using MongoDB.Driver; +using Squidex.Domain.Apps.Entities.Tags; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb.OData; @@ -18,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors public static class FindExtensions { private static readonly FilterDefinitionBuilder Filter = Builders.Filter; - private static readonly PropertyCalculator PropertyCalculator = propertyNames => + private static readonly ConvertProperty PropertyCalculator = propertyNames => { if (propertyNames.Length > 0) { @@ -47,15 +49,17 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors return cursor.Skip(query); } - public static FilterDefinition BuildQuery(ODataUriParser query, Guid appId) + public static FilterDefinition BuildQuery(ODataUriParser query, Guid appId, ITagService tagService) { + var convertValue = CreateValueConverter(appId, tagService); + var filters = new List> { Filter.Eq(x => x.IndexedAppId, appId), Filter.Eq(x => x.IsDeleted, false) }; - var filter = query.BuildFilter(PropertyCalculator, false); + var filter = query.BuildFilter(PropertyCalculator, convertValue, false); if (filter.Filter != null) { @@ -82,5 +86,23 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors return new BsonDocument(); } } + + public static ConvertValue CreateValueConverter(Guid appId, ITagService tagService) + { + return new ConvertValue((field, value) => + { + if (string.Equals(field, nameof(MongoAssetEntity.Tags), StringComparison.OrdinalIgnoreCase)) + { + var tags = Task.Run(() => tagService.GetTagIdsAsync(appId, TagGroups.Assets, new[] { value.ToString() })).Result; + + if (tags.Length == 1) + { + return tags[0] ?? value; + } + } + + return value; + }); + } } } diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs index 35f218538..de6b94581 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Visitors/FindExtensions.cs @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors typeof(MongoContentEntity).GetProperties() .ToDictionary(x => x.Name, x => x.GetCustomAttribute()?.ElementName ?? x.Name, StringComparer.OrdinalIgnoreCase); - public static PropertyCalculator CreatePropertyCalculator(Schema schema, bool useDraft) + public static ConvertProperty CreatePropertyCalculator(Schema schema, bool useDraft) { return propertyNames => { @@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors }; } - public static IFindFluent ContentSort(this IFindFluent cursor, ODataUriParser query, PropertyCalculator propertyCalculator) + public static IFindFluent ContentSort(this IFindFluent cursor, ODataUriParser query, ConvertProperty propertyCalculator) { var sort = query.BuildSort(propertyCalculator); @@ -85,7 +85,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors return cursor.Skip(query); } - public static FilterDefinition BuildQuery(ODataUriParser query, Guid schemaId, Status[] status, PropertyCalculator propertyCalculator) + public static FilterDefinition BuildQuery(ODataUriParser query, Guid schemaId, Status[] status, ConvertProperty propertyCalculator) { var filters = new List> { diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs index 53d7bdb48..687b712d0 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetGrain.cs @@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Assets { GuardAsset.CanTag(c); - c.Tags = await tagService.NormalizeTagsAsync(Snapshot.AppId.Id, "Assets", c.Tags, Snapshot.Tags); + c.Tags = await tagService.NormalizeTagsAsync(Snapshot.AppId.Id, TagGroups.Assets, c.Tags, Snapshot.Tags); Tag(c); }); diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs index 62a33ede5..efd04181e 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs @@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Assets { var tags = assets.SelectMany(x => x.Tags).Distinct().ToArray(); - var tagsById = await tagService.DenormalizeTagsAsync(appId, "Assets", tags); + var tagsById = await tagService.DenormalizeTagsAsync(appId, TagGroups.Assets, tags); foreach (var asset in assets) { diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Edm/EdmAssetModel.cs b/src/Squidex.Domain.Apps.Entities/Assets/Edm/EdmAssetModel.cs index ffb58c065..b6b77f59a 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Edm/EdmAssetModel.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Edm/EdmAssetModel.cs @@ -30,6 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.Edm entityType.AddStructuralProperty(nameof(IAssetEntity.MimeType).ToCamelCase(), EdmPrimitiveTypeKind.String); entityType.AddStructuralProperty(nameof(IAssetEntity.PixelHeight).ToCamelCase(), EdmPrimitiveTypeKind.Int32); entityType.AddStructuralProperty(nameof(IAssetEntity.PixelWidth).ToCamelCase(), EdmPrimitiveTypeKind.Int32); + entityType.AddStructuralProperty(nameof(IAssetEntity.Tags).ToCamelCase(), EdmPrimitiveTypeKind.String); var container = new EdmEntityContainer("Squidex", "Container"); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs index 922a2fb68..7cfeccd85 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/GrainTagService.cs @@ -29,6 +29,11 @@ namespace Squidex.Domain.Apps.Entities.Tags return GetGrain(appId, category).NormalizeTagsAsync(names, ids); } + public Task GetTagIdsAsync(Guid appId, string category, string[] names) + { + return GetGrain(appId, category).GetTagIdsAsync(names); + } + public Task> DenormalizeTagsAsync(Guid appId, string category, string[] ids) { return GetGrain(appId, category).DenormalizeTagsAsync(ids); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs b/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs index 8dd82aa87..8639fc51b 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/ITagGrain.cs @@ -15,6 +15,8 @@ namespace Squidex.Domain.Apps.Entities.Tags { Task NormalizeTagsAsync(string[] names, string[] ids); + Task GetTagIdsAsync(string[] names); + Task> DenormalizeTagsAsync(string[] ids); Task> GetTagsAsync(); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs b/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs index 8a48be95f..d045e1e5c 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/ITagService.cs @@ -15,6 +15,8 @@ namespace Squidex.Domain.Apps.Entities.Tags { Task NormalizeTagsAsync(Guid appId, string category, string[] names, string[] ids); + Task GetTagIdsAsync(Guid appId, string category, string[] names); + Task> DenormalizeTagsAsync(Guid appId, string category, string[] ids); Task> GetTagsAsync(Guid appId, string category); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs index 6fda8c6dd..0fb7c281e 100644 --- a/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Tags/TagGrain.cs @@ -106,6 +106,18 @@ namespace Squidex.Domain.Apps.Entities.Tags return result.ToArray(); } + public Task GetTagIdsAsync(string[] names) + { + var result = new List(); + + foreach (var name in names) + { + result.Add(state.Tags.FirstOrDefault(x => x.Value.Name == name).Key); + } + + return Task.FromResult(result.ToArray()); + } + public Task> DenormalizeTagsAsync(string[] ids) { var result = new Dictionary(); diff --git a/src/Squidex.Domain.Apps.Entities/Tags/TagGroups.cs b/src/Squidex.Domain.Apps.Entities/Tags/TagGroups.cs new file mode 100644 index 000000000..99742c077 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Tags/TagGroups.cs @@ -0,0 +1,14 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Entities.Tags +{ + public static class TagGroups + { + public const string Assets = "Assets"; + } +} diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterBuilder.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterBuilder.cs index ffc82c1dc..c0562708e 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterBuilder.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterBuilder.cs @@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.MongoDb.OData { public static class FilterBuilder { - public static (FilterDefinition Filter, bool Last) BuildFilter(this ODataUriParser query, PropertyCalculator propertyCalculator = null, bool supportsSearch = true) + public static (FilterDefinition Filter, bool Last) BuildFilter(this ODataUriParser query, ConvertProperty convertProperty = null, ConvertValue convertValue = null, bool supportsSearch = true) { SearchClause search; try @@ -47,7 +47,7 @@ namespace Squidex.Infrastructure.MongoDb.OData if (filter != null) { - return (FilterVisitor.Visit(filter.Expression, propertyCalculator), true); + return (FilterVisitor.Visit(filter.Expression, convertProperty, convertValue), true); } return (null, false); diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterVisitor.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterVisitor.cs index cfe498c4e..826068b0e 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterVisitor.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/FilterVisitor.cs @@ -16,16 +16,18 @@ namespace Squidex.Infrastructure.MongoDb.OData public sealed class FilterVisitor : QueryNodeVisitor> { private static readonly FilterDefinitionBuilder Filter = Builders.Filter; - private readonly PropertyCalculator propertyCalculator; + private readonly ConvertProperty convertProperty; + private readonly ConvertValue convertValue; - private FilterVisitor(PropertyCalculator propertyCalculator) + private FilterVisitor(ConvertProperty convertProperty, ConvertValue convertValue) { - this.propertyCalculator = propertyCalculator; + this.convertProperty = convertProperty; + this.convertValue = convertValue; } - public static FilterDefinition Visit(QueryNode node, PropertyCalculator propertyCalculator) + public static FilterDefinition Visit(QueryNode node, ConvertProperty propertyCalculator, ConvertValue convertValue) { - var visitor = new FilterVisitor(propertyCalculator); + var visitor = new FilterVisitor(propertyCalculator, convertValue); return node.Accept(visitor); } @@ -52,23 +54,26 @@ namespace Squidex.Infrastructure.MongoDb.OData if (string.Equals(nodeIn.Name, "endswith", StringComparison.OrdinalIgnoreCase)) { - var value = BuildRegex(valueNode, v => v + "$"); + var f = BuildFieldDefinition(fieldNode); + var v = BuildRegex(f, valueNode, s => s + "$"); - return Filter.Regex(BuildFieldDefinition(fieldNode), value); + return Filter.Regex(f, v); } if (string.Equals(nodeIn.Name, "startswith", StringComparison.OrdinalIgnoreCase)) { - var value = BuildRegex(valueNode, v => "^" + v); + var f = BuildFieldDefinition(fieldNode); + var v = BuildRegex(f, valueNode, s => "^" + s); - return Filter.Regex(BuildFieldDefinition(fieldNode), value); + return Filter.Regex(f, v); } if (string.Equals(nodeIn.Name, "contains", StringComparison.OrdinalIgnoreCase)) { - var value = BuildRegex(valueNode, v => v); + var f = BuildFieldDefinition(fieldNode); + var v = BuildRegex(f, valueNode, s => s); - return Filter.Regex(BuildFieldDefinition(fieldNode), value); + return Filter.Regex(f, v); } throw new NotSupportedException(); @@ -107,53 +112,72 @@ namespace Squidex.Infrastructure.MongoDb.OData { if (nodeIn.OperatorKind == BinaryOperatorKind.NotEqual) { - var field = BuildFieldDefinition(nodeIn.Left); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); - return Filter.Or( - Filter.Not(Filter.Exists(field)), - Filter.Ne(field, BuildValue(nodeIn.Right))); + return Filter.Or(Filter.Not(Filter.Exists(f)), Filter.Ne(f, v)); } if (nodeIn.OperatorKind == BinaryOperatorKind.Equal) { - return Filter.Eq(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); + + return Filter.Eq(f, v); } if (nodeIn.OperatorKind == BinaryOperatorKind.LessThan) { - return Filter.Lt(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); + + return Filter.Lt(f, v); } if (nodeIn.OperatorKind == BinaryOperatorKind.LessThanOrEqual) { - return Filter.Lte(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); + + return Filter.Lte(f, v); } if (nodeIn.OperatorKind == BinaryOperatorKind.GreaterThan) { - return Filter.Gt(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); + + return Filter.Gt(f, v); } if (nodeIn.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual) { - return Filter.Gte(BuildFieldDefinition(nodeIn.Left), BuildValue(nodeIn.Right)); + var f = BuildFieldDefinition(nodeIn.Left); + var v = BuildValue(f, nodeIn.Right); + + return Filter.Gte(f, v); } } throw new NotSupportedException(); } - private static BsonRegularExpression BuildRegex(QueryNode node, Func formatter) + private BsonRegularExpression BuildRegex(string field, QueryNode node, Func formatter) + { + return new BsonRegularExpression(formatter(BuildValue(field, node).ToString()), "i"); + } + + private string BuildFieldDefinition(QueryNode nodeIn) { - return new BsonRegularExpression(formatter(BuildValue(node).ToString()), "i"); + return nodeIn.BuildFieldDefinition(convertProperty); } - private FieldDefinition BuildFieldDefinition(QueryNode nodeIn) + private object BuildValue(string field, QueryNode nodeIn) { - return nodeIn.BuildFieldDefinition(propertyCalculator); + return ValueConversion.Convert(field, ConstantVisitor.Visit(nodeIn), convertValue); } - private static object BuildValue(QueryNode nodeIn) + private object BuildValue(QueryNode nodeIn) { return ConstantVisitor.Visit(nodeIn); } diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/PropertyBuilder.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/PropertyBuilder.cs index ede103c64..c1fa51466 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/PropertyBuilder.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/PropertyBuilder.cs @@ -11,23 +11,23 @@ using MongoDB.Driver; namespace Squidex.Infrastructure.MongoDb.OData { - public delegate string PropertyCalculator(string[] parts); + public delegate string ConvertProperty(string[] parts); public static class PropertyBuilder { - private static readonly PropertyCalculator DefaultCalculator = parts => + private static readonly ConvertProperty Default = parts => { return string.Join(".", parts).ToPascalCase(); }; - public static StringFieldDefinition BuildFieldDefinition(this QueryNode node, PropertyCalculator propertyCalculator) + public static string BuildFieldDefinition(this QueryNode node, ConvertProperty convertProperty) { - propertyCalculator = propertyCalculator ?? DefaultCalculator; + convertProperty = convertProperty ?? Default; var propertyParts = node.Accept(PropertyNameVisitor.Instance).ToArray(); - var propertyName = propertyCalculator(propertyParts); + var propertyName = convertProperty(propertyParts); - return new StringFieldDefinition(propertyName); + return propertyName; } } } diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/SortBuilder.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/SortBuilder.cs index c19ca4305..9a43597e1 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/SortBuilder.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/SortBuilder.cs @@ -13,7 +13,7 @@ namespace Squidex.Infrastructure.MongoDb.OData { public static class SortBuilder { - public static SortDefinition BuildSort(this ODataUriParser query, PropertyCalculator propertyCalculator = null) + public static SortDefinition BuildSort(this ODataUriParser query, ConvertProperty propertyCalculator = null) { var orderBy = query.ParseOrderBy(); @@ -41,9 +41,9 @@ namespace Squidex.Infrastructure.MongoDb.OData return null; } - public static SortDefinition OrderBy(OrderByClause clause, PropertyCalculator propertyCalculator = null) + public static SortDefinition OrderBy(OrderByClause clause, ConvertProperty propertyCalculator = null) { - var propertyName = clause.Expression.BuildFieldDefinition(propertyCalculator); + var propertyName = clause.Expression.BuildFieldDefinition(propertyCalculator); if (clause.Direction == OrderByDirection.Ascending) { diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/ValueConversion.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/ValueConversion.cs new file mode 100644 index 000000000..8492c7098 --- /dev/null +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/OData/ValueConversion.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Infrastructure.MongoDb.OData +{ + public delegate object ConvertValue(string field, object value); + + public static class ValueConversion + { + public static object Convert(string field, object value, ConvertValue converter = null) + { + if (converter == null) + { + return value; + } + + return converter(field, value); + } + } +} diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Assets/OData/ODataQueryTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Assets/OData/ODataQueryTests.cs index 6a0a6be00..cfe1a5b51 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Assets/OData/ODataQueryTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Assets/OData/ODataQueryTests.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; using FakeItEasy; using Microsoft.OData.Edm; using MongoDB.Bson.Serialization; @@ -12,6 +13,7 @@ using MongoDB.Driver; using Squidex.Domain.Apps.Entities.Assets.Edm; using Squidex.Domain.Apps.Entities.MongoDb.Assets; using Squidex.Domain.Apps.Entities.MongoDb.Assets.Visitors; +using Squidex.Domain.Apps.Entities.Tags; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb.OData; using Xunit; @@ -20,15 +22,26 @@ namespace Squidex.Domain.Apps.Entities.Assets.OData { public class ODataQueryTests { + private readonly ITagService tagService = A.Fake(); private readonly IBsonSerializerRegistry registry = BsonSerializer.SerializerRegistry; private readonly IBsonSerializer serializer = BsonSerializer.SerializerRegistry.GetSerializer(); private readonly IEdmModel edmModel = EdmAssetModel.Edm; + private readonly Guid appId = Guid.NewGuid(); + private readonly ConvertValue valueConverter; static ODataQueryTests() { InstantSerializer.Register(); } + public ODataQueryTests() + { + A.CallTo(() => tagService.GetTagIdsAsync(appId, TagGroups.Assets, A.That.Contains("tag1"))) + .Returns(new[] { "normalized1" }); + + valueConverter = FindExtensions.CreateValueConverter(appId, tagService); + } + [Fact] public void Should_parse_query() { @@ -82,6 +95,24 @@ namespace Squidex.Domain.Apps.Entities.Assets.OData Assert.Equal(o, i); } + [Fact] + public void Should_make_query_with_normalized_tags() + { + var i = F("$filter=tags eq 'tag1'"); + var o = C("{ 'Tags' : 'normalized1' }"); + + Assert.Equal(o, i); + } + + [Fact] + public void Should_make_query_with_tags() + { + var i = F("$filter=tags eq 'tag2'"); + var o = C("{ 'Tags' : 'tag2' }"); + + Assert.Equal(o, i); + } + [Fact] public void Should_make_query_with_fileName() { @@ -246,7 +277,7 @@ namespace Squidex.Domain.Apps.Entities.Assets.OData var parser = edmModel.ParseQuery(value); var query = - parser.BuildFilter() + parser.BuildFilter(convertValue: valueConverter) .Filter.Render(serializer, registry).ToString(); return query; diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs index 8911861be..02d6b9b6d 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs @@ -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), A.That.Matches(x => x.ODataQuery == string.Empty)); + var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), Query.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), A.That.Matches(x => x.ODataQuery == "query"))); + return Assert.ThrowsAsync(() => sut.QueryAsync(context.WithSchemaId(schemaId), Query.Empty.WithODataQuery("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), A.Ignored); + var result = await sut.QueryAsync(context.WithSchemaId(schemaId).WithArchived(archive), Query.Empty.WithIds(ids)); Assert.Equal(ids, result.Select(x => x.Id).ToList()); Assert.Equal(total, result.Total);