Browse Source

Fixes

pull/380/head
Sebastian Stehle 7 years ago
parent
commit
e142a3b0e1
  1. 8
      src/Squidex.Domain.Apps.Entities/Assets/AssetQueryService.cs
  2. 2
      src/Squidex.Domain.Apps.Entities/Assets/IAssetQueryService.cs
  3. 70
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  4. 107
      src/Squidex.Domain.Apps.Entities/Contents/ContextExtensions.cs
  5. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs
  6. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  7. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphQLService.cs
  8. 8
      src/Squidex.Domain.Apps.Entities/Contents/IContentQueryService.cs
  9. 4
      src/Squidex.Domain.Apps.Entities/Contents/QueryExecutionContext.cs
  10. 15
      src/Squidex.Domain.Apps.Entities/Contents/StatusForApi.cs
  11. 14
      src/Squidex.Domain.Apps.Entities/Context.cs
  12. 116
      src/Squidex.Domain.Apps.Entities/QueryContext.cs
  13. 6
      src/Squidex.Web/ApiController.cs
  14. 5
      src/Squidex.Web/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs
  15. 18
      src/Squidex.Web/ContextExtensions.cs
  16. 5
      src/Squidex.Web/Pipeline/AppResolver.cs
  17. 9
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  18. 47
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  19. 4
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  20. 2
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  21. 3
      src/Squidex/Areas/Api/Controllers/UI/UIController.cs
  22. 4
      src/Squidex/Config/Web/WebServices.cs
  23. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetQueryServiceTests.cs
  24. 36
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEnricherTests.cs
  25. 32
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentQueryServiceTests.cs
  26. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  27. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs
  28. 2
      tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs
  29. 31
      tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs
  30. 40
      tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs
  31. 2
      tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs
  32. 6
      tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs

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

@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
return await assetEnricher.EnrichAsync(assets);
}
public async Task<IResultList<IEnrichedAssetEntity>> QueryAsync(QueryContext context, Q query)
public async Task<IResultList<IEnrichedAssetEntity>> QueryAsync(Context context, Q query)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(query, nameof(query));
@ -91,14 +91,14 @@ namespace Squidex.Domain.Apps.Entities.Assets
return ResultList.Create(assets.Total, enriched);
}
private async Task<IResultList<IAssetEntity>> QueryByQueryAsync(QueryContext context, Q query)
private async Task<IResultList<IAssetEntity>> QueryByQueryAsync(Context context, Q query)
{
var parsedQuery = ParseQuery(context, query.ODataQuery);
return await assetRepository.QueryAsync(context.App.Id, parsedQuery);
}
private async Task<IResultList<IAssetEntity>> QueryByIdsAsync(QueryContext context, Q query)
private async Task<IResultList<IAssetEntity>> QueryByIdsAsync(Context context, Q query)
{
var assets = await assetRepository.QueryAsync(context.App.Id, new HashSet<Guid>(query.Ids));
@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
return assets.SortSet(x => x.Id, ids);
}
private Query ParseQuery(QueryContext context, string query)
private Query ParseQuery(Context context, string query)
{
try
{

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

@ -18,7 +18,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
Task<IReadOnlyList<IEnrichedAssetEntity>> QueryByHashAsync(Guid appId, string hash);
Task<IResultList<IEnrichedAssetEntity>> QueryAsync(QueryContext contex, Q query);
Task<IResultList<IEnrichedAssetEntity>> QueryAsync(Context contex, Q query);
Task<IEnrichedAssetEntity> FindAssetAsync(Guid id);
}

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

@ -78,13 +78,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
this.scriptEngine = scriptEngine;
}
public async Task<IEnrichedContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = -1)
public async Task<IEnrichedContentEntity> FindContentAsync(Context context, string schemaIdOrName, Guid id, long version = -1)
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema);
CheckPermission(context, schema);
using (Profiler.TraceMethod<ContentQueryService>())
{
@ -108,13 +108,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query)
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, string schemaIdOrName, Q query)
{
Guard.NotNull(context, nameof(context));
var schema = await GetSchemaOrThrowAsync(context, schemaIdOrName);
CheckPermission(context.User, schema);
CheckPermission(context, schema);
using (Profiler.TraceMethod<ContentQueryService>())
{
@ -133,7 +133,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids)
public async Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, IReadOnlyList<Guid> ids)
{
Guard.NotNull(context, nameof(context));
@ -148,13 +148,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
var contents = await QueryCoreAsync(context, ids);
var permissions = context.User.Permissions();
foreach (var group in contents.GroupBy(x => x.Schema.Id))
{
var schema = group.First().Schema;
if (HasPermission(permissions, schema))
if (HasPermission(context, schema))
{
var enriched = await TransformCoreAsync(context, schema, group.Select(x => x.Content));
@ -166,21 +164,21 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
private async Task<IResultList<IEnrichedContentEntity>> TransformAsync(QueryContext context, ISchemaEntity schema, IResultList<IContentEntity> contents)
private async Task<IResultList<IEnrichedContentEntity>> TransformAsync(Context context, ISchemaEntity schema, IResultList<IContentEntity> contents)
{
var transformed = await TransformCoreAsync(context, schema, contents);
return ResultList.Create(contents.Total, transformed);
}
private async Task<IEnrichedContentEntity> TransformAsync(QueryContext context, ISchemaEntity schema, IContentEntity content)
private async Task<IEnrichedContentEntity> TransformAsync(Context context, ISchemaEntity schema, IContentEntity content)
{
var transformed = await TransformCoreAsync(context, schema, Enumerable.Repeat(content, 1));
return transformed[0];
}
private async Task<IReadOnlyList<IEnrichedContentEntity>> TransformCoreAsync(QueryContext context, ISchemaEntity schema, IEnumerable<IContentEntity> contents)
private async Task<IReadOnlyList<IEnrichedContentEntity>> TransformCoreAsync(Context context, ISchemaEntity schema, IEnumerable<IContentEntity> contents)
{
using (Profiler.TraceMethod<ContentQueryService>())
{
@ -209,7 +207,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
result.Data = result.Data.ConvertName2Name(schema.SchemaDef, converters);
}
if (result.DataDraft != null && (context.ApiStatus == StatusForApi.All || context.IsFrontendClient))
if (result.DataDraft != null && (context.IsUnpublished() || context.IsFrontendClient))
{
result.DataDraft = result.DataDraft.ConvertName2Name(schema.SchemaDef, converters);
}
@ -225,7 +223,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
private IEnumerable<FieldConverter> GenerateConverters(QueryContext context)
private IEnumerable<FieldConverter> GenerateConverters(Context context)
{
if (!context.IsFrontendClient)
{
@ -243,19 +241,23 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
yield return FieldConverters.ResolveFallbackLanguages(context.App.LanguagesConfig);
if (context.Languages?.Any() == true)
var languages = context.Languages();
if (languages.Any())
{
yield return FieldConverters.FilterLanguages(context.App.LanguagesConfig, context.Languages);
yield return FieldConverters.FilterLanguages(context.App.LanguagesConfig, languages);
}
if (context.AssetUrlsToResolve?.Any() == true)
var assetUrls = context.AssetUrls();
if (assetUrls.Any() == true)
{
yield return FieldConverters.ResolveAssetUrls(context.AssetUrlsToResolve, assetUrlGenerator);
yield return FieldConverters.ResolveAssetUrls(assetUrls.ToList(), assetUrlGenerator);
}
}
}
private Query ParseQuery(QueryContext context, string query, ISchemaEntity schema)
private Query ParseQuery(Context context, string query, ISchemaEntity schema)
{
using (Profiler.TraceMethod<ContentQueryService>())
{
@ -292,7 +294,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
public async Task<ISchemaEntity> GetSchemaOrThrowAsync(QueryContext context, string schemaIdOrName)
public async Task<ISchemaEntity> GetSchemaOrThrowAsync(Context context, string schemaIdOrName)
{
ISchemaEntity schema = null;
@ -314,29 +316,27 @@ namespace Squidex.Domain.Apps.Entities.Contents
return schema;
}
private static void CheckPermission(ClaimsPrincipal user, params ISchemaEntity[] schemas)
private static void CheckPermission(Context context, params ISchemaEntity[] schemas)
{
var permissions = user.Permissions();
foreach (var schema in schemas)
{
if (!HasPermission(permissions, schema))
if (!HasPermission(context, schema))
{
throw new DomainForbiddenException("You do not have permission for this schema.");
}
}
}
private static bool HasPermission(PermissionSet permissions, ISchemaEntity schema)
private static bool HasPermission(Context context, ISchemaEntity schema)
{
var permission = Permissions.ForApp(Permissions.AppContentsRead, schema.AppId.Name, schema.SchemaDef.Name);
return permissions.Allows(permission);
return context.Permissions.Allows(permission);
}
private static Status[] GetStatus(QueryContext context)
private static Status[] GetStatus(Context context)
{
if (context.IsFrontendClient || context.ApiStatus == StatusForApi.All)
if (context.IsFrontendClient || context.IsUnpublished())
{
return null;
}
@ -346,36 +346,36 @@ namespace Squidex.Domain.Apps.Entities.Contents
}
}
private async Task<IResultList<IContentEntity>> QueryByQueryAsync(QueryContext context, Q query, ISchemaEntity schema)
private async Task<IResultList<IContentEntity>> QueryByQueryAsync(Context context, Q query, ISchemaEntity schema)
{
var parsedQuery = ParseQuery(context, query.ODataQuery, schema);
return await QueryCoreAsync(context, schema, parsedQuery);
}
private async Task<IResultList<IContentEntity>> QueryByIdsAsync(QueryContext context, Q query, ISchemaEntity schema)
private async Task<IResultList<IContentEntity>> QueryByIdsAsync(Context context, Q query, ISchemaEntity schema)
{
var contents = await QueryCoreAsync(context, schema, query.Ids.ToHashSet());
return contents.SortSet(x => x.Id, query.Ids);
}
private Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryCoreAsync(QueryContext context, IReadOnlyList<Guid> ids)
private Task<List<(IContentEntity Content, ISchemaEntity Schema)>> QueryCoreAsync(Context context, IReadOnlyList<Guid> ids)
{
return contentRepository.QueryAsync(context.App, GetStatus(context), new HashSet<Guid>(ids), WithDraft(context));
}
private Task<IResultList<IContentEntity>> QueryCoreAsync(QueryContext context, ISchemaEntity schema, Query query)
private Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, ISchemaEntity schema, Query query)
{
return contentRepository.QueryAsync(context.App, schema, GetStatus(context), context.IsFrontendClient, query, WithDraft(context));
}
private Task<IResultList<IContentEntity>> QueryCoreAsync(QueryContext context, ISchemaEntity schema, HashSet<Guid> ids)
private Task<IResultList<IContentEntity>> QueryCoreAsync(Context context, ISchemaEntity schema, HashSet<Guid> ids)
{
return contentRepository.QueryAsync(context.App, schema, GetStatus(context), ids, WithDraft(context));
}
private Task<IContentEntity> FindCoreAsync(QueryContext context, Guid id, ISchemaEntity schema)
private Task<IContentEntity> FindCoreAsync(Context context, Guid id, ISchemaEntity schema)
{
return contentRepository.FindContentAsync(context.App, schema, GetStatus(context), id, WithDraft(context));
}
@ -385,9 +385,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
return contentVersionLoader.LoadAsync(id, version);
}
private static bool WithDraft(QueryContext context)
private static bool WithDraft(Context context)
{
return context.ApiStatus == StatusForApi.All || context.IsFrontendClient;
return context.IsUnpublished() || context.IsFrontendClient;
}
}
}

107
src/Squidex.Domain.Apps.Entities/Contents/ContextExtensions.cs

@ -5,44 +5,133 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents
{
public static class ContextExtensions
{
private const string HeaderUnpublished = "X-Unpublished";
private const string HeaderFlatten = "X-Flatten";
private const string HeaderLanguages = "X-Languages";
private const string HeaderResolveFlow = "X-ResolveFlow";
private const string HeaderResolveAssetUrls = "X-Resolve-Urls";
private static readonly char[] Separators = { ',', ';' };
public static bool IsUnpublished(this Context context)
{
return context.Headers.ContainsKey("X-Unpublished");
return context.Headers.ContainsKey(HeaderUnpublished);
}
public static Context WithUnpublished(this Context context)
public static Context WithUnpublished(this Context context, bool value = true)
{
context.Headers["X-Unpublished"] = "1";
if (value)
{
context.Headers[HeaderUnpublished] = "1";
}
else
{
context.Headers.Remove(HeaderUnpublished);
}
return context;
}
public static bool IsFlatten(this Context context)
{
return context.Headers.ContainsKey("X-Flatten");
return context.Headers.ContainsKey(HeaderFlatten);
}
public static Context WithFlatten(this Context context)
public static Context WithFlatten(this Context context, bool value = true)
{
context.Headers["X-Flatten"] = "1";
if (value)
{
context.Headers[HeaderFlatten] = "1";
}
else
{
context.Headers.Remove(HeaderFlatten);
}
return context;
}
public static bool IsResolveFlow(this Context context)
{
return context.Headers.ContainsKey("X-ResolveFlow");
return context.Headers.ContainsKey(HeaderResolveFlow);
}
public static Context WithResolveFlow(this Context context, bool value = true)
{
if (value)
{
context.Headers[HeaderResolveFlow] = "1";
}
else
{
context.Headers.Remove(HeaderResolveFlow);
}
return context;
}
public static IEnumerable<string> AssetUrls(this Context context)
{
if (context.Headers.TryGetValue(HeaderResolveAssetUrls, out var value))
{
return value.Split(Separators, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToHashSet();
}
return Enumerable.Empty<string>();
}
public static Context WithAssetUrlsToResolve(this Context context, IEnumerable<string> fieldNames)
{
if (fieldNames?.Any() == true)
{
context.Headers[HeaderResolveAssetUrls] = string.Join(",", fieldNames);
}
else
{
context.Headers.Remove(HeaderResolveAssetUrls);
}
return context;
}
public static IEnumerable<Language> Languages(this Context context)
{
if (context.Headers.TryGetValue(HeaderResolveAssetUrls, out var value))
{
var languages = new HashSet<Language>();
foreach (var iso2Code in value.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
if (Language.TryGetLanguage(iso2Code.Trim(), out var language))
{
languages.Add(language);
}
}
return languages;
}
return Enumerable.Empty<Language>();
}
public static Context WithResolveFlow(this Context context)
public static Context WithLanguages(this Context context, IEnumerable<string> fieldNames)
{
context.Headers["X-ResolveFlow"] = "1";
if (fieldNames?.Any() == true)
{
context.Headers[HeaderLanguages] = string.Join(",", fieldNames);
}
else
{
context.Headers.Remove(HeaderLanguages);
}
return context;
}

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/CachingGraphQLService.cs

@ -29,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
this.resolver = resolver;
}
public async Task<(bool HasError, object Response)> QueryAsync(QueryContext context, params GraphQLQuery[] queries)
public async Task<(bool HasError, object Response)> QueryAsync(Context context, params GraphQLQuery[] queries)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(queries, nameof(queries));
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return (result.Any(x => x.HasError), result.ToArray(x => x.Response));
}
public async Task<(bool HasError, object Response)> QueryAsync(QueryContext context, GraphQLQuery query)
public async Task<(bool HasError, object Response)> QueryAsync(Context context, GraphQLQuery query)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(query, nameof(query));

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

@ -29,7 +29,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
public ISemanticLog Log { get; }
public GraphQLExecutionContext(QueryContext context, IDependencyResolver resolver)
public GraphQLExecutionContext(Context context, IDependencyResolver resolver)
: base(context,
resolver.Resolve<IAssetQueryService>(),
resolver.Resolve<IContentQueryService>())

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/IGraphQLService.cs

@ -11,8 +11,8 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public interface IGraphQLService
{
Task<(bool HasError, object Response)> QueryAsync(QueryContext context, params GraphQLQuery[] queries);
Task<(bool HasError, object Response)> QueryAsync(Context context, params GraphQLQuery[] queries);
Task<(bool HasError, object Response)> QueryAsync(QueryContext context, GraphQLQuery query);
Task<(bool HasError, object Response)> QueryAsync(Context context, GraphQLQuery query);
}
}

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

@ -17,12 +17,12 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
int DefaultPageSizeGraphQl { get; }
Task<IResultList<IEnrichedContentEntity>> QueryAsync(QueryContext context, IReadOnlyList<Guid> ids);
Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, IReadOnlyList<Guid> ids);
Task<IResultList<IEnrichedContentEntity>> QueryAsync(QueryContext context, string schemaIdOrName, Q query);
Task<IResultList<IEnrichedContentEntity>> QueryAsync(Context context, string schemaIdOrName, Q query);
Task<IEnrichedContentEntity> FindContentAsync(QueryContext context, string schemaIdOrName, Guid id, long version = EtagVersion.Any);
Task<IEnrichedContentEntity> FindContentAsync(Context context, string schemaIdOrName, Guid id, long version = EtagVersion.Any);
Task<ISchemaEntity> GetSchemaOrThrowAsync(QueryContext context, string schemaIdOrName);
Task<ISchemaEntity> GetSchemaOrThrowAsync(Context context, string schemaIdOrName);
}
}

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

@ -21,9 +21,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
private readonly ConcurrentDictionary<Guid, IEnrichedAssetEntity> cachedAssets = new ConcurrentDictionary<Guid, IEnrichedAssetEntity>();
private readonly IContentQueryService contentQuery;
private readonly IAssetQueryService assetQuery;
private readonly QueryContext context;
private readonly Context context;
public QueryExecutionContext(QueryContext context, IAssetQueryService assetQuery, IContentQueryService contentQuery)
public QueryExecutionContext(Context context, IAssetQueryService assetQuery, IContentQueryService contentQuery)
{
Guard.NotNull(assetQuery, nameof(assetQuery));
Guard.NotNull(contentQuery, nameof(contentQuery));

15
src/Squidex.Domain.Apps.Entities/Contents/StatusForApi.cs

@ -1,15 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Entities.Contents
{
public enum StatusForApi
{
PublishedOnly,
All,
}
}

14
src/Squidex.Domain.Apps.Entities/Context.cs

@ -15,13 +15,16 @@ namespace Squidex.Domain.Apps.Entities
{
public sealed class Context
{
public IDictionary<string, string> Headers { get; } = new Dictionary<string, string>();
public IAppEntity App { get; set; }
public ClaimsPrincipal User { get; set; }
public PermissionSet Permissions { get; set; }
public IDictionary<string, string> Headers { get; } = new Dictionary<string, string>();
public PermissionSet Permissions
{
get { return User?.Permissions() ?? PermissionSet.Empty; }
}
public Context()
{
@ -31,11 +34,6 @@ namespace Squidex.Domain.Apps.Entities
{
User = user;
if (user != null)
{
Permissions = user.Permissions();
}
App = app;
}

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

@ -1,116 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Security.Claims;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Security;
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; }
public bool Flatten { get; set; }
public StatusForApi ApiStatus { get; private set; }
public IReadOnlyCollection<string> AssetUrlsToResolve { get; private set; }
public IReadOnlyCollection<Language> Languages { get; private set; }
private QueryContext()
{
}
public static QueryContext Create(IAppEntity app, ClaimsPrincipal user)
{
return new QueryContext { App = app, User = user };
}
public QueryContext WithFlatten(bool flatten)
{
return Clone(c => c.Flatten = flatten);
}
public QueryContext WithUnpublished(bool unpublished)
{
return WithApiStatus(unpublished ? StatusForApi.All : StatusForApi.PublishedOnly);
}
public QueryContext WithApiStatus(StatusForApi status)
{
return Clone(c => c.ApiStatus = status);
}
public QueryContext WithAssetUrlsToResolve(IEnumerable<string> fieldNames)
{
if (fieldNames != null)
{
return Clone(c =>
{
var fields = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
c.AssetUrlsToResolve?.Foreach(x => fields.Add(x));
foreach (var part in fieldNames)
{
foreach (var fieldName in part.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
fields.Add(fieldName.Trim());
}
}
c.AssetUrlsToResolve = fields;
});
}
return this;
}
public QueryContext WithLanguages(IEnumerable<string> languageCodes)
{
if (languageCodes != null)
{
return Clone(c =>
{
var languages = new HashSet<Language>();
c.Languages?.Foreach(x => languages.Add(x));
foreach (var part in languageCodes)
{
foreach (var iso2Code in part.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
if (Language.TryGetLanguage(iso2Code.Trim(), out var language))
{
languages.Add(language);
}
}
}
c.Languages = languages;
});
}
return this;
}
public bool IsFrontendClient
{
get { return User.IsInClient("squidex-frontend"); }
}
}
}

6
src/Squidex.Web/ApiController.cs

@ -8,6 +8,7 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -37,6 +38,11 @@ namespace Squidex.Web
}
}
protected Context Context
{
get { return HttpContext.Context(); }
}
protected Guid AppId
{
get { return App.Id; }

5
src/Squidex.Web/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs

@ -63,6 +63,11 @@ namespace Squidex.Web.CommandMiddlewares
appId = appCommand.AppId;
}
if (appId == null)
{
appId = actionContextAccessor.ActionContext.HttpContext.Context().App?.NamedId();
}
if (appId != null)
{
var routeValues = actionContextAccessor.ActionContext.RouteData.Values;

18
src/Squidex.Web/ContextExtensions.cs

@ -14,14 +14,24 @@ namespace Squidex.Web
{
public static Context Context(this HttpContext httpContext)
{
var result = httpContext.Features.Get<Context>();
var context = httpContext.Features.Get<Context>();
if (result == null)
if (context == null)
{
httpContext.Features.Set(new Context { User = httpContext.User });
context = new Context { User = httpContext.User };
foreach (var header in httpContext.Request.Headers)
{
if (header.Key.StartsWith("X-", System.StringComparison.Ordinal))
{
context.Headers.Add(header.Key, header.Value.ToString());
}
}
httpContext.Features.Set(context);
}
return result;
return context;
}
}
}

5
src/Squidex.Web/Pipeline/AppResolver.cs

@ -66,12 +66,11 @@ namespace Squidex.Web.Pipeline
}
}
var permissionset = user.Permissions();
var permissionSet = user.Permissions();
context.HttpContext.Context().App = app;
context.HttpContext.Context().Permissions = permissions;
if (!permissionset.Includes(Permissions.ForApp(Permissions.App, appName)) && !AllowAnonymous(context))
if (!permissionSet.Includes(Permissions.ForApp(Permissions.App, appName)) && !AllowAnonymous(context))
{
context.Result = new NotFoundResult();
return;

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

@ -101,9 +101,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
[ApiCosts(1)]
public async Task<IActionResult> GetAssets(string app, [FromQuery] string ids = null)
{
var context = Context();
var assets = await assetQuery.QueryAsync(context, Q.Empty.WithODataQuery(Request.QueryString.ToString()).WithIds(ids));
var assets = await assetQuery.QueryAsync(Context, Q.Empty.WithODataQuery(Request.QueryString.ToString()).WithIds(ids));
var response = AssetsDto.FromAssets(assets, this, app);
@ -304,10 +302,5 @@ namespace Squidex.Areas.Api.Controllers.Assets
return assetFile;
}
private QueryContext Context()
{
return QueryContext.Create(App, User);
}
}
}

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

