Skip to content

Commit

Permalink
Added field middleware that is bound to the query executer. (#482)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jan 20, 2019
1 parent 27b4a17 commit e034f03
Show file tree
Hide file tree
Showing 54 changed files with 1,066 additions and 746 deletions.
5 changes: 5 additions & 0 deletions .bettercodehub.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
component_depth: 2
languages:
- csharp
exclude:
- /examples/.*
- /benchmarks/.*
- /src/Templates/.*
- .*\.js
File renamed without changes.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Query complexity validation rules [#80](https://github.com/ChilliCream/hotchocolate/issues/80)
- Added support for relay global object identification specification [specification](http://facebook.github.io/relay/graphql/objectidentification.htm).
- Added Source Code Link for NuGet support.
- Added support for a executer scoped field middleware [#482](https://github.com/ChilliCream/hotchocolate/issues/482).

### Changed

Expand Down
8 changes: 2 additions & 6 deletions src/Core/Core.Tests/Execution/ExecutionContextTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ public void ContextDataArePassedOn()

var variables = new Mock<IVariableCollection>();

var directives = new DirectiveLookup(new Dictionary<ObjectType, IDictionary<FieldNode, IReadOnlyCollection<IDirective>>>());

var contextData = new Dictionary<string, object>
{
{ "abc", "123" }
Expand All @@ -45,7 +43,7 @@ public void ContextDataArePassedOn()
// act
var executionContext = new ExecutionContext(
schema, serviceScope, operation.Object,
variables.Object, directives, contextData,
variables.Object, fs => null, contextData,
CancellationToken.None);

// assert
Expand Down Expand Up @@ -77,8 +75,6 @@ public void CloneExecutionContext()

var variables = new Mock<IVariableCollection>();

var directives = new DirectiveLookup(new Dictionary<ObjectType, IDictionary<FieldNode, IReadOnlyCollection<IDirective>>>());

var contextData = new Dictionary<string, object>
{
{ "abc", "123" }
Expand All @@ -87,7 +83,7 @@ public void CloneExecutionContext()
// act
var executionContext = new ExecutionContext(
schema, serviceScope, operation.Object,
variables.Object, directives, contextData,
variables.Object, fs => null, contextData,
CancellationToken.None);
IExecutionContext cloned = executionContext.Clone();

Expand Down
106 changes: 106 additions & 0 deletions src/Core/Core.Tests/Execution/ExecutionFieldMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Threading.Tasks;
using ChilliCream.Testing;
using HotChocolate.Resolvers;
using Xunit;

namespace HotChocolate.Execution
{
public class ExecutionFieldMiddleware
{
[Fact]
public async Task ExecuteFieldWithExecutionMiddleware()
{
// arrange
var schema = Schema.Create(
"type Query { a: String }",
c => c.Use(next => context =>
{
context.Result = "a";
return next.Invoke(context);
}));

IQueryExecutor executor = schema.MakeExecutable(b =>
b.UseDefaultPipeline()
.UseField(next => async context =>
{
await next.Invoke(context);
if (context.Result is string s)
{
context.Result = s.ToUpperInvariant();
}
}));

// act
IExecutionResult result = await executor.ExecuteAsync("{ a }");

// assert
result.Snapshot();
}

[Fact]
public async Task ExecuteFieldWithExecutionClassMiddleware()
{
// arrange
var schema = Schema.Create(
"type Query { a: String }",
c => c.Use(next => context =>
{
context.Result = "a";
return next.Invoke(context);
}));

IQueryExecutor executor = schema.MakeExecutable(b =>
b.UseDefaultPipeline().UseField<ToUpperMiddleware>());

// act
IExecutionResult result = await executor.ExecuteAsync("{ a }");

// assert
result.Snapshot();
}

[Fact]
public async Task ExecuteFieldWithExecutionClassMiddlewareWithFactory()
{
// arrange
var schema = Schema.Create(
"type Query { a: String }",
c => c.Use(next => context =>
{
context.Result = "a";
return next.Invoke(context);
}));

IQueryExecutor executor = schema.MakeExecutable(b =>
b.UseDefaultPipeline()
.UseField((sp, next) => new ToUpperMiddleware(next)));

// act
IExecutionResult result = await executor.ExecuteAsync("{ a }");

// assert
result.Snapshot();
}

public class ToUpperMiddleware
{
private readonly FieldDelegate _next;

public ToUpperMiddleware(FieldDelegate next)
{
_next = next;
}

public async Task InvokeAsync(IMiddlewareContext context)
{
await _next.Invoke(context);

if (context.Result is string s)
{
context.Result = s.ToUpperInvariant();
}
}
}
}
}
74 changes: 74 additions & 0 deletions src/Core/Core.Tests/Execution/IntrospectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,60 @@ public async Task ExecuteGraphiQLIntrospectionQuery()
result.Snapshot();
}

[Fact]
public async Task FieldMiddlewareDoesNotHaveAnEffectOnIntrospection()
{
// arrange
string query = "{ __typename a }";

var schema = Schema.Create(c =>
{
c.RegisterExtendedScalarTypes();
c.RegisterType<Query>();
c.Use(next => async context =>
{
await next.Invoke(context);
if (context.Result is string s)
{
context.Result = s.ToUpperInvariant();
}
});
});

IQueryExecutor executor = schema.MakeExecutable();

// act
IExecutionResult result = await executor.ExecuteAsync(query);

// assert
Assert.Empty(result.Errors);
result.Snapshot();
}

[Fact]
public async Task DirectiveMiddlewareDoesWorkOnIntrospection()
{
// arrange
string query = "{ __typename @upper a }";

var schema = Schema.Create(c =>
{
c.RegisterExtendedScalarTypes();
c.RegisterType<Query>();
c.RegisterDirective<UpperDirectiveType>();
});

IQueryExecutor executor = schema.MakeExecutable();

// act
IExecutionResult result = await executor.ExecuteAsync(query);

// assert
Assert.Empty(result.Errors);
result.Snapshot();
}

private static Schema CreateSchema()
{
return Schema.Create(c =>
Expand Down Expand Up @@ -119,5 +173,25 @@ protected override void Configure(IObjectTypeDescriptor descriptor)
.Resolver(() => "foo.a");
}
}

private sealed class UpperDirectiveType
: DirectiveType
{
protected override void Configure(
IDirectiveTypeDescriptor descriptor)
{
descriptor.Name("upper");
descriptor.Location(DirectiveLocation.Field);
descriptor.Middleware(next => async context =>
{
await next.Invoke(context);
if (context.Result is string s)
{
context.Result = s.ToUpperInvariant();
}
});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ public async Task ParseQueryMiddleware_ValidQuery_DocumentIsSet()
}
}.ToReadOnly();

var context = new QueryContext(
schema, MiddlewareTools.CreateEmptyRequestServiceScope(), request);
var context = new QueryContext
(
schema,
MiddlewareTools.CreateEmptyRequestServiceScope(),
request,
fs => fs.Field.Middleware
);

context.Document = Parser.Default.Parse(request.Query);
context.Operation = new Operation(
context.Document,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ public async Task ExecuteOperationMiddleware_Mutation_ExecutedSerially()
typeof(IErrorHandler),
ErrorHandler.Default));

var context = new QueryContext(
schema, services.CreateRequestServiceScope(), request)
var context = new QueryContext
(
schema,
services.CreateRequestServiceScope(),
request,
fs => fs.Field.Middleware
)
{
Document = query,
Operation = operation,
Expand All @@ -67,7 +72,7 @@ public async Task ExecuteOperationMiddleware_Mutation_ExecutedSerially()
var middleware = new ExecuteOperationMiddleware(
c => Task.CompletedTask,
strategyResolver,
new Cache<DirectiveLookup>(10));
new Cache<DirectiveMiddlewareCompiler>(10));

// act
await middleware.InvokeAsync(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,13 @@ type Query {
typeof(IErrorHandler),
ErrorHandler.Default));

var context = new QueryContext(
schema, services.CreateRequestServiceScope(), request)
var context = new QueryContext
(
schema,
services.CreateRequestServiceScope(),
request,
fs => fs.Field.Middleware
)
{
Document = query,
Operation = operation,
Expand Down Expand Up @@ -131,8 +136,13 @@ type Query {
typeof(IErrorHandler),
ErrorHandler.Default));

var context = new QueryContext(
schema, services.CreateRequestServiceScope(), request)
var context = new QueryContext
(
schema,
services.CreateRequestServiceScope(),
request,
fs => fs.Field.Middleware
)
{
Document = query,
Operation = operation,
Expand Down Expand Up @@ -212,8 +222,13 @@ input FooInput {
typeof(IErrorHandler),
ErrorHandler.Default));

var context = new QueryContext(
schema, services.CreateRequestServiceScope(), request)
var context = new QueryContext
(
schema,
services.CreateRequestServiceScope(),
request,
fs => fs.Field.Middleware
)
{
Document = query,
Operation = operation,
Expand Down Expand Up @@ -290,17 +305,22 @@ input FooInput {
typeof(IErrorHandler),
ErrorHandler.Default));

var context = new QueryContext(
schema, services.CreateRequestServiceScope(), request)
var context = new QueryContext
(
schema,
services.CreateRequestServiceScope(),
request,
fs => fs.Field.Middleware
)
{
Document = query,
Operation = operation,
Variables = new VariableValueBuilder(
schema, operation.Definition)
.CreateValues(new Dictionary<string, object>
{
schema, operation.Definition)
.CreateValues(new Dictionary<string, object>
{
{ "i", count }
})
})
};

var middleware = new MaxComplexityMiddleware(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ public async Task ParseQueryMiddleware_ValidQuery_DocumentIsSet()

var request = new QueryRequest("{ a }").ToReadOnly();

var context = new QueryContext(
var context = new QueryContext
(
schema,
MiddlewareTools.CreateEmptyRequestServiceScope(),
request);
request,
fs => fs.Field.Middleware
);

var middleware = new ParseQueryMiddleware(
c => Task.CompletedTask,
Expand All @@ -43,10 +46,13 @@ public Task ParseQueryMiddleware_InvalidQuery_DocumentNull()

var request = new QueryRequest("{").ToReadOnly();

var context = new QueryContext(
var context = new QueryContext
(
schema,
MiddlewareTools.CreateEmptyRequestServiceScope(),
request);
request,
fs => fs.Field.Middleware
);

var middleware = new ParseQueryMiddleware(
c => Task.CompletedTask,
Expand Down
Loading

0 comments on commit e034f03

Please sign in to comment.