Skip to content

Commit

Permalink
Server Modularization (ChilliCream#940)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Jul 31, 2019
1 parent ac9bafc commit 090a84b
Show file tree
Hide file tree
Showing 181 changed files with 5,832 additions and 1,192 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added UTF-8 request parser. [#869](https://github.com/ChilliCream/hotchocolate/pull/869)
- Added new syntax visitor API.
- Added Redis subscription provider [#902](https://github.com/ChilliCream/hotchocolate/pull/902)
- Added support for batching over HTTP [#933](https://github.com/ChilliCream/hotchocolate/pull/933)

### Changed

- Subscription now uses pipeline API to abstract sockets. [#807](https://github.com/ChilliCream/hotchocolate/pull/807)
- Improved parser performance. [#806](https://github.com/ChilliCream/hotchocolate/pull/806)
- Roles collection on authorization directive is now interpreted as OR.
- The type conversion API is now better integreated with dependency injection.
- The server is now more modularized and the various server middlewares can be added separably.

### Fixed

Expand All @@ -31,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Respect UseXmlDocumentation with Schema.Create [#897](https://github.com/ChilliCream/hotchocolate/pull/897)
- Variables now work in lists and input objects [#896](https://github.com/ChilliCream/hotchocolate/pull/896)
- Fixed url scalar now correctly detects url strings.
- Support directives declared stitched schemas [#936](https://github.com/ChilliCream/hotchocolate/pull/936)

## [9.0.4] - 2019-06-16

Expand Down Expand Up @@ -156,7 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- The authoization directive is now more aligned how the authorize attribute in ASP.net works.
- The authoization directive is now more aligned how the authorize attribute in ASP .Net works.

### Fixed

Expand Down Expand Up @@ -304,7 +307,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Separate package providing a _GraphiQL_ middleware. The middleware can serve all of _GraphiQL_ without needing to refer to CDNs making it useful even in closed networks. Moreover, we have configured _GraphiQL_ to work with the _GraphQL-ws_ protocol which is supported by _Hot Chocolate_.
- Initial Support for _GraphQL_ subscriptions. We currently support the _GraphQL-ws_ protocol over web sockets. There will be a lot of additional work in version _0.7.0_ that will harden it.
- Authorization package for ASP.net core which supports policy-base authorization on fields.
- Authorization package for ASP .Net core which supports policy-base authorization on fields.
- Diagnostic source which can be used to track field execution times and other events.
- Implementing a directive middleware has now become much easier with this release. We have built the authorize-directive with these new APIs.

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- `StringExtensions.cs`
- `ServiceCollectionExtensions.cs`
- Write for every type a separate extension file.
- Use the origin namespace of the type; use `HotChocolate` when extending an external type like `Microsoft.Ectensions.DependencyInjection.IServiceCollection` or `HotChocolate.AspNetCore` when extending a ASP.net core specific type like `Microsoft.AspNetCore.Builder.IApplicationBuilder`.
- Use the origin namespace of the type; use `HotChocolate` when extending an external type like `Microsoft.Ectensions.DependencyInjection.IServiceCollection` or `HotChocolate.AspNetCore` when extending a ASP .Net core specific type like `Microsoft.AspNetCore.Builder.IApplicationBuilder`.
Example:
```csharp
using Microsoft.Extensions.DependencyInjection;
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ This runs a query fetching the one field defined. The graphql function will firs
Console.WriteLine(executor.Execute("{ foo }").ToJson());
```

In order to set up a GraphQL HTTP endpoint, Hot Chocolate comes with an ASP.net core middleware.
In order to set up a GraphQL HTTP endpoint, Hot Chocolate comes with an ASP .Net core middleware.

Create a new project with the web template that comes with your dotnet CLI.

Expand Down
2 changes: 1 addition & 1 deletion src/Core/Abstractions/Execution/IQueryRequestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ IQueryRequestBuilder SetQuery(
IQueryRequestBuilder SetQueryName(
string queryName);
IQueryRequestBuilder SetQueryHash(
string queryHash);
string queryHash);
IQueryRequestBuilder SetOperation(
string operationName);
IQueryRequestBuilder SetVariableValues(
Expand Down
10 changes: 8 additions & 2 deletions src/Core/Abstractions/Execution/QueryRequestBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.Serialization;
using System.Linq;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -267,13 +268,18 @@ public static QueryRequestBuilder From(GraphQLRequest request)
{
var builder = QueryRequestBuilder.New();

builder.SetQuery(request.Query)
builder
.SetQueryName(request.QueryName)
.SetQueryName(request.QueryName) // TODO : we should have a hash here
.SetQueryHash(request.QueryHash)
.SetOperation(request.OperationName)
.SetVariableValues(request.Variables)
.SetProperties(request.Extensions);

if (request.Query != null)
{
builder.SetQuery(request.Query);
}

return builder;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public async Task<IReadOnlyQueryResult> ReadAsync(
.SetVariableValues(variableValues)
.AddExportedVariables(_exportedVariables)
.SetQueryName(null) // TODO ... should we create a name here?
.SetQueryHash(null)
.Create();

var result =
Expand Down
13 changes: 7 additions & 6 deletions src/Core/Core/Execution/JsonQueryResultSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ public sealed class JsonQueryResultSerializer

public Task SerializeAsync(
IReadOnlyQueryResult result,
Stream stream)
Stream stream) =>
SerializeAsync(result, stream, CancellationToken.None);

public Task SerializeAsync(
IReadOnlyQueryResult result,
Stream stream,
CancellationToken cancellationToken)
{
if (result is null)
{
Expand All @@ -32,10 +38,5 @@ public Task SerializeAsync(
byte[] buffer = _encoding.GetBytes(json);
return stream.WriteAsync(buffer, 0, buffer.Length);
}

public Task SerializeAsync(IReadOnlyQueryResult result, Stream stream, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
57 changes: 39 additions & 18 deletions src/Core/Core/Execution/Middleware/ParseQueryMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,46 @@ public async Task InvokeAsync(IQueryContext context)
try
{
bool documentRetrievedFromCache = true;
string queryKey = context.Request.QueryName;
ICachedQuery cachedQuery = null;

string queryKey = context.Request.QueryHash
?? context.Request.QueryName;
if (queryKey is null || context.Request.Query != null)
{
queryKey = context.Request.QueryHash is null
? _documentHashProvider.ComputeHash(
context.Request.Query.ToSource())
: context.Request.QueryHash;
}

if (queryKey is null)
if (context.Request.Query is null
&& !_queryCache.TryGet(queryKey, out cachedQuery))
{
queryKey = _documentHashProvider.ComputeHash(
context.Request.Query.ToSource());
// TODO : check for query storage here?
// TODO : RESOURCES
context.Result = QueryResult.CreateError(
ErrorBuilder.New()
.SetMessage("persistedQueryNotFound")
.SetCode("CACHED_QUERY_NOT_FOUND")
.Build());
return;
}

if (cachedQuery is null)
{
cachedQuery = _queryCache.GetOrCreate(
queryKey,
() =>
{
documentRetrievedFromCache = false;
DocumentNode document =
ParseDocument(context.Request.Query);
return new CachedQuery(queryKey, document);
});
}

// update context
context.QueryKey = queryKey;
context.CachedQuery = _queryCache.GetOrCreate(
queryKey,
() =>
{
documentRetrievedFromCache = false;
DocumentNode document =
ParseDocument(context.Request.Query);
return new CachedQuery(queryKey, document);
});
context.CachedQuery = cachedQuery;
context.Document = context.CachedQuery.Document;
context.ContextData[ContextDataKeys.DocumentCached] =
documentRetrievedFromCache;
Expand All @@ -80,9 +100,9 @@ public async Task InvokeAsync(IQueryContext context)
{
_diagnosticEvents.EndParsing(activity, context);
}
}

await _next(context).ConfigureAwait(false);
await _next(context).ConfigureAwait(false);
}
}

private DocumentNode ParseDocument(IQuery query)
Expand All @@ -104,8 +124,9 @@ private DocumentNode ParseDocument(IQuery query)

private static bool IsContextIncomplete(IQueryContext context)
{
return context.Request == null
|| context.Request.Query == null;
return context.Request is null
|| (context.Request.Query is null
&& context.Request.QueryName is null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public ValidateQueryMiddleware(

public async Task InvokeAsync(IQueryContext context)
{

if (context.Document == null)
{
context.Result = QueryResult.CreateError(new Error
Expand Down
8 changes: 6 additions & 2 deletions src/Core/Language/Utf8/GraphQLRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ namespace HotChocolate.Language
public class GraphQLRequest
{
public GraphQLRequest(DocumentNode query)
: this(query, null, null, null, null)
: this(query, null, null, null, null, null)
{
}

public GraphQLRequest(DocumentNode query, string queryName)
: this(query, queryName, null, null, null)
: this(query, queryName, null, null, null, null)
{
}

public GraphQLRequest(
DocumentNode query,
string queryName,
string queryHash,
string operationName,
IReadOnlyDictionary<string, object> variables,
IReadOnlyDictionary<string, object> extensions)
Expand All @@ -29,6 +30,7 @@ public GraphQLRequest(

OperationName = operationName;
QueryName = queryName;
QueryHash = queryHash;
Query = query;
Variables = variables;
Extensions = extensions;
Expand All @@ -38,6 +40,8 @@ public GraphQLRequest(

public string QueryName { get; }

public string QueryHash { get; }

public string OperationName { get; }

public IReadOnlyDictionary<string, object> Variables { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/Core/Language/Utf8/Utf8GraphQLRequestParser.Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ private ref struct Request

public string NamedQuery { get; set; }

public string QueryHash { get; set; }

public ReadOnlySpan<byte> Query { get; set; }

public bool IsQueryNull { get; set; }
Expand Down
35 changes: 24 additions & 11 deletions src/Core/Language/Utf8/Utf8GraphQLRequestParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,30 +136,43 @@ private GraphQLRequest ParseRequest()
"property have to have a value.");
}

DocumentNode document;
DocumentNode document = null;

if (_useCache)
if (!request.IsQueryNull)
{
if (request.NamedQuery is null)
if (_useCache)
{
request.NamedQuery =
_hashProvider.ComputeHash(request.Query);
}
if (request.NamedQuery is null)
{
request.NamedQuery =
request.QueryHash =
_hashProvider.ComputeHash(request.Query);
}

if (!_cache.TryGetDocument(request.NamedQuery, out document))
if (!_cache.TryGetDocument(
request.NamedQuery,
out document))
{
document = ParseQuery(in request);

if (request.QueryHash is null)
{
request.QueryHash =
_hashProvider.ComputeHash(request.Query);
}
}
}
else
{
document = ParseQuery(in request);
}
}
else
{
document = ParseQuery(in request);
}

return new GraphQLRequest
(
document,
request.NamedQuery,
request.QueryHash,
request.OperationName,
request.Variables,
request.Extensions
Expand Down
1 change: 1 addition & 0 deletions src/Core/Types/InternalsVisibleTo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("HotChocolate.AspNetCore.Tests")]
[assembly: InternalsVisibleTo("HotChocolate.AspNetClassic.Tests")]
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ public interface IInterfaceTypeDescriptor
{
// <summary>
/// Associates the specified
/// <paramref name="interfaceTypeDefinitionNode"/>
/// <paramref name="interfaceTypeDefinition"/>
/// with the <see cref="InterfaceType"/>.
/// </summary>
/// <param name="syntaxNode">
/// The <see cref="InterfaceTypeDefinitionNode"/> of a parsed schema.
/// </param>
IInterfaceTypeDescriptor SyntaxNode(
InterfaceTypeDefinitionNode interfaceTypeDefinitionNode);
InterfaceTypeDefinitionNode interfaceTypeDefinition);

/// <summary>
/// Defines the name of the <see cref="InterfaceType"/>.
Expand All @@ -41,7 +41,7 @@ IInterfaceTypeDescriptor ResolveAbstractType(

IInterfaceFieldDescriptor Field(NameString name);

IInterfaceTypeDescriptor Directive<T>(T directive)
IInterfaceTypeDescriptor Directive<T>(T directiveInstance)
where T : class;

IInterfaceTypeDescriptor Directive<T>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,32 @@ public interface IInterfaceTypeDescriptor<T>
, IFluent
{
// <summary>
/// Associates the specified <paramref name="syntaxNode"/>
/// Associates the specified
/// <paramref name="syntaxinterfaceTypeDefinitionode"/>
/// with the <see cref="InterfaceType"/>.
/// </summary>
/// <param name="syntaxNode">
/// <param name="interfaceTypeDefinition">
/// The <see cref="InterfaceTypeDefinitionNode"/> of a parsed schema.
/// </param>
IInterfaceTypeDescriptor<T> SyntaxNode(
InterfaceTypeDefinitionNode syntaxNode);
InterfaceTypeDefinitionNode syntaxinterfaceTypeDefinitionode);

/// <summary>
/// Defines the name of the <see cref="InterfaceType"/>.
/// </summary>
/// <param name="name">The interface type name.</param>
/// <param name="value">The interface type name.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="name"/> is <c>null</c> or
/// <paramref name="value"/> is <c>null</c> or
/// <see cref="string.Empty"/>.
/// </exception>
IInterfaceTypeDescriptor<T> Name(NameString name);
IInterfaceTypeDescriptor<T> Name(NameString value);

/// <summary>
/// Adds explanatory text to the <see cref="InterfaceType"/>
/// that can be accessd via introspection.
/// </summary>
/// <param name="description">The interface type description.</param>
IInterfaceTypeDescriptor<T> Description(string description);
/// <param name="value">The interface type description.</param>
IInterfaceTypeDescriptor<T> Description(string value);

/// <summary>
/// Defines the field binding behavior.
Expand Down
Loading

0 comments on commit 090a84b

Please sign in to comment.