@ -62,7 +62,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(), query);
var result = await graphQl.QueryAsync(Context, query);
if (result.HasError)
{
@ -93,7 +93,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(), batch);
var result = await graphQl.QueryAsync(Context, batch);
if (result.HasError)
{
@ -124,10 +124,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetAllContents(string app, [FromQuery] string ids)
{
var context = Context();
var contents = await contentQuery.QueryAsync(context, Q.Empty.WithIds(ids).Ids);
var contents = await contentQuery.QueryAsync(Context, Q.Empty.WithIds(ids).Ids);
var response = await ContentsDto.FromContentsAsync(contents, context, this, null, contentWorkflow);
var response = await ContentsDto.FromContentsAsync(contents, Context, this, null, contentWorkflow);
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{
@ -159,12 +158,11 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetContents(string app, string name, [FromQuery] string ids = null)
{
var context = Context();
var contents = await contentQuery.QueryAsync(context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var contents = await contentQuery.QueryAsync(Context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var schema = await contentQuery.GetSchemaOrThrowAsync(context, name);
var schema = await contentQuery.GetSchemaOrThrowAsync(Context, name);
var response = await ContentsDto.FromContentsAsync(contents, context, this, schema, contentWorkflow);
var response = await ContentsDto.FromContentsAsync(contents, Context, this, schema, contentWorkflow);
if (ShouldProvideSurrogateKeys(response))
{
@ -196,10 +194,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetContent(string app, string name, Guid id)
{
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id);
var content = await contentQuery.FindContentAsync(Context, name, id);
var response = ContentDto.FromContent(context, content, this);
var response = ContentDto.FromContent(Context, content, this);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -232,10 +229,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> GetContentVersion(string app, string name, Guid id, int version)
{
var context = Context();
var content = await contentQuery.FindContentAsync(context, name, id, version);
var content = await contentQuery.FindContentAsync(Context, name, id, version);
var response = ContentDto.FromContent(context, content, this);
var response = ContentDto.FromContent(Context, content, this);
if (controllerOptions.Value.EnableSurrogateKeys)
{
@ -269,7 +265,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.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
if (publish && !this.HasPermission(Helper.StatusPermission(app, name, Status.Published)))
{
@ -306,7 +302,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.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
var command = new UpdateContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -338,7 +334,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.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
var command = new PatchContent { ContentId = id, Data = request.ToCleaned(), AsDraft = asDraft };
@ -369,7 +365,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> PutContentStatus(string app, string name, Guid id, ChangeStatusDto request)
{
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
if (!this.HasPermission(Helper.StatusPermission(app, name, Status.Published)))
{
@ -404,7 +400,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DiscardDraft(string app, string name, Guid id)
{
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
var command = new DiscardChanges { ContentId = id };
@ -432,7 +428,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
[ApiCosts(1)]
public async Task<IActionResult> DeleteContent(string app, string name, Guid id)
{
await contentQuery.GetSchemaOrThrowAsync(Context(), name);
await contentQuery.GetSchemaOrThrowAsync(Context, name);
var command = new DeleteContent { ContentId = id };
@ -451,15 +447,6 @@ namespace Squidex.Areas.Api.Controllers.Contents
return response;
}
private QueryContext Context()
{
return QueryContext.Create(App, User)
.WithAssetUrlsToResolve(Request.Headers["X-Resolve-Urls"])
.WithFlatten(Request.Headers.ContainsKey("X-Flatten"))
.WithLanguages(Request.Headers["X-Languages"])
.WithUnpublished(Request.Headers.ContainsKey("X-Unpublished"));
}
private bool ShouldProvideSurrogateKeys(ContentsDto response)
{
return controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys;

4
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs

@ -84,11 +84,11 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
/// </summary>
public long Version { get; set; }
public static ContentDto FromContent(QueryContext context, IEnrichedContentEntity content, ApiController controller)
public static ContentDto FromContent(Context context, IEnrichedContentEntity content, ApiController controller)
{
var response = SimpleMapper.Map(content, new ContentDto());
if (context?.Flatten == true)
if (context.IsFlatten())
{
response.Data = content.Data?.ToFlatten();
response.DataDraft = content.DataDraft?.ToFlatten();

2
src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs

@ -48,7 +48,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
}
public static async Task<ContentsDto> FromContentsAsync(IResultList<IEnrichedContentEntity> contents,
QueryContext context, ApiController controller, ISchemaEntity schema, IContentWorkflow contentWorkflow)
Context context, ApiController controller, ISchemaEntity schema, IContentWorkflow contentWorkflow)
{
var result = new ContentsDto
{

3
src/Squidex/Areas/Api/Controllers/UI/UIController.cs

@ -16,7 +16,6 @@ using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Security;
using Squidex.Shared;
using Squidex.Shared.Identity;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.UI
@ -53,7 +52,7 @@ namespace Squidex.Areas.Api.Controllers.UI
MapKey = uiOptions.Map?.GoogleMaps?.Key
};
var canCreateApps = !uiOptions.OnlyAdminsCanCreateApps || User.Permissions().Includes(CreateAppPermission);
var canCreateApps = !uiOptions.OnlyAdminsCanCreateApps || Context.Permissions.Includes(CreateAppPermission);
result.CanCreateApps = canCreateApps;

4
src/Squidex/Config/Web/WebServices.cs

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Squidex.Config.Domain;
using Squidex.Domain.Apps.Entities;
using Squidex.Pipeline.Plugins;
using Squidex.Pipeline.Robots;
using Squidex.Web;
@ -41,6 +42,9 @@ namespace Squidex.Config.Web
services.AddSingletonAs<RequestLogPerformanceMiddleware>()
.AsSelf();
services.AddSingletonAs<ContextProvider>()
.As<IContextProvider>();
services.AddSingletonAs<ApiPermissionUnifier>()
.As<IClaimsTransformation>();

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

@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
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");
private readonly QueryContext context;
private readonly Context context;
private readonly AssetQueryService sut;
public AssetQueryServiceTests()
@ -41,7 +41,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
A.CallTo(() => app.Name).Returns(appId.Name);
A.CallTo(() => app.LanguagesConfig).Returns(LanguagesConfig.English);
context = QueryContext.Create(app, user);
context = new Context(user, app);
var options = Options.Create(new AssetOptions { DefaultPageSize = 30 });

36
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEnricherTests.cs

@ -16,13 +16,18 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
public class ContentEnricherTests
{
private readonly IContentWorkflow workflow = A.Fake<IContentWorkflow>();
private readonly IContentWorkflow contentWorkflow = A.Fake<IContentWorkflow>();
private readonly IContextProvider contextProvider = A.Fake<IContextProvider>();
private readonly Context context = new Context();
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema");
private readonly ContentEnricher sut;
public ContentEnricherTests()
{
sut = new ContentEnricher(workflow);
A.CallTo(() => contextProvider.Context)
.Returns(context);
sut = new ContentEnricher(contentWorkflow, contextProvider);
}
[Fact]
@ -30,7 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(source))
A.CallTo(() => contentWorkflow.GetInfoAsync(source))
.Returns(new StatusInfo(Status.Published, StatusColors.Published));
var result = await sut.EnrichAsync(source);
@ -43,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(source))
A.CallTo(() => contentWorkflow.GetInfoAsync(source))
.Returns(Task.FromResult<StatusInfo>(null));
var result = await sut.EnrichAsync(source);
@ -54,9 +59,11 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Fact]
public async Task Should_enrich_content_with_can_update()
{
context.WithResolveFlow(true);
var source = new ContentEntity { SchemaId = schemaId };
A.CallTo(() => workflow.CanUpdateAsync(source))
A.CallTo(() => contentWorkflow.CanUpdateAsync(source))
.Returns(true);
var result = await sut.EnrichAsync(source);
@ -64,13 +71,28 @@ namespace Squidex.Domain.Apps.Entities.Contents
Assert.True(result.CanUpdate);
}
[Fact]
public async Task Should_not_enrich_content_with_can_update_if_disabled_in_context()
{
context.WithResolveFlow(false);
var source = new ContentEntity { SchemaId = schemaId };
var result = await sut.EnrichAsync(source);
Assert.False(result.CanUpdate);
A.CallTo(() => contentWorkflow.CanUpdateAsync(source))
.MustNotHaveHappened();
}
[Fact]
public async Task Should_enrich_multiple_contents_and_cache_color()
{
var source1 = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
var source2 = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(source1))
A.CallTo(() => contentWorkflow.GetInfoAsync(source1))
.Returns(new StatusInfo(Status.Published, StatusColors.Published));
var result = await sut.EnrichAsync(new[] { source1, source2 });
@ -78,7 +100,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
Assert.Equal(StatusColors.Published, result[0].StatusColor);
Assert.Equal(StatusColors.Published, result[1].StatusColor);
A.CallTo(() => workflow.GetInfoAsync(A<IContentEntity>.Ignored))
A.CallTo(() => contentWorkflow.GetInfoAsync(A<IContentEntity>.Ignored))
.MustHaveHappenedOnceExactly();
}
}

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

@ -53,13 +53,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
private readonly ClaimsPrincipal user;
private readonly ClaimsIdentity identity = new ClaimsIdentity();
private readonly EdmModelBuilder modelBuilder = new EdmModelBuilder(new MemoryCache(Options.Create(new MemoryCacheOptions())));
private readonly QueryContext context;
private readonly Context context;
private readonly ContentQueryService sut;
public static IEnumerable<object[]> ApiStatusTests = new[]
{
new object[] { StatusForApi.PublishedOnly, 0, new[] { Status.Published } },
new object[] { StatusForApi.All, 1, null }
new object[] { 0, new[] { Status.Published } },
new object[] { 1, null }
};
public ContentQueryServiceTests()
@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupEnricher();
context = QueryContext.Create(app, user);
context = new Context(user, app);
var options = Options.Create(new ContentOptions { DefaultPageSize = 30 });
@ -221,16 +221,16 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Theory]
[MemberData(nameof(ApiStatusTests))]
public async Task Should_return_single_content_for_api_with_transform(StatusForApi request, int includeDraft, Status[] status)
public async Task Should_return_single_content_for_api_with_transform(int unpublished, Status[] status)
{
var content = CreateContent(contentId);
SetupClaims(isFrontend: false);
SetupSchemaFound();
SetupScripting(contentId);
SetupContent(status, content, includeDraft == 1);
SetupContent(status, content, unpublished == 1);
var ctx = context.WithApiStatus(request);
var ctx = context.WithUnpublished(unpublished == 1);
var result = await sut.FindContentAsync(ctx, schemaId.Name, contentId);
@ -299,7 +299,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Theory]
[MemberData(nameof(ApiStatusTests))]
public async Task Should_query_contents_by_query_for_api_and_transform(StatusForApi request, int includeDraft, Status[] status)
public async Task Should_query_contents_by_query_for_api_and_transform(int unpublished, Status[] status)
{
const int count = 5, total = 200;
@ -308,9 +308,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims(isFrontend: false);
SetupSchemaFound();
SetupScripting(contentId);
SetupContents(status, count, total, content, inDraft: false, includeDraft == 1);
SetupContents(status, count, total, content, inDraft: false, unpublished == 1);
var ctx = context.WithApiStatus(request);
var ctx = context.WithUnpublished(unpublished == 1);
var result = await sut.QueryAsync(ctx, schemaId.Name, Q.Empty);
@ -359,7 +359,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Theory]
[MemberData(nameof(ApiStatusTests))]
public async Task Should_query_contents_by_id_for_api_and_transform(StatusForApi request, int includeDraft, Status[] status)
public async Task Should_query_contents_by_id_for_api_and_transform(int unpublished, Status[] status)
{
const int count = 5, total = 200;
@ -368,9 +368,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims(isFrontend: false);
SetupSchemaFound();
SetupScripting(ids.ToArray());
SetupContents(status, total, ids, includeDraft == 1);
SetupContents(status, total, ids, unpublished == 1);
var ctx = context.WithApiStatus(request);
var ctx = context.WithUnpublished(unpublished == 1);
var result = await sut.QueryAsync(ctx, schemaId.Name, Q.Empty.WithIds(ids));
@ -405,7 +405,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
[Theory]
[MemberData(nameof(ApiStatusTests))]
public async Task Should_query_all_contents_by_id_for_api_and_transform(StatusForApi request, int includeDraft, Status[] status)
public async Task Should_query_all_contents_by_id_for_api_and_transform(int unpublished, Status[] status)
{
const int count = 5;
@ -414,9 +414,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
SetupClaims(isFrontend: false);
SetupSchemaFound();
SetupScripting(ids.ToArray());
SetupContents(status, ids, includeDraft == 1);
SetupContents(status, ids, unpublished == 1);
var ctx = context.WithApiStatus(request);
var ctx = context.WithUnpublished(unpublished == 1);
var result = await sut.QueryAsync(ctx, ids);

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

@ -1017,14 +1017,14 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return A<Q>.That.Matches(x => x.Ids.Count == 1 && x.Ids[0] == contentId);
}
private QueryContext MatchsAssetContext()
private Context MatchsAssetContext()
{
return A<QueryContext>.That.Matches(x => x.App == app && x.User == user);
return A<Context>.That.Matches(x => x.App == app && x.User == user);
}
private QueryContext MatchsContentContext()
private Context MatchsContentContext()
{
return A<QueryContext>.That.Matches(x => x.App == app && x.User == user);
return A<Context>.That.Matches(x => x.App == app && x.User == user);
}
}
}

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

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
protected readonly IJsonSerializer serializer = TestUtils.CreateSerializer(TypeNameHandling.None);
protected readonly IDependencyResolver dependencyResolver;
protected readonly IAppEntity app = A.Dummy<IAppEntity>();
protected readonly QueryContext context;
protected readonly Context context;
protected readonly ClaimsPrincipal user = new ClaimsPrincipal();
protected readonly IGraphQLService sut;
@ -88,7 +88,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
A.CallTo(() => app.Name).Returns(appName);
A.CallTo(() => app.LanguagesConfig).Returns(LanguagesConfig.Build(Language.DE, Language.GermanGermany));
context = QueryContext.Create(app, user);
context = new Context(user, app);
A.CallTo(() => schema.Id).Returns(schemaId);
A.CallTo(() => schema.SchemaDef).Returns(schemaDef);

2
tests/Squidex.Web.Tests/ApiPermissionAttributeTests.cs

@ -38,7 +38,7 @@ namespace Squidex.Web
actionExecutingContext = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), this);
actionExecutingContext.HttpContext = httpContext;
actionExecutingContext.HttpContext.User = new ClaimsPrincipal(user);
actionExecutingContext.HttpContext.Context().User = new ClaimsPrincipal(user);
next = () =>
{

31
tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs

@ -8,44 +8,43 @@
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Microsoft.AspNetCore.Http;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Contents.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Web.Pipeline;
using Xunit;
namespace Squidex.Web.CommandMiddlewares
{
public class EnrichWithAppIdCommandMiddlewareTests
{
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>();
private readonly IContextProvider contextProvider = A.Fake<IContextProvider>();
private readonly ICommandBus commandBus = A.Fake<ICommandBus>();
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app");
private readonly HttpContext httpContext = new DefaultHttpContext();
private readonly Context appContext = new Context();
private readonly EnrichWithAppIdCommandMiddleware sut;
public EnrichWithAppIdCommandMiddlewareTests()
{
A.CallTo(() => httpContextAccessor.HttpContext)
.Returns(httpContext);
A.CallTo(() => contextProvider.Context)
.Returns(appContext);
var app = A.Fake<IAppEntity>();
A.CallTo(() => app.Id).Returns(appId.Id);
A.CallTo(() => app.Name).Returns(appId.Name);
httpContext.Features.Set<IAppFeature>(new AppResolver.AppFeature(app));
appContext.App = app;
sut = new EnrichWithAppIdCommandMiddleware(httpContextAccessor);
sut = new EnrichWithAppIdCommandMiddleware(contextProvider);
}
[Fact]
public async Task Should_throw_exception_if_app_not_found()
{
httpContext.Features.Set<IAppFeature>(new AppResolver.AppFeature(null));
appContext.App = null;
var command = new CreateContent();
var context = new CommandContext(command, commandBus);
@ -53,20 +52,6 @@ namespace Squidex.Web.CommandMiddlewares
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.HandleAsync(context));
}
[Fact]
public async Task Should_do_nothing_when_context_is_null()
{
A.CallTo(() => httpContextAccessor.HttpContext)
.Returns(null);
var command = new CreateContent();
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
Assert.Null(command.Actor);
}
[Fact]
public async Task Should_assign_app_id_and_name_to_app_command()
{

40
tests/Squidex.Web.Tests/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs

@ -20,7 +20,6 @@ using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Web.Pipeline;
using Xunit;
namespace Squidex.Web.CommandMiddlewares
@ -49,7 +48,7 @@ namespace Squidex.Web.CommandMiddlewares
A.CallTo(() => app.Id).Returns(appId.Id);
A.CallTo(() => app.Name).Returns(appId.Name);
httpContext.Features.Set<IAppFeature>(new AppResolver.AppFeature(app));
httpContext.Context().App = app;
var schema = A.Fake<ISchemaEntity>();
@ -65,33 +64,19 @@ namespace Squidex.Web.CommandMiddlewares
}
[Fact]
public async Task Should_throw_exception_if_app_not_found()
public async Task Should_throw_exception_if_schema_not_found()
{
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, "other-schema"))
.Returns(Task.FromResult<ISchemaEntity>(null));
actionContext.RouteData.Values["name"] = "other-schema";
var command = new CreateContent();
var command = new CreateContent { AppId = appId };
var context = new CommandContext(command, commandBus);
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.HandleAsync(context));
}
[Fact]
public async Task Should_do_nothing_when_context_is_null()
{
A.CallTo(() => actionContextAccessor.ActionContext)
.Returns(null);
var command = new CreateContent();
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
Assert.Null(command.Actor);
}
[Fact]
public async Task Should_do_nothing_when_route_has_no_parameter()
{
@ -108,7 +93,7 @@ namespace Squidex.Web.CommandMiddlewares
{
actionContext.RouteData.Values["name"] = schemaId.Name;
var command = new CreateContent();
var command = new CreateContent { AppId = appId };
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
@ -119,9 +104,9 @@ namespace Squidex.Web.CommandMiddlewares
[Fact]
public async Task Should_assign_schema_id_and_name_from_id()
{
actionContext.RouteData.Values["name"] = schemaId.Name;
actionContext.RouteData.Values["name"] = schemaId.Id;
var command = new CreateContent();
var command = new CreateContent { AppId = appId };
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
@ -142,19 +127,6 @@ namespace Squidex.Web.CommandMiddlewares
Assert.Equal(schemaId.Id, command.SchemaId);
}
[Fact]
public async Task Should_use_app_id_from_command()
{
actionContext.RouteData.Values["name"] = schemaId.Name;
var command = new CreateContent { AppId = appId };
var context = new CommandContext(command, commandBus);
await sut.HandleAsync(context);
Assert.Equal(schemaId, command.SchemaId);
}
[Fact]
public async Task Should_not_override_schema_id()
{

2
tests/Squidex.Web.Tests/Pipeline/ApiCostsFilterTests.cs

@ -161,7 +161,7 @@ namespace Squidex.Web.Pipeline
private void SetupApp()
{
httpContext.Features.Set<IAppFeature>(new AppResolver.AppFeature(appEntity));
httpContext.Context().App = appEntity;
}
}
}

6
tests/Squidex.Web.Tests/Pipeline/AppResolverTests.cs

@ -86,7 +86,7 @@ namespace Squidex.Web.Pipeline
await sut.OnActionExecutionAsync(actionExecutingContext, next);
Assert.Same(app, httpContext.Features.Get<IAppFeature>()?.App);
Assert.Same(app, httpContext.Context().App);
Assert.True(user.Claims.Count() > 2);
Assert.True(isNextCalled);
}
@ -104,7 +104,7 @@ namespace Squidex.Web.Pipeline
await sut.OnActionExecutionAsync(actionExecutingContext, next);
Assert.Same(app, httpContext.Features.Get<IAppFeature>()?.App);
Assert.Same(app, httpContext.Context().App);
Assert.True(user.Claims.Count() > 2);
Assert.True(isNextCalled);
}
@ -124,7 +124,7 @@ namespace Squidex.Web.Pipeline
await sut.OnActionExecutionAsync(actionExecutingContext, next);
Assert.Same(app, httpContext.Features.Get<IAppFeature>()?.App);
Assert.Same(app, httpContext.Context().App);
Assert.Equal(2, user.Claims.Count());
Assert.True(isNextCalled);
}

Loading…
Cancel
Save