mirror of https://github.com/Squidex/squidex.git
Browse Source
* #639 Create JSON schemas for all rule events * Use graphql middleware. * Guard it. * Just some naming. * Additional test for graphql to ensure compatibility.pull/653/head
committed by
GitHub
48 changed files with 902 additions and 757 deletions
@ -0,0 +1,39 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.IO; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using GraphQL; |
||||
|
using Microsoft.AspNetCore.WebUtilities; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Json; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL |
||||
|
{ |
||||
|
public sealed class DefaultDocumentWriter : IDocumentWriter |
||||
|
{ |
||||
|
private readonly IJsonSerializer jsonSerializer; |
||||
|
|
||||
|
public DefaultDocumentWriter(IJsonSerializer jsonSerializer) |
||||
|
{ |
||||
|
Guard.NotNull(jsonSerializer, nameof(jsonSerializer)); |
||||
|
|
||||
|
this.jsonSerializer = jsonSerializer; |
||||
|
} |
||||
|
|
||||
|
public async Task WriteAsync<T>(Stream stream, T value, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
await using (var buffer = new FileBufferingWriteStream()) |
||||
|
{ |
||||
|
jsonSerializer.Serialize(value, buffer, true); |
||||
|
|
||||
|
await buffer.DrainBufferAsync(stream, cancellationToken); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,20 +1,19 @@ |
|||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Squidex Headless CMS
|
// Squidex Headless CMS
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
// All rights reserved. Licensed under the MIT license.
|
// All rights reserved. Licensed under the MIT license.
|
||||
// ==========================================================================
|
// ==========================================================================
|
||||
|
|
||||
using GraphQL; |
using GraphQL.Types; |
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL |
namespace Squidex.Web.GraphQL |
||||
{ |
{ |
||||
public sealed class GraphQLQuery |
public sealed class DummySchema : Schema |
||||
{ |
{ |
||||
public string OperationName { get; set; } |
public DummySchema() |
||||
|
{ |
||||
public string Query { get; set; } |
Query = new ObjectGraphType(); |
||||
|
} |
||||
public Inputs? Inputs { get; set; } |
|
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using GraphQL; |
||||
|
using Squidex.Domain.Apps.Entities.Contents.GraphQL; |
||||
|
|
||||
|
namespace Squidex.Web.GraphQL |
||||
|
{ |
||||
|
public sealed class DynamicExecutor : IDocumentExecuter |
||||
|
{ |
||||
|
private readonly IGraphQLService graphQLService; |
||||
|
|
||||
|
public DynamicExecutor(IGraphQLService graphQLService) |
||||
|
{ |
||||
|
this.graphQLService = graphQLService; |
||||
|
} |
||||
|
|
||||
|
public Task<ExecutionResult> ExecuteAsync(ExecutionOptions options) |
||||
|
{ |
||||
|
return graphQLService.ExecuteAsync(options); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using GraphQL.Server.Transports.AspNetCore; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Squidex.Domain.Apps.Entities; |
||||
|
using Squidex.Domain.Apps.Entities.Contents.GraphQL; |
||||
|
|
||||
|
namespace Squidex.Web.GraphQL |
||||
|
{ |
||||
|
public sealed class DynamicUserContextBuilder : IUserContextBuilder |
||||
|
{ |
||||
|
private readonly ObjectFactory factory = ActivatorUtilities.CreateFactory(typeof(GraphQLExecutionContext), new[] { typeof(Context) }); |
||||
|
|
||||
|
public Task<IDictionary<string, object>> BuildUserContext(HttpContext httpContext) |
||||
|
{ |
||||
|
var executionContext = (GraphQLExecutionContext)factory(httpContext.RequestServices, new object[] { httpContext.Context() }); |
||||
|
|
||||
|
return Task.FromResult<IDictionary<string, object>>(executionContext); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using GraphQL.Server.Transports.AspNetCore; |
||||
|
using GraphQL.Server.Transports.AspNetCore.Common; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
|
||||
|
namespace Squidex.Web.GraphQL |
||||
|
{ |
||||
|
public sealed class GraphQLMiddleware : GraphQLHttpMiddleware<DummySchema> |
||||
|
{ |
||||
|
private static readonly RequestDelegate Noop = _ => Task.CompletedTask; |
||||
|
|
||||
|
public GraphQLMiddleware(IGraphQLRequestDeserializer deserializer) |
||||
|
: base(Noop, default, deserializer) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Microsoft.Net.Http.Headers; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public sealed class CachingKeysMiddleware |
||||
|
{ |
||||
|
private readonly CachingOptions cachingOptions; |
||||
|
private readonly CachingManager cachingManager; |
||||
|
private readonly RequestDelegate next; |
||||
|
|
||||
|
public CachingKeysMiddleware(CachingManager cachingManager, IOptions<CachingOptions> cachingOptions, RequestDelegate next) |
||||
|
{ |
||||
|
Guard.NotNull(cachingManager, nameof(cachingManager)); |
||||
|
Guard.NotNull(cachingOptions, nameof(cachingOptions)); |
||||
|
Guard.NotNull(next, nameof(next)); |
||||
|
|
||||
|
this.cachingOptions = cachingOptions.Value; |
||||
|
this.cachingManager = cachingManager; |
||||
|
|
||||
|
this.next = next; |
||||
|
} |
||||
|
|
||||
|
public async Task InvokeAsync(HttpContext context) |
||||
|
{ |
||||
|
cachingManager.Start(context); |
||||
|
|
||||
|
AppendAuthHeaders(context); |
||||
|
|
||||
|
context.Response.OnStarting(x => |
||||
|
{ |
||||
|
var httpContext = (HttpContext)x; |
||||
|
|
||||
|
if (httpContext.Response.Headers.TryGetString(HeaderNames.ETag, out var etag)) |
||||
|
{ |
||||
|
if (!cachingOptions.StrongETag && IsWeakEtag(etag)) |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = ToWeakEtag(etag); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
cachingManager.Finish(httpContext); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
}, context); |
||||
|
|
||||
|
await next(context); |
||||
|
} |
||||
|
|
||||
|
private void AppendAuthHeaders(HttpContext httpContext) |
||||
|
{ |
||||
|
cachingManager.AddHeader("Auth-State"); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(httpContext.User.OpenIdSubject())) |
||||
|
{ |
||||
|
cachingManager.AddHeader(HeaderNames.Authorization); |
||||
|
} |
||||
|
else if (!string.IsNullOrWhiteSpace(httpContext.User.OpenIdClientId())) |
||||
|
{ |
||||
|
cachingManager.AddHeader("Auth-ClientId"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static string ToWeakEtag(string? etag) |
||||
|
{ |
||||
|
return $"W/{etag}"; |
||||
|
} |
||||
|
|
||||
|
private static bool IsWeakEtag(string etag) |
||||
|
{ |
||||
|
return !etag.StartsWith("W/", StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,31 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.NewtonsoftJson; |
|
||||
using Squidex.Domain.Apps.Entities.Contents.GraphQL; |
|
||||
using Squidex.Infrastructure.Reflection; |
|
||||
|
|
||||
namespace Squidex.Areas.Api.Controllers.Contents.Models |
|
||||
{ |
|
||||
public class GraphQLGetDto |
|
||||
{ |
|
||||
public string OperationName { get; set; } |
|
||||
|
|
||||
public string Query { get; set; } |
|
||||
|
|
||||
public string Variables { get; set; } |
|
||||
|
|
||||
public GraphQLQuery ToQuery() |
|
||||
{ |
|
||||
var query = SimpleMapper.Map(this, new GraphQLQuery()); |
|
||||
|
|
||||
query.Inputs = Variables?.ToInputs(); |
|
||||
|
|
||||
return query; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,32 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using GraphQL.NewtonsoftJson; |
|
||||
using Newtonsoft.Json.Linq; |
|
||||
using Squidex.Domain.Apps.Entities.Contents.GraphQL; |
|
||||
using Squidex.Infrastructure.Reflection; |
|
||||
|
|
||||
namespace Squidex.Areas.Api.Controllers.Contents.Models |
|
||||
{ |
|
||||
public class GraphQLPostDto |
|
||||
{ |
|
||||
public string OperationName { get; set; } |
|
||||
|
|
||||
public string Query { get; set; } |
|
||||
|
|
||||
public JObject Variables { get; set; } |
|
||||
|
|
||||
public GraphQLQuery ToQuery() |
|
||||
{ |
|
||||
var query = SimpleMapper.Map(this, new GraphQLQuery()); |
|
||||
|
|
||||
query.Inputs = Variables?.ToInputs(); |
|
||||
|
|
||||
return query; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,328 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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 System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Http.Features; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Microsoft.Extensions.Primitives; |
||||
|
using Microsoft.Net.Http.Headers; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public class CachingKeysMiddlewareTests |
||||
|
{ |
||||
|
private readonly List<(object, Func<object, Task>)> callbacks = new List<(object, Func<object, Task>)>(); |
||||
|
private readonly IHttpContextAccessor httpContextAccessor = A.Fake<IHttpContextAccessor>(); |
||||
|
private readonly IHttpResponseBodyFeature httpResponseBodyFeature = A.Fake<IHttpResponseBodyFeature>(); |
||||
|
private readonly IHttpResponseFeature httpResponseFeature = A.Fake<IHttpResponseFeature>(); |
||||
|
private readonly HttpContext httpContext = new DefaultHttpContext(); |
||||
|
private readonly CachingOptions cachingOptions = new CachingOptions(); |
||||
|
private readonly CachingManager cachingManager; |
||||
|
private readonly RequestDelegate next; |
||||
|
private readonly CachingKeysMiddleware sut; |
||||
|
private bool isNextCalled; |
||||
|
|
||||
|
public CachingKeysMiddlewareTests() |
||||
|
{ |
||||
|
var headers = new HeaderDictionary(); |
||||
|
|
||||
|
A.CallTo(() => httpResponseFeature.Headers) |
||||
|
.Returns(headers); |
||||
|
|
||||
|
A.CallTo(() => httpResponseFeature.OnStarting(A<Func<object, Task>>._, A<object>._)) |
||||
|
.Invokes(c => |
||||
|
{ |
||||
|
callbacks.Add(( |
||||
|
c.GetArgument<object>(1)!, |
||||
|
c.GetArgument<Func<object, Task>>(0)!)); |
||||
|
}); |
||||
|
|
||||
|
A.CallTo(() => httpResponseBodyFeature.StartAsync(A<CancellationToken>._)) |
||||
|
.Invokes(c => |
||||
|
{ |
||||
|
foreach (var (state, callback) in callbacks) |
||||
|
{ |
||||
|
callback(state).Wait(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
httpContext.Features.Set(httpResponseBodyFeature); |
||||
|
httpContext.Features.Set(httpResponseFeature); |
||||
|
|
||||
|
next = context => |
||||
|
{ |
||||
|
isNextCalled = true; |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
}; |
||||
|
|
||||
|
A.CallTo(() => httpContextAccessor.HttpContext) |
||||
|
.Returns(httpContext); |
||||
|
|
||||
|
cachingManager = new CachingManager(httpContextAccessor, Options.Create(cachingOptions)); |
||||
|
|
||||
|
sut = new CachingKeysMiddleware(cachingManager, Options.Create(cachingOptions), next); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_invoke_next() |
||||
|
{ |
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_etag_if_not_found() |
||||
|
{ |
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.Equal(StringValues.Empty, httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_authorization_header_as_vary() |
||||
|
{ |
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.Equal("Auth-State", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_authorization_as_header_when_user_has_subject() |
||||
|
{ |
||||
|
var identity = (ClaimsIdentity)httpContext.User.Identity!; |
||||
|
|
||||
|
identity.AddClaim(new Claim(OpenIdClaims.Subject, "my-id")); |
||||
|
|
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.Equal("Auth-State,Authorization", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_client_id_as_header_when_user_has_client_but_no_subject() |
||||
|
{ |
||||
|
var identity = (ClaimsIdentity)httpContext.User.Identity!; |
||||
|
|
||||
|
identity.AddClaim(new Claim(OpenIdClaims.ClientId, "my-client")); |
||||
|
|
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.Equal("Auth-State,Auth-ClientId", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_null_header_as_vary() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddHeader(null!); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("Auth-State", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_empty_header_as_vary() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddHeader(string.Empty); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("Auth-State", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_custom_header_as_vary() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddHeader("X-Header"); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("Auth-State,X-Header", httpContext.Response.Headers[HeaderNames.Vary]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_etag_if_empty() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = string.Empty; |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(string.Empty, httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_convert_strong_etag_if_disabled() |
||||
|
{ |
||||
|
cachingOptions.StrongETag = true; |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = "13"; |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("13", httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_convert_already_weak_tag() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = "W/13"; |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("W/13", httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_convert_strong_to_weak_tag() |
||||
|
{ |
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = "13"; |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("W/13", httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_convert_empty_string_to_weak_tag() |
||||
|
{ |
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = string.Empty; |
||||
|
|
||||
|
await MakeRequestAsync(); |
||||
|
|
||||
|
Assert.Equal(string.Empty, httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_surrogate_keys() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
cachingOptions.MaxSurrogateKeysSize = 100; |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(id1, 12); |
||||
|
cachingManager.AddDependency(id2, 12); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal($"{id1} {id2}", httpContext.Response.Headers["Surrogate-Key"]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_append_surrogate_keys_if_just_enough_space_for_one() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
cachingOptions.MaxSurrogateKeysSize = 36; |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(id1, 12); |
||||
|
cachingManager.AddDependency(id2, 12); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal($"{id1}", httpContext.Response.Headers["Surrogate-Key"]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_surrogate_keys_if_maximum_is_exceeded() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
cachingOptions.MaxSurrogateKeysSize = 20; |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(id1, 12); |
||||
|
cachingManager.AddDependency(id2, 12); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(StringValues.Empty, httpContext.Response.Headers["Surrogate-Key"]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_append_surrogate_keys_if_maximum_is_overriden() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
httpContext.Request.Headers[CachingManager.SurrogateKeySizeHeader] = "20"; |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(id1, 12); |
||||
|
cachingManager.AddDependency(id2, 12); |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(StringValues.Empty, httpContext.Response.Headers["Surrogate-Key"]); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_generate_etag_from_ids_and_versions() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(id1, 12); |
||||
|
cachingManager.AddDependency(id2, 12); |
||||
|
cachingManager.AddDependency(12); |
||||
|
}); |
||||
|
|
||||
|
Assert.True(httpContext.Response.Headers[HeaderNames.ETag].ToString().Length > 20); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_not_generate_etag_when_already_added() |
||||
|
{ |
||||
|
var id1 = DomainId.NewGuid(); |
||||
|
var id2 = DomainId.NewGuid(); |
||||
|
|
||||
|
await MakeRequestAsync(() => |
||||
|
{ |
||||
|
cachingManager.AddDependency(DomainId.NewGuid(), 12); |
||||
|
cachingManager.AddDependency(DomainId.NewGuid(), 12); |
||||
|
cachingManager.AddDependency(12); |
||||
|
|
||||
|
httpContext.Response.Headers[HeaderNames.ETag] = "W/20"; |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal("W/20", httpContext.Response.Headers[HeaderNames.ETag]); |
||||
|
} |
||||
|
|
||||
|
private async Task MakeRequestAsync(Action? action = null) |
||||
|
{ |
||||
|
await sut.InvokeAsync(httpContext); |
||||
|
|
||||
|
action?.Invoke(); |
||||
|
|
||||
|
await httpContext.Response.StartAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue