Browse Source

Deferred conversion.

pull/382/head
Sebastian 7 years ago
parent
commit
8fbc5f4f11
  1. 3
      src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs
  2. 1
      src/Squidex.Web/ApiPermissionAttribute.cs
  3. 42
      src/Squidex.Web/Deferred.cs
  4. 61
      src/Squidex.Web/ETagExtensions.cs
  5. 18
      src/Squidex.Web/IGenerateEtag.cs
  6. 29
      src/Squidex.Web/Json/TypedJsonInheritanceConverter.cs
  7. 26
      src/Squidex.Web/Pipeline/DeferredActionFilter.cs
  8. 2
      src/Squidex.Web/Pipeline/ETagFilter.cs
  9. 2
      src/Squidex.Web/Pipeline/ETagOptions.cs
  10. 7
      src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs
  11. 31
      src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs
  12. 7
      src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs
  13. 7
      src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs
  14. 16
      src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs
  15. 7
      src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs
  16. 7
      src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs
  17. 2
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  18. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs
  19. 37
      src/Squidex/Areas/Api/Controllers/Assets/AssetsController.cs
  20. 2
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetDto.cs
  21. 10
      src/Squidex/Areas/Api/Controllers/Assets/Models/AssetsDto.cs
  22. 8
      src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs
  23. 37
      src/Squidex/Areas/Api/Controllers/Contents/ContentsController.cs
  24. 2
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentDto.cs
  25. 10
      src/Squidex/Areas/Api/Controllers/Contents/Models/ContentsDto.cs
  26. 5
      src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs
  27. 7
      src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs
  28. 4
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs
  29. 2
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs
  30. 4
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs
  31. 5
      src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs
  32. 14
      src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs
  33. 4
      src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs
  34. 2
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs
  35. 5
      src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs
  36. 14
      src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs
  37. 1
      src/Squidex/Config/Domain/SerializationInitializer.cs
  38. 1
      src/Squidex/Config/Web/WebServices.cs

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

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.OData;
@ -23,9 +22,7 @@ using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Queries.OData;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Security;
using Squidex.Shared;
using Squidex.Shared.Identity;
#pragma warning disable RECS0147

1
src/Squidex.Web/ApiPermissionAttribute.cs

@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Squidex.Infrastructure.Security;
using Squidex.Infrastructure.Tasks;
using Squidex.Shared.Identity;
namespace Squidex.Web
{

42
src/Squidex.Web/Deferred.cs

@ -0,0 +1,42 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure;
namespace Squidex.Web
{
public struct Deferred
{
private readonly Lazy<Task<object>> value;
public Task<object> Value
{
get { return value.Value; }
}
private Deferred(Func<Task<object>> value)
{
this.value = new Lazy<Task<object>>(value);
}
public static Deferred Response(Func<object> factory)
{
Guard.NotNull(factory, nameof(factory));
return new Deferred(() => Task.FromResult(factory()));
}
public static Deferred AsyncResponse<T>(Func<Task<T>> factory)
{
Guard.NotNull(factory, nameof(factory));
return new Deferred(async () => await factory());
}
}
}

61
src/Squidex.Web/ETagExtensions.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using Squidex.Domain.Apps.Entities;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Log;
@ -17,40 +18,54 @@ namespace Squidex.Web
{
private static readonly int GuidLength = Guid.Empty.ToString().Length;
public static string ToManyEtag<T>(this IReadOnlyList<T> items, long total = 0) where T : IGenerateETag
public static string ToEtag<T>(this IReadOnlyList<T> items, IEntityWithVersion app = null) where T : IEntity, IEntityWithVersion
{
using (Profiler.Trace("CalculateEtag"))
{
var unhashed = Unhashed(items, total);
var unhashed = Unhashed(items, 0, app);
return unhashed.Sha256Base64();
}
}
private static string Unhashed<T>(IReadOnlyList<T> items, long total) where T : IGenerateETag
public static string ToEtag<T>(this IResultList<T> items, IEntityWithVersion app = null) where T : IEntity, IEntityWithVersion
{
var sb = new StringBuilder((items.Count * (GuidLength + 4)) + 10);
using (Profiler.Trace("CalculateEtag"))
{
var unhashed = Unhashed(items, items.Total, app);
return unhashed.Sha256Base64();
}
}
private static string Unhashed<T>(IReadOnlyList<T> items, long total, IEntityWithVersion app) where T : IEntity, IEntityWithVersion
{
var sb = new StringBuilder((items.Count * (GuidLength + 8)) + 10);
for (var i = 0; i < items.Count; i++)
{
sb.Append(";");
sb.Append(items[i].ToEtag());
}
sb.Append(total);
sb.Append("_");
sb.Append(total);
if (items.Count > 0)
if (app != null)
{
sb.Append(items[0].Id.ToString());
sb.Append(items[0].Version);
for (var i = 1; i < items.Count; i++)
{
sb.Append(";");
sb.Append(items[i].Id.ToString());
sb.Append(items[i].Version);
}
sb.Append("_");
sb.Append(app.Version);
}
return sb.ToString().Sha256Base64();
return sb.ToString();
}
public static string ToSurrogateKey<T>(this T item) where T : IEntity
{
return item.Id.ToString();
}
public static string ToSurrogateKeys<T>(this IReadOnlyList<T> items) where T : IGenerateETag
public static string ToSurrogateKeys<T>(this IReadOnlyList<T> items) where T : IEntity
{
if (items.Count == 0)
{
@ -70,9 +85,17 @@ namespace Squidex.Web
return sb.ToString();
}
public static string ToEtag<T>(this T item) where T : IGenerateETag
public static string ToEtag<T>(this T item, IEntityWithVersion app = null) where T : IEntity, IEntityWithVersion
{
return item.Version.ToString();
var result = $"{item.Id};{item.Version}";
if (app != null)
{
result += ";";
result += app.Version;
}
return result;
}
}
}

18
src/Squidex.Web/IGenerateEtag.cs

@ -1,18 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Web
{
public interface IGenerateETag
{
Guid Id { get; }
long Version { get; }
}
}

29
src/Squidex.Web/MyJsonInheritanceConverter.cs → src/Squidex.Web/Json/TypedJsonInheritanceConverter.cs

@ -16,17 +16,16 @@ using Squidex.Infrastructure;
#pragma warning disable RECS0108 // Warns about static fields in generic types
namespace Squidex.Web
namespace Squidex.Web.Json
{
public class MyJsonInheritanceConverter<T> : JsonInheritanceConverter
public class TypedJsonInheritanceConverter<T> : JsonInheritanceConverter
{
private static readonly Dictionary<string, Type> DefaultMapping = new Dictionary<string, Type>();
private readonly IReadOnlyDictionary<string, Type> maping;
static MyJsonInheritanceConverter()
private static readonly Lazy<Dictionary<string, Type>> DefaultMapping = new Lazy<Dictionary<string, Type>>(() =>
{
var baseName = typeof(T).Name;
var result = new Dictionary<string, Type>();
void AddType(Type type)
{
var discriminator = type.Name;
@ -36,7 +35,7 @@ namespace Squidex.Web
discriminator = discriminator.Substring(0, discriminator.Length - baseName.Length);
}
DefaultMapping[discriminator] = type;
result[discriminator] = type;
}
foreach (var attribute in typeof(T).GetCustomAttributes<KnownTypeAttribute>())
@ -66,17 +65,23 @@ namespace Squidex.Web
}
}
}
}
public MyJsonInheritanceConverter(string discriminator)
: this(discriminator, DefaultMapping)
return result;
});
private readonly IReadOnlyDictionary<string, Type> maping;
public TypedJsonInheritanceConverter(string discriminator)
: this(discriminator, DefaultMapping.Value)
{
}
public MyJsonInheritanceConverter(string discriminator, IReadOnlyDictionary<string, Type> mapping)
public TypedJsonInheritanceConverter(string discriminator, IReadOnlyDictionary<string, Type> mapping)
: base(typeof(T), discriminator)
{
maping = mapping ?? DefaultMapping;
Guard.NotNull(maping, nameof(maping));
maping = mapping;
}
protected override Type GetDiscriminatorType(JObject jObject, Type objectType, string discriminatorValue)

26
src/Squidex.Web/Pipeline/DeferredActionFilter.cs

@ -0,0 +1,26 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Squidex.Web.Pipeline
{
public sealed class DeferredActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
await next();
if (context.Result is ObjectResult objectResult && objectResult.Value is Deferred deferred)
{
objectResult.Value = await deferred.Value;
}
}
}
}

2
src/Squidex.Web/ETagFilter.cs → src/Squidex.Web/Pipeline/ETagFilter.cs

@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Squidex.Web
namespace Squidex.Web.Pipeline
{
public sealed class ETagFilter : IAsyncActionFilter
{

2
src/Squidex.Web/ETagOptions.cs → src/Squidex.Web/Pipeline/ETagOptions.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Web
namespace Squidex.Web.Pipeline
{
public sealed class ETagOptions
{

7
src/Squidex/Areas/Api/Controllers/Apps/AppClientsController.cs

@ -46,9 +46,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetClients(string app)
{
var response = ClientsDto.FromApp(App, this);
var response = Deferred.Response(() =>
{
return ClientsDto.FromApp(App, this);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}

31
src/Squidex/Areas/Api/Controllers/Apps/AppContributorsController.cs

@ -48,9 +48,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetContributors(string app)
{
var response = ContributorsDto.FromApp(App, appPlansProvider, this, false);
var response = Deferred.Response(() =>
{
return ContributorsDto.FromApp(App, appPlansProvider, this, false);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}
@ -73,18 +76,8 @@ namespace Squidex.Areas.Api.Controllers.Apps
public async Task<IActionResult> PostContributor(string app, [FromBody] AssignContributorDto request)
{
var command = request.ToCommand();
var context = await CommandBus.PublishAsync(command);
var response = (ContributorsDto)null;
if (context.PlainResult is IAppEntity newApp)
{
response = ContributorsDto.FromApp(newApp, appPlansProvider, this, false);
}
else if (context.PlainResult is InvitedResult invited)
{
response = ContributorsDto.FromApp(invited.App, appPlansProvider, this, true);
}
var response = await InvokeCommandAsync(command);
return CreatedAtAction(nameof(GetContributors), new { app }, response);
}
@ -117,10 +110,14 @@ namespace Squidex.Areas.Api.Controllers.Apps
{
var context = await CommandBus.PublishAsync(command);
var result = context.Result<IAppEntity>();
var response = ContributorsDto.FromApp(result, appPlansProvider, this, false);
return response;
if (context.PlainResult is InvitedResult invited)
{
return ContributorsDto.FromApp(invited.App, appPlansProvider, this, true);
}
else
{
return ContributorsDto.FromApp(context.Result<IAppEntity>(), appPlansProvider, this, false);
}
}
}
}

7
src/Squidex/Areas/Api/Controllers/Apps/AppLanguagesController.cs

@ -45,9 +45,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetLanguages(string app)
{
var response = AppLanguagesDto.FromApp(App, this);
var response = Deferred.Response(() =>
{
return AppLanguagesDto.FromApp(App, this);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}

7
src/Squidex/Areas/Api/Controllers/Apps/AppPatternsController.cs

@ -47,9 +47,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetPatterns(string app)
{
var response = PatternsDto.FromApp(App, this);
var response = Deferred.Response(() =>
{
return PatternsDto.FromApp(App, this);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}

16
src/Squidex/Areas/Api/Controllers/Apps/AppRolesController.cs

@ -47,9 +47,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetRoles(string app)
{
var response = RolesDto.FromApp(App, this);
var response = Deferred.Response(() =>
{
return RolesDto.FromApp(App, this);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}
@ -67,11 +70,14 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ProducesResponseType(typeof(string[]), 200)]
[ApiPermission(Permissions.AppRolesRead)]
[ApiCosts(0)]
public async Task<IActionResult> GetPermissions(string app)
public IActionResult GetPermissions(string app)
{
var response = await permissionsProvider.GetPermissionsAsync(App);
var response = Deferred.AsyncResponse(() =>
{
return permissionsProvider.GetPermissionsAsync(App);
});
Response.Headers[HeaderNames.ETag] = string.Join(";", response).Sha256Base64();
Response.Headers[HeaderNames.ETag] = string.Concat(response).Sha256Base64();
return Ok(response);
}

7
src/Squidex/Areas/Api/Controllers/Apps/AppWorkflowsController.cs

@ -44,9 +44,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
[ApiCosts(0)]
public IActionResult GetWorkflows(string app)
{
var response = WorkflowsDto.FromApp(App, this);
var response = Deferred.Response(() =>
{
return WorkflowsDto.FromApp(App, this);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}

7
src/Squidex/Areas/Api/Controllers/Apps/AppsController.cs

@ -62,9 +62,12 @@ namespace Squidex.Areas.Api.Controllers.Apps
var apps = await appProvider.GetUserApps(userOrClientId, userPermissions);
var response = apps.ToArray(a => AppDto.FromApp(a, userOrClientId, userPermissions, appPlansProvider, this));
var response = Deferred.Response(() =>
{
return apps.ToArray(a => AppDto.FromApp(a, userOrClientId, userPermissions, appPlansProvider, this));
});
Response.Headers[HeaderNames.ETag] = response.ToManyEtag();
Response.Headers[HeaderNames.ETag] = apps.ToEtag();
return Ok(response);
}

2
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -25,7 +25,7 @@ using AllPermissions = Squidex.Shared.Permissions;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{
public sealed class AppDto : Resource, IGenerateETag
public sealed class AppDto : Resource
{
/// <summary>
/// The name of the app.

1
src/Squidex/Areas/Api/Controllers/Apps/Models/WorkflowsDto.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Squidex.Domain.Apps.Entities.Apps;

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

@ -103,14 +103,17 @@ namespace Squidex.Areas.Api.Controllers.Assets
{
var assets = await assetQuery.QueryAsync(Context, Q.Empty.WithODataQuery(Request.QueryString.ToString()).WithIds(ids));
var response = AssetsDto.FromAssets(assets, this, app);
var response = Deferred.Response(() =>
{
return AssetsDto.FromAssets(assets, this, app);
});
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
if (controllerOptions.Value.EnableSurrogateKeys && assets.Count <= controllerOptions.Value.MaxItemsForSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = response.ToSurrogateKeys();
Response.Headers["Surrogate-Key"] = assets.ToSurrogateKeys();
}
Response.Headers[HeaderNames.ETag] = response.ToEtag();
Response.Headers[HeaderNames.ETag] = assets.ToEtag();
return Ok(response);
}
@ -138,14 +141,17 @@ namespace Squidex.Areas.Api.Controllers.Assets
return NotFound();
}
var response = AssetDto.FromAsset(asset, this, app);
var response = Deferred.Response(() =>
{
return AssetDto.FromAsset(asset, this, app);
});
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = asset.Id.ToString();
Response.Headers["Surrogate-Key"] = asset.ToSurrogateKey();
}
Response.Headers[HeaderNames.ETag] = asset.Version.ToString();
Response.Headers[HeaderNames.ETag] = asset.ToEtag();
return Ok(response);
}
@ -175,10 +181,7 @@ namespace Squidex.Areas.Api.Controllers.Assets
var command = new CreateAsset { File = assetFile };
var context = await CommandBus.PublishAsync(command);
var result = context.Result<AssetCreatedResult>();
var response = AssetDto.FromAsset(result.Asset, this, app, result.IsDuplicate);
var response = await InvokeCommandAsync(app, command);
return CreatedAtAction(nameof(GetAsset), new { app, id = response.Id }, response);
}
@ -263,10 +266,14 @@ namespace Squidex.Areas.Api.Controllers.Assets
{
var context = await CommandBus.PublishAsync(command);
var result = context.Result<IEnrichedAssetEntity>();
var response = AssetDto.FromAsset(result, this, app);
return response;
if (context.PlainResult is AssetCreatedResult created)
{
return AssetDto.FromAsset(created.Asset, this, app, created.IsDuplicate);
}
else
{
return AssetDto.FromAsset(context.Result<IEnrichedAssetEntity>(), this, app);
}
}
private async Task<AssetFile> CheckAssetFileAsync(IReadOnlyList<IFormFile> file)

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

@ -18,7 +18,7 @@ using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Assets.Models
{
public sealed class AssetDto : Resource, IGenerateETag
public sealed class AssetDto : Resource
{
/// <summary>
/// The id of the asset.

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

@ -27,16 +27,6 @@ namespace Squidex.Areas.Api.Controllers.Assets.Models
[Required]
public AssetDto[] Items { get; set; }
public string ToEtag()
{
return Items.ToManyEtag(Total);
}
public string ToSurrogateKeys()
{
return Items.ToSurrogateKeys();
}
public static AssetsDto FromAssets(IResultList<IEnrichedAssetEntity> assets, ApiController controller, string app)
{
var response = new AssetsDto

8
src/Squidex/Areas/Api/Controllers/Comments/CommentsController.cs

@ -55,9 +55,13 @@ namespace Squidex.Areas.Api.Controllers.Comments
public async Task<IActionResult> GetComments(string app, Guid commentsId, [FromQuery] long version = EtagVersion.Any)
{
var result = await grainFactory.GetGrain<ICommentGrain>(commentsId).GetCommentsAsync(version);
var response = CommentsDto.FromResult(result);
Response.Headers[HeaderNames.ETag] = response.Version.ToString();
var response = Deferred.Response(() =>
{
return CommentsDto.FromResult(result);
});
Response.Headers[HeaderNames.ETag] = result.Version.ToString();
return Ok(response);
}

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

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
@ -126,14 +127,17 @@ namespace Squidex.Areas.Api.Controllers.Contents
{
var contents = await contentQuery.QueryAsync(Context, Q.Empty.WithIds(ids).Ids);
var response = await ContentsDto.FromContentsAsync(contents, Context, this, null, contentWorkflow);
var response = Deferred.AsyncResponse(() =>
{
return ContentsDto.FromContentsAsync(contents, Context, this, null, contentWorkflow);
});
if (controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys)
if (ShouldProvideSurrogateKeys(contents))
{
Response.Headers["Surrogate-Key"] = response.ToSurrogateKeys();
Response.Headers["Surrogate-Key"] = contents.ToSurrogateKeys();
}
Response.Headers[HeaderNames.ETag] = $"{response.ToEtag()}_{App.Version}";
Response.Headers[HeaderNames.ETag] = contents.ToEtag(App);
return Ok(response);
}
@ -160,16 +164,19 @@ namespace Squidex.Areas.Api.Controllers.Contents
{
var contents = await contentQuery.QueryAsync(Context, name, Q.Empty.WithIds(ids).WithODataQuery(Request.QueryString.ToString()));
var schema = await contentQuery.GetSchemaOrThrowAsync(Context, name);
var response = Deferred.AsyncResponse(async () =>
{
var schema = await contentQuery.GetSchemaOrThrowAsync(Context, name);
var response = await ContentsDto.FromContentsAsync(contents, Context, this, schema, contentWorkflow);
return await ContentsDto.FromContentsAsync(contents, Context, this, schema, contentWorkflow);
});
if (ShouldProvideSurrogateKeys(response))
if (ShouldProvideSurrogateKeys(contents))
{
Response.Headers["Surrogate-Key"] = response.ToSurrogateKeys();
Response.Headers["Surrogate-Key"] = contents.ToSurrogateKeys();
}
Response.Headers[HeaderNames.ETag] = $"{response.ToEtag()}_{App.Version}";
Response.Headers[HeaderNames.ETag] = contents.ToEtag(App);
return Ok(response);
}
@ -200,10 +207,10 @@ namespace Squidex.Areas.Api.Controllers.Contents
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = content.Id.ToString();
Response.Headers["Surrogate-Key"] = content.ToSurrogateKey();
}
Response.Headers[HeaderNames.ETag] = $"{response.ToEtag()}_{App.Version}";
Response.Headers[HeaderNames.ETag] = content.ToEtag(App);
return Ok(response);
}
@ -235,10 +242,10 @@ namespace Squidex.Areas.Api.Controllers.Contents
if (controllerOptions.Value.EnableSurrogateKeys)
{
Response.Headers["Surrogate-Key"] = content.Id.ToString();
Response.Headers["Surrogate-Key"] = content.ToSurrogateKey();
}
Response.Headers[HeaderNames.ETag] = $"{response.ToEtag()}_{App.Version}";
Response.Headers[HeaderNames.ETag] = content.ToEtag(App);
return Ok(response.Data);
}
@ -447,9 +454,9 @@ namespace Squidex.Areas.Api.Controllers.Contents
return response;
}
private bool ShouldProvideSurrogateKeys(ContentsDto response)
private bool ShouldProvideSurrogateKeys(IReadOnlyList<IContentEntity> response)
{
return controllerOptions.Value.EnableSurrogateKeys && response.Items.Length <= controllerOptions.Value.MaxItemsForSurrogateKeys;
return controllerOptions.Value.EnableSurrogateKeys && response.Count <= controllerOptions.Value.MaxItemsForSurrogateKeys;
}
}
}

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

@ -19,7 +19,7 @@ using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Contents.Models
{
public sealed class ContentDto : Resource, IGenerateETag
public sealed class ContentDto : Resource
{
/// <summary>
/// The if of the content item.

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

@ -37,16 +37,6 @@ namespace Squidex.Areas.Api.Controllers.Contents.Models
[Required]
public StatusInfoDto[] Statuses { get; set; }
public string ToEtag()
{
return Items.ToManyEtag(Total);
}
public string ToSurrogateKeys()
{
return Items.ToSurrogateKeys();
}
public static async Task<ContentsDto> FromContentsAsync(IResultList<IEnrichedContentEntity> contents,
Context context, ApiController controller, ISchemaEntity schema, IContentWorkflow contentWorkflow)
{

5
src/Squidex/Areas/Api/Controllers/Languages/LanguagesController.cs

@ -40,7 +40,10 @@ namespace Squidex.Areas.Api.Controllers.Languages
[ApiPermission]
public IActionResult GetLanguages()
{
var response = Language.AllLanguages.Select(LanguageDto.FromLanguage).ToArray();
var response = Deferred.Response(() =>
{
return Language.AllLanguages.Select(LanguageDto.FromLanguage).ToArray();
});
Response.Headers[HeaderNames.ETag] = "1";

7
src/Squidex/Areas/Api/Controllers/Plans/AppPlansController.cs

@ -51,9 +51,12 @@ namespace Squidex.Areas.Api.Controllers.Plans
{
var hasPortal = appPlansBillingManager.HasPortal;
var response = AppPlansDto.FromApp(App, appPlansProvider, hasPortal);
var response = Deferred.Response(() =>
{
return AppPlansDto.FromApp(App, appPlansProvider, hasPortal);
});
Response.Headers[HeaderNames.ETag] = App.Version.ToString();
Response.Headers[HeaderNames.ETag] = App.ToEtag();
return Ok(response);
}

4
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionConverter.cs

@ -8,11 +8,11 @@
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Web;
using Squidex.Web.Json;
namespace Squidex.Areas.Api.Controllers.Rules.Models
{
public sealed class RuleActionConverter : MyJsonInheritanceConverter<RuleAction>
public sealed class RuleActionConverter : TypedJsonInheritanceConverter<RuleAction>
{
public static IReadOnlyDictionary<string, Type> Mapping { get; set; }

2
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs

@ -19,7 +19,7 @@ using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Rules.Models
{
public sealed class RuleDto : Resource, IGenerateETag
public sealed class RuleDto : Resource
{
/// <summary>
/// The id of the rule.

4
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleTriggerDto.cs

@ -10,11 +10,11 @@ using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Web;
using Squidex.Web.Json;
namespace Squidex.Areas.Api.Controllers.Rules.Models
{
[JsonConverter(typeof(MyJsonInheritanceConverter<RuleTriggerDto>), "triggerType")]
[JsonConverter(typeof(TypedJsonInheritanceConverter<RuleTriggerDto>), "triggerType")]
[KnownType(nameof(Subtypes))]
public abstract class RuleTriggerDto
{

5
src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs

@ -22,11 +22,6 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
[Required]
public RuleDto[] Items { get; set; }
public string GenerateEtag()
{
return Items.ToManyEtag(0);
}
public static RulesDto FromRules(IEnumerable<IRuleEntity> items, ApiController controller, string app)
{
var result = new RulesDto

14
src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs

@ -58,9 +58,12 @@ namespace Squidex.Areas.Api.Controllers.Rules
[ApiCosts(0)]
public IActionResult GetActions()
{
var etag = string.Join(";", ruleRegistry.Actions.Select(x => x.Key)).Sha256Base64();
var etag = string.Concat(ruleRegistry.Actions.Select(x => x.Key)).Sha256Base64();
var response = ruleRegistry.Actions.ToDictionary(x => x.Key, x => RuleElementDto.FromDefinition(x.Value));
var response = Deferred.Response(() =>
{
return ruleRegistry.Actions.ToDictionary(x => x.Key, x => RuleElementDto.FromDefinition(x.Value));
});
Response.Headers[HeaderNames.ETag] = etag;
@ -84,9 +87,12 @@ namespace Squidex.Areas.Api.Controllers.Rules
{
var rules = await appProvider.GetRulesAsync(AppId);
var response = RulesDto.FromRules(rules, this, app);
var response = Deferred.Response(() =>
{
return RulesDto.FromRules(rules, this, app);
});
Response.Headers[HeaderNames.ETag] = response.GenerateEtag();
Response.Headers[HeaderNames.ETag] = rules.ToEtag();
return Ok(response);
}

4
src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldPropertiesDto.cs

@ -11,11 +11,11 @@ using System.Linq;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Web;
using Squidex.Web.Json;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
[JsonConverter(typeof(MyJsonInheritanceConverter<FieldPropertiesDto>), "fieldType")]
[JsonConverter(typeof(TypedJsonInheritanceConverter<FieldPropertiesDto>), "fieldType")]
[KnownType(nameof(Subtypes))]
public abstract class FieldPropertiesDto
{

2
src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDto.cs

@ -17,7 +17,7 @@ using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Schemas.Models
{
public class SchemaDto : Resource, IGenerateETag
public class SchemaDto : Resource
{
/// <summary>
/// The id of the schema.

5
src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemasDto.cs

@ -21,11 +21,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models
/// </summary>
public SchemaDto[] Items { get; set; }
public string ToEtag()
{
return Items.ToManyEtag();
}
public static SchemasDto FromSchemas(IList<ISchemaEntity> schemas, ApiController controller, string app)
{
var result = new SchemasDto

14
src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs

@ -50,9 +50,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas
{
var schemas = await appProvider.GetSchemasAsync(AppId);
var response = SchemasDto.FromSchemas(schemas, this, app);
var response = Deferred.Response(() =>
{
return SchemasDto.FromSchemas(schemas, this, app);
});
Response.Headers[HeaderNames.ETag] = response.ToEtag();
Response.Headers[HeaderNames.ETag] = schemas.ToEtag();
return Ok(response);
}
@ -89,9 +92,12 @@ namespace Squidex.Areas.Api.Controllers.Schemas
return NotFound();
}
var response = SchemaDetailsDto.FromSchemaWithDetails(schema, this, app);
var response = Deferred.Response(() =>
{
return SchemaDetailsDto.FromSchemaWithDetails(schema, this, app);
});
Response.Headers[HeaderNames.ETag] = schema.Version.ToString();
Response.Headers[HeaderNames.ETag] = schema.ToEtag();
return Ok(response);
}

1
src/Squidex/Config/Domain/SerializationInitializer.cs

@ -29,6 +29,7 @@ namespace Squidex.Config.Domain
{
this.jsonNetSerializer = jsonNetSerializer;
this.jsonSerializer = jsonSerializer;
this.ruleRegistry = ruleRegistry;
}

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

@ -51,6 +51,7 @@ namespace Squidex.Config.Web
services.AddMvc(options =>
{
options.Filters.Add<ETagFilter>();
options.Filters.Add<DeferredActionFilter>();
options.Filters.Add<AppResolver>();
options.Filters.Add<MeasureResultFilter>();
})

Loading…
Cancel
Save