Browse Source

Asset field resolver.

pull/336/head
Sebastian Stehle 8 years ago
parent
commit
d683e6d9c8
  1. 31
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs
  2. 14
      src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IAssetUrlGenerator.cs
  3. 51
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs
  4. 55
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  5. 6
      src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs
  6. 6
      src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs
  7. 41
      src/Squidex.Domain.Apps.Entities/QueryContext.cs
  8. 47
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  9. 3
      src/Squidex/Config/Domain/EntitiesServices.cs
  10. 8
      src/Squidex/Pipeline/UrlGenerator.cs
  11. 78
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  12. 68
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs
  13. 18
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

31
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/FieldConverters.cs

@ -63,6 +63,37 @@ namespace Squidex.Domain.Apps.Core.ConvertContent
};
}
public static FieldConverter ResolveAssetUrls(ISet<string> fields, IAssetUrlGenerator urlGenerator)
{
if (fields?.Any() != true)
{
return (data, field) => data;
}
var isAll = fields.First() == "*";
return (data, field) =>
{
if (field is IField<AssetsFieldProperties> assetField && (isAll || fields.Contains(field.Name)))
{
foreach (var partition in data)
{
if (partition.Value is JArray array)
{
for (var i = 0; i < array.Count; i++)
{
var id = array[i].ToString();
array[i] = urlGenerator.GenerateUrl(id);
}
}
}
}
return data;
};
}
public static FieldConverter ResolveInvariant(LanguagesConfig config)
{
var codeForInvariant = InvariantPartitioning.Instance.Master.Key;

14
src/Squidex.Domain.Apps.Core.Operations/ConvertContent/IAssetUrlGenerator.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.Core.ConvertContent
{
public interface IAssetUrlGenerator
{
string GenerateUrl(string assetId);
}
}

51
src/Squidex.Domain.Apps.Entities/Contents/ContentQueryContext.cs

@ -1,51 +0,0 @@
// ==========================================================================
// 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<ContentQueryContext>
{
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 WithUnpublished(bool unpublished)
{
return Clone(c => c.Base = c.Base.WithUnpublished(unpublished));
}
public ContentQueryContext WithSchemaId(Guid id)
{
return Clone(c => c.SchemaIdOrName = id.ToString());
}
}
}

55
src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs

@ -40,12 +40,14 @@ namespace Squidex.Domain.Apps.Entities.Contents
private readonly IContentRepository contentRepository;
private readonly IContentVersionLoader contentVersionLoader;
private readonly IAppProvider appProvider;
private readonly IAssetUrlGenerator assetUrlGenerator;
private readonly IScriptEngine scriptEngine;
private readonly ContentOptions options;
private readonly EdmModelBuilder modelBuilder;
public ContentQueryService(
IAppProvider appProvider,
IAssetUrlGenerator assetUrlGenerator,
IContentRepository contentRepository,
IContentVersionLoader contentVersionLoader,
IScriptEngine scriptEngine,
@ -53,6 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
EdmModelBuilder modelBuilder)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(assetUrlGenerator, nameof(assetUrlGenerator));
Guard.NotNull(contentRepository, nameof(contentRepository));
Guard.NotNull(contentVersionLoader, nameof(contentVersionLoader));
Guard.NotNull(modelBuilder, nameof(modelBuilder));
@ -60,6 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
Guard.NotNull(scriptEngine, nameof(scriptEngine));
this.appProvider = appProvider;
this.assetUrlGenerator = assetUrlGenerator;
this.contentRepository = contentRepository;
this.contentVersionLoader = contentVersionLoader;
this.modelBuilder = modelBuilder;
@ -67,66 +71,66 @@ namespace Squidex.Domain.Apps.Entities.Contents
this.scriptEngine = scriptEngine;
}
public Task ThrowIfSchemaNotExistsAsync(ContentQueryContext context)
public Task ThrowIfSchemaNotExistsAsync(QueryContext context, string schemaIdOrName)
{
return GetSchemaAsync(context);
return GetSchemaAsync(context, schemaIdOrName);
}
public async Task<IContentEntity> FindContentAsync(ContentQueryContext context, Guid id, long version = -1)
public async Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = -1)
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context);
var schema = await GetSchemaAsync(context, schemaIdOrName);
CheckPermission(schema, context.Base.User);
CheckPermission(schema, context.User);
using (Profiler.TraceMethod<ContentQueryService>())
{
var isVersioned = version > EtagVersion.Empty;
var status = GetFindStatus(context.Base);
var status = GetFindStatus(context);
var content =
isVersioned ?
await FindContentByVersionAsync(id, version) :
await FindContentAsync(context.Base, id, status, schema);
await FindContentAsync(context, id, status, schema);
if (content == null || (content.Status != Status.Published && !context.Base.IsFrontendClient) || content.SchemaId.Id != schema.Id)
if (content == null || (content.Status != Status.Published && !context.IsFrontendClient) || content.SchemaId.Id != schema.Id)
{
throw new DomainObjectNotFoundException(id.ToString(), typeof(IContentEntity));
}
return Transform(context.Base, schema, true, content);
return Transform(context, schema, true, content);
}
}
public async Task<IResultList<IContentEntity>> QueryAsync(ContentQueryContext context, Q query)
public async Task<IResultList<IContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query)
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaAsync(context);
var schema = await GetSchemaAsync(context, schemaIdOrName);
CheckPermission(schema, context.Base.User);
CheckPermission(schema, context.User);
using (Profiler.TraceMethod<ContentQueryService>())
{
var status = GetQueryStatus(context.Base);
var status = GetQueryStatus(context);
IResultList<IContentEntity> contents;
if (query.Ids?.Count > 0)
{
contents = await contentRepository.QueryAsync(context.Base.App, schema, status, new HashSet<Guid>(query.Ids));
contents = await contentRepository.QueryAsync(context.App, schema, status, new HashSet<Guid>(query.Ids));
contents = Sort(contents, query.Ids);
}
else
{
var parsedQuery = ParseQuery(context.Base, query.ODataQuery, schema);
var parsedQuery = ParseQuery(context, query.ODataQuery, schema);
contents = await contentRepository.QueryAsync(context.Base.App, schema, status, parsedQuery);
contents = await contentRepository.QueryAsync(context.App, schema, status, parsedQuery);
}
return Transform(context.Base, schema, true, contents);
return Transform(context, schema, true, contents);
}
}
@ -183,7 +187,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
private static IEnumerable<FieldConverter> GenerateConverters(QueryContext context, bool checkType)
private IEnumerable<FieldConverter> GenerateConverters(QueryContext context, bool checkType)
{
if (!context.IsFrontendClient)
{
@ -208,6 +212,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
yield return FieldConverters.FilterLanguages(context.App.LanguagesConfig, context.Languages);
}
if (context.AssetFieldsToResolve?.Any() == true)
{
yield return FieldConverters.ResolveAssetUrls(context.AssetFieldsToResolve, assetUrlGenerator);
}
}
}
@ -244,23 +253,23 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<ISchemaEntity> GetSchemaAsync(ContentQueryContext context)
public async Task<ISchemaEntity> GetSchemaAsync(QueryContext context, string schemaIdOrName)
{
ISchemaEntity schema = null;
if (Guid.TryParse(context.SchemaIdOrName, out var id))
if (Guid.TryParse(schemaIdOrName, out var id))
{
schema = await appProvider.GetSchemaAsync(context.Base.App.Id, id);
schema = await appProvider.GetSchemaAsync(context.App.Id, id);
}
if (schema == null)
{
schema = await appProvider.GetSchemaAsync(context.Base.App.Id, context.SchemaIdOrName);
schema = await appProvider.GetSchemaAsync(context.App.Id, schemaIdOrName);
}
if (schema == null)
{
throw new DomainObjectNotFoundException(context.SchemaIdOrName, typeof(ISchemaEntity));
throw new DomainObjectNotFoundException(schemaIdOrName, typeof(ISchemaEntity));
}
return schema;

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

@ -13,10 +13,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
public interface IContentQueryService
{
Task<IResultList<IContentEntity>> QueryAsync(ContentQueryContext context, Q query);
Task<IResultList<IContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query);
Task<IContentEntity> FindContentAsync(ContentQueryContext context, Guid id, long version = EtagVersion.Any);
Task<IContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = EtagVersion.Any);
Task ThrowIfSchemaNotExistsAsync(ContentQueryContext context);
Task ThrowIfSchemaNotExistsAsync(QueryContext context, string schemaIdOrName);
}
}

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

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (content == null)
{
content = await contentQuery.FindContentAsync(new ContentQueryContext(context).WithSchemaId(schemaId), id);
content = await contentQuery.FindContentAsync(context, schemaId.ToString(), id);
if (content != null)
{
@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public async Task<IResultList<IContentEntity>> QueryContentsAsync(string schemaIdOrName, string query)
{
var result = await contentQuery.QueryAsync(new ContentQueryContext(context).WithSchemaName(schemaIdOrName), Q.Empty.WithODataQuery(query));
var result = await contentQuery.QueryAsync(context, schemaIdOrName, Q.Empty.WithODataQuery(query));
foreach (var content in result)
{
@ -119,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
if (notLoadedContents.Count > 0)
{
var result = await contentQuery.QueryAsync(new ContentQueryContext(context).WithSchemaId(schemaId), Q.Empty.WithIds(notLoadedContents));
var result = await contentQuery.QueryAsync(context, schemaId.ToString(), Q.Empty.WithIds(notLoadedContents));
foreach (var content in result)
{

41
src/Squidex.Domain.Apps.Entities/QueryContext.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Squidex.Domain.Apps.Entities.Apps;
@ -15,6 +16,8 @@ namespace Squidex.Domain.Apps.Entities
{
public sealed class QueryContext : Cloneable<QueryContext>
{
private static readonly char[] Separators = { ',', ';' };
public ClaimsPrincipal User { get; private set; }
public IAppEntity App { get; private set; }
@ -25,7 +28,9 @@ namespace Squidex.Domain.Apps.Entities
public bool Unpublished { get; set; }
public IEnumerable<Language> Languages { get; private set; }
public ISet<string> AssetFieldsToResolve { get; private set; }
public ISet<Language> Languages { get; private set; }
private QueryContext()
{
@ -51,19 +56,45 @@ namespace Squidex.Domain.Apps.Entities
return Clone(c => c.Flatten = flatten);
}
public QueryContext WithAssetFieldsToResolve(IEnumerable<string> fieldNames)
{
if (fieldNames != null)
{
return Clone(c =>
{
var fields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var part in fieldNames)
{
foreach (var fieldName in part.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
fields.Add(fieldName.Trim());
}
}
c.AssetFieldsToResolve = fields;
});
}
return this;
}
public QueryContext WithLanguages(IEnumerable<string> languageCodes)
{
if (languageCodes != null)
{
return Clone(c =>
{
var languages = new List<Language>();
var languages = new HashSet<Language>();
foreach (var iso2Code in languageCodes)
foreach (var part in languageCodes)
{
if (Language.TryGetLanguage(iso2Code, out var language))
foreach (var iso2Code in part.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
languages.Add(language);
if (Language.TryGetLanguage(iso2Code.Trim(), out var language))
{
languages.Add(language);
}
}
}

47
src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs

@ -61,7 +61,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(2)]
public async Task<IActionResult> PostGraphQL(string app, [FromBody] GraphQLQuery query)
{
var result = await graphQl.QueryAsync(Context().Base, query);
var result = await graphQl.QueryAsync(Context(), query);
if (result.HasError)
{
@ -92,7 +92,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(2)]
public async Task<IActionResult> PostGraphQLBatch(string app, [FromBody] GraphQLQuery[] batch)
{
var result = await graphQl.QueryAsync(Context().Base, batch);
var result = await graphQl.QueryAsync(Context(), batch);
if (result.HasError)
{
@ -124,14 +124,14 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(2)]
public async Task<IActionResult> GetContents(string app, string name, [FromQuery] bool archived = false, [FromQuery] string ids = null)
{
var context = Context().WithArchived(archived).WithSchemaName(name);
var context = Context().WithArchived(archived);
var result = await contentQuery.QueryAsync(context, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var result = await contentQuery.QueryAsync(context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var response = new ContentsDto
{
Total = result.Total,
Items = result.Take(200).Select(x => ContentDto.FromContent(x, context.Base)).ToArray()
Items = result.Take(200).Select(x => ContentDto.FromContent(x, context)).ToArray()
};
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
@ -163,10 +163,10 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetContent(string app, string name, Guid id)
{
var context = Context().WithSchemaName(name);
var content = await contentQuery.FindContentAsync(context, id);
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id);
var response = ContentDto.FromContent(content, context.Base);
var response = ContentDto.FromContent(content, context);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -199,10 +199,10 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetContentVersion(string app, string name, Guid id, int version)
{
var context = Context().WithSchemaName(name);
var content = await contentQuery.FindContentAsync(context, id, version);
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id, version);
var response = ContentDto.FromContent(content, context.Base);
var response = ContentDto.FromContent(content, context);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -235,7 +235,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PostContent(string app, string name, [FromBody] NamedContentData request, [FromQuery] bool publish = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = new CreateContent { ContentId = Guid.NewGuid(), Data = request.ToCleaned(), Publish = publish };
@ -269,7 +269,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PutContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = new UpdateContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
var context = await CommandBus.PublishAsync(command);
@ -302,7 +302,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PatchContent(string app, string name, Guid id, [FromBody] NamedContentData request, [FromQuery] bool asDraft = false)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = new PatchContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
var context = await CommandBus.PublishAsync(command);
@ -334,7 +334,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PublishContent(string app, string name, Guid id, string dueTime = null)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = CreateCommand(id, Status.Published, dueTime);
@ -364,7 +364,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> UnpublishContent(string app, string name, Guid id, string dueTime = null)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = CreateCommand(id, Status.Draft, dueTime);
@ -394,7 +394,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> ArchiveContent(string app, string name, Guid id, string dueTime = null)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = CreateCommand(id, Status.Archived, dueTime);
@ -424,7 +424,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> RestoreContent(string app, string name, Guid id, string dueTime = null)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = CreateCommand(id, Status.Draft, dueTime);
@ -453,7 +453,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DiscardChanges(string app, string name, Guid id)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = new DiscardChanges { ContentId = id };
@ -481,7 +481,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DeleteContent(string app, string name, Guid id)
{
await contentQuery.ThrowIfSchemaNotExistsAsync(Context().WithSchemaName(name));
await contentQuery.ThrowIfSchemaNotExistsAsync(Context(), name);
var command = new DeleteContent { ContentId = id };
@ -507,11 +507,12 @@ namespace Squidex.Areas.Api.Controllers.Contents
return new ChangeContentStatus { Status = status, ContentId = id, DueTime = dt };
}
private ContentQueryContext Context()
private QueryContext Context()
{
return new ContentQueryContext(QueryContext.Create(App, User)
.WithLanguages(Request.Headers["X-Languages"]))
return QueryContext.Create(App, User)
.WithAssetFieldsToResolve(Request.Headers["X-AssetFields"])
.WithFlatten(Request.Headers.ContainsKey("X-Flatten"))
.WithLanguages(Request.Headers["X-Languages"])
.WithUnpublished(Request.Headers.ContainsKey("X-Unpublished"));
}
}

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

@ -13,6 +13,7 @@ using Migrate_01;
using Migrate_01.Migrations;
using Orleans;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Core.Tags;
@ -60,7 +61,7 @@ namespace Squidex.Config.Domain
c.GetRequiredService<IOptions<MyUrlsOptions>>(),
c.GetRequiredService<IAssetStore>(),
exposeSourceUrl))
.As<IGraphQLUrlGenerator>().As<IRuleUrlGenerator>();
.As<IGraphQLUrlGenerator>().As<IRuleUrlGenerator>().As<IAssetUrlGenerator>();
services.AddSingletonAs<HistoryService>()
.As<IEventConsumer>()

8
src/Squidex/Pipeline/UrlGenerator.cs

@ -8,6 +8,7 @@
using System;
using Microsoft.Extensions.Options;
using Squidex.Config;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Assets;
@ -19,7 +20,7 @@ using Squidex.Infrastructure.Assets;
namespace Squidex.Pipeline
{
public sealed class UrlGenerator : IGraphQLUrlGenerator, IRuleUrlGenerator
public sealed class UrlGenerator : IGraphQLUrlGenerator, IRuleUrlGenerator, IAssetUrlGenerator
{
private readonly IAssetStore assetStore;
private readonly MyUrlsOptions urlsOptions;
@ -44,6 +45,11 @@ namespace Squidex.Pipeline
return urlsOptions.BuildUrl($"api/assets/{asset.Id}?version={asset.Version}&width=100&mode=Max");
}
public string GenerateUrl(string assetId)
{
return urlsOptions.BuildUrl($"api/assets/{assetId}");
}
public string GenerateAssetUrl(IAppEntity app, IAssetEntity asset)
{
return urlsOptions.BuildUrl($"api/assets/{asset.Id}?version={asset.Version}");

78
tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

@ -5,7 +5,9 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using FakeItEasy;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
@ -18,15 +20,23 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
public class FieldConvertersTests
{
private readonly IAssetUrlGenerator assetUrlGenerator = A.Fake<IAssetUrlGenerator>();
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.EN, Language.DE);
private readonly RootField<NumberFieldProperties> numberField = Fields.Number(1, "1", Partitioning.Invariant);
private readonly RootField<JsonFieldProperties> jsonField = Fields.Json(1, "1", Partitioning.Invariant);
private readonly RootField<StringFieldProperties> stringLanguageField = Fields.String(1, "1", Partitioning.Language);
private readonly RootField<StringFieldProperties> stringInvariantField = Fields.String(1, "1", Partitioning.Invariant);
private readonly RootField<JsonFieldProperties> jsonField = Fields.Json(1, "1", Partitioning.Invariant);
private readonly RootField<NumberFieldProperties> numberField = Fields.Number(1, "1", Partitioning.Invariant);
private readonly RootField<AssetsFieldProperties> assetsField = Fields.Assets(1, "1", Partitioning.Invariant);
private readonly RootField<ArrayFieldProperties> arrayField = Fields.Array(1, "1", Partitioning.Invariant,
Fields.Number(1, "field1"),
Fields.Number(2, "field2").Hide());
public FieldConvertersTests()
{
A.CallTo(() => assetUrlGenerator.GenerateUrl(A<string>.Ignored))
.ReturnsLazily(ctx => $"url/to/{ctx.GetArgument<string>(0)}");
}
[Fact]
public void Should_filter_for_value_conversion()
{
@ -406,5 +416,69 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
Assert.Same(source, result);
}
[Fact]
public void Should_convert_asset_ids_to_urls()
{
var source =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", new JArray("url/to/1", "url/to/2"));
var rtesult = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "1" }), assetUrlGenerator)(source, assetsField);
Assert.Equal(expected, source);
}
[Fact]
public void Should_convert_asset_ids_to_urls_for_wildcard_fields()
{
var source =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", new JArray("url/to/1", "url/to/2"));
var rtesult = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "*" }), assetUrlGenerator)(source, assetsField);
Assert.Equal(expected, source);
}
[Fact]
public void Should_not_convert_asset_ids_to_urls_when_field_does_not_match()
{
var source =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var rtesult = FieldConverters.ResolveAssetUrls(new HashSet<string>(new[] { "2" }), assetUrlGenerator)(source, assetsField);
Assert.Equal(expected, source);
}
[Fact]
public void Should_not_convert_asset_ids_to_urls_when_field_is_specified()
{
var source =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var expected =
new ContentFieldData()
.AddValue("iv", new JArray("1", "2"));
var rtesult = FieldConverters.ResolveAssetUrls(null, assetUrlGenerator)(source, assetsField);
Assert.Equal(expected, source);
}
}
}

68
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs

@ -15,6 +15,7 @@ using Microsoft.Extensions.Options;
using Microsoft.OData;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Entities.Apps;
@ -40,6 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
private readonly IAppEntity app = A.Fake<IAppEntity>();
private readonly IAppProvider appProvider = A.Fake<IAppProvider>();
private readonly IAssetUrlGenerator urlGenerator = A.Fake<IAssetUrlGenerator>();
private readonly Guid appId = Guid.NewGuid();
private readonly Guid schemaId = Guid.NewGuid();
private readonly Guid contentId = Guid.NewGuid();
@ -51,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private readonly ClaimsPrincipal user;
private readonly ClaimsIdentity identity = new ClaimsIdentity();
private readonly EdmModelBuilder modelBuilder = A.Fake<EdmModelBuilder>();
private readonly ContentQueryContext context;
private readonly QueryContext context;
private readonly ContentQueryService sut;
public ContentQueryServiceTests()
@ -68,9 +70,15 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => schema.SchemaDef).Returns(new Schema(schemaName));
A.CallTo(() => schema.ScriptQuery).Returns(script);
context = new ContentQueryContext(QueryContext.Create(app, user));
context = QueryContext.Create(app, user);
sut = new ContentQueryService(appProvider, contentRepository, contentVersionLoader, scriptEngine, Options.Create(new ContentOptions()), modelBuilder);
sut = new ContentQueryService(
appProvider,
urlGenerator,
contentRepository,
contentVersionLoader,
scriptEngine,
Options.Create(new ContentOptions()), modelBuilder);
}
[Fact]
@ -78,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchema();
var result = await sut.GetSchemaAsync(context.WithSchemaId(schemaId));
var result = await sut.GetSchemaAsync(context, schemaId.ToString());
Assert.Equal(schema, result);
}
@ -88,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchema();
var result = await sut.GetSchemaAsync(context.WithSchemaName(schemaName));
var result = await sut.GetSchemaAsync(context, schemaName);
Assert.Equal(schema, result);
}
@ -98,7 +106,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchemaNotFound();
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaAsync(context.WithSchemaName(schemaName)));
var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.GetSchemaAsync(ctx, schemaName));
}
[Fact]
@ -106,7 +116,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
SetupSchemaNotFound();
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.ThrowIfSchemaNotExistsAsync(context.WithSchemaName(schemaName)));
var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.ThrowIfSchemaNotExistsAsync(ctx, schemaName));
}
public static IEnumerable<object[]> SingleDataFrontend = new[]
@ -127,9 +139,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims(false, false);
SetupSchema();
var ctx = context.WithSchemaId(schemaId);
var ctx = context;
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindContentAsync(ctx, contentId));
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.FindContentAsync(ctx, schemaId.ToString(), contentId));
}
[Fact]
@ -141,9 +153,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => contentRepository.FindContentAsync(app, schema, new[] { Status.Published }, contentId))
.Returns((IContentEntity)null);
var ctx = context.WithSchemaId(schemaId);
var ctx = context;
await Assert.ThrowsAsync<DomainObjectNotFoundException>(async () => await sut.FindContentAsync(ctx, contentId));
await Assert.ThrowsAsync<DomainObjectNotFoundException>(async () => await sut.FindContentAsync(ctx, schemaId.ToString(), contentId));
}
[Theory]
@ -159,9 +171,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => contentRepository.FindContentAsync(app, schema, A<Status[]>.That.IsSameSequenceAs(status), contentId))
.Returns(content);
var ctx = context.WithSchemaId(schemaId).WithUnpublished(unpublished);
var ctx = context.WithUnpublished(unpublished);
var result = await sut.FindContentAsync(ctx, contentId);
var result = await sut.FindContentAsync(ctx, schemaId.ToString(), contentId);
Assert.Equal(contentTransformed, result.Data);
Assert.Equal(content.Id, result.Id);
@ -183,9 +195,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => contentRepository.FindContentAsync(app, schema, A<Status[]>.That.IsSameSequenceAs(status), contentId))
.Returns(content);
var ctx = context.WithSchemaId(schemaId).WithUnpublished(unpublished);
var ctx = context.WithUnpublished(unpublished);
var result = await sut.FindContentAsync(ctx, contentId);
var result = await sut.FindContentAsync(ctx, schemaId.ToString(), contentId);
Assert.Equal(contentTransformed, result.Data);
Assert.Equal(content.Id, result.Id);
@ -206,9 +218,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => contentVersionLoader.LoadAsync(contentId, 10))
.Returns(content);
var ctx = context.WithSchemaId(schemaId);
var ctx = context;
var result = await sut.FindContentAsync(ctx, contentId, 10);
var result = await sut.FindContentAsync(ctx, schemaId.ToString(), contentId, 10);
Assert.Equal(contentTransformed, result.Data);
Assert.Equal(content.Id, result.Id);
@ -236,9 +248,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims(false, false);
SetupSchema();
var ctx = context.WithSchemaId(schemaId);
var ctx = context;
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.QueryAsync(ctx, Q.Empty));
await Assert.ThrowsAsync<DomainForbiddenException>(() => sut.QueryAsync(ctx, schemaId.ToString(), Q.Empty));
}
[Theory]
@ -254,9 +266,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupScripting(contentId);
SetupContents(status, count, total, content);
var ctx = context.WithSchemaId(schemaId).WithArchived(archive).WithUnpublished(unpublished);
var ctx = context.WithArchived(archive).WithUnpublished(unpublished);
var result = await sut.QueryAsync(ctx, Q.Empty);
var result = await sut.QueryAsync(ctx, schemaId.ToString(), Q.Empty);
Assert.Equal(contentData, result[0].Data);
Assert.Equal(content.Id, result[0].Id);
@ -280,9 +292,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupScripting(contentId);
SetupContents(status, count, total, content);
var ctx = context.WithSchemaId(schemaId).WithArchived(archive).WithUnpublished(unpublished);
var ctx = context.WithArchived(archive).WithUnpublished(unpublished);
var result = await sut.QueryAsync(ctx, Q.Empty);
var result = await sut.QueryAsync(ctx, schemaId.ToString(), Q.Empty);
Assert.Equal(contentData, result[0].Data);
Assert.Equal(contentId, result[0].Id);
@ -302,7 +314,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
A.CallTo(() => modelBuilder.BuildEdmModel(schema, app))
.Throws(new ODataException());
return Assert.ThrowsAsync<ValidationException>(() => sut.QueryAsync(context.WithSchemaId(schemaId), Q.Empty.WithODataQuery("query")));
return Assert.ThrowsAsync<ValidationException>(() => sut.QueryAsync(context, schemaId.ToString(), Q.Empty.WithODataQuery("query")));
}
public static IEnumerable<object[]> ManyIdDataFrontend = new[]
@ -334,9 +346,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupScripting(ids.ToArray());
SetupContents(status, total, ids);
var ctx = context.WithSchemaId(schemaId).WithArchived(archive).WithUnpublished(unpublished);
var ctx = context.WithArchived(archive).WithUnpublished(unpublished);
var result = await sut.QueryAsync(ctx, Q.Empty.WithIds(ids));
var result = await sut.QueryAsync(ctx, schemaId.ToString(), Q.Empty.WithIds(ids));
Assert.Equal(ids, result.Select(x => x.Id).ToList());
Assert.Equal(total, result.Total);
@ -358,9 +370,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupScripting(ids.ToArray());
SetupContents(status, total, ids);
var ctx = context.WithSchemaId(schemaId).WithArchived(archive).WithUnpublished(unpublished);
var ctx = context.WithArchived(archive).WithUnpublished(unpublished);
var result = await sut.QueryAsync(ctx, Q.Empty.WithIds(ids));
var result = await sut.QueryAsync(ctx, schemaId.ToString(), Q.Empty.WithIds(ids));
Assert.Equal(ids, result.Select(x => x.Id).ToList());
Assert.Equal(total, result.Total);

18
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -283,7 +283,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var content = CreateContent(Guid.NewGuid(), Guid.Empty, Guid.Empty);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5")))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5")))
.Returns(ResultList.Create(0, content));
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
@ -422,7 +422,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var content = CreateContent(Guid.NewGuid(), Guid.Empty, Guid.Empty);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5")))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), A<Q>.That.Matches(x => x.ODataQuery == "?$top=30&$skip=5")))
.Returns(ResultList.Create(10, content));
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
@ -547,7 +547,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}}
}}";
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any))
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any))
.Returns(content);
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
@ -642,10 +642,10 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}}
}}";
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any))
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any))
.Returns(content);
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), A<Q>.Ignored))
A.CallTo(() => contentQuery.QueryAsync(MatchsContentContext(), schemaId.ToString(), A<Q>.Ignored))
.Returns(ResultList.Create(0, contentRef));
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
@ -700,7 +700,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}}
}}";
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any))
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any))
.Returns(content);
A.CallTo(() => assetQuery.QueryAsync(MatchsAssetContext(), A<Q>.Ignored))
@ -814,7 +814,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}}
}}";
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), contentId, EtagVersion.Any))
A.CallTo(() => contentQuery.FindContentAsync(MatchsContentContext(), schemaId.ToString(), contentId, EtagVersion.Any))
.Returns(content);
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
@ -829,9 +829,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return A<QueryContext>.That.Matches(x => x.App == app && x.User == user && !x.Archived);
}
private ContentQueryContext MatchsContentContext()
private QueryContext MatchsContentContext()
{
return A<ContentQueryContext>.That.Matches(x => x.Base.App == app && x.Base.User == user && !x.Base.Archived && x.SchemaIdOrName == schema.Id.ToString());
return A<QueryContext>.That.Matches(x => x.App == app && x.User == user && !x.Archived);
}
}
}

Loading…
Cancel
Save