Skip to content

Commit

Permalink
Fixed variable rewriter (#3611)
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalSenn authored Apr 28, 2021
1 parent 6617ec6 commit 7156ab0
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public void CoerceVariableValues(
{
throw ThrowHelper.NonNullVariableIsNull(variableDefinition);
}
coercedValues[variableName] = new VariableValueOrLiteral(
variableType, null, NullValueNode.Default);
coercedValues[variableName] =
new VariableValueOrLiteral(variableType, null, NullValueNode.Default);
}
else
{
coercedValues[variableName] = CoerceVariableValue(
variableDefinition, variableType, value);
coercedValues[variableName] =
CoerceVariableValue(variableDefinition, variableType, value);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ private static ObjectValueNode Rewrite(
}
rewrittenItems[i] = rewritten;
}
else if (rewrittenItems is { })
{
rewrittenItems[i] = node.Fields[i];
}
}

if (rewrittenItems is { })
Expand Down Expand Up @@ -138,6 +142,10 @@ private static ListValueNode Rewrite(
}
rewrittenItems[i] = rewritten;
}
else if (rewrittenItems is { })
{
rewrittenItems[i] = node.Items[i];
}
}

if (rewrittenItems is { })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ context.LocalContextData[ContextValueNodeKey] is IValueNode node
: context.ArgumentLiteral<IValueNode>(argumentName);

// if no filter is defined we can stop here and yield back control.
if (filter.IsNull() ||
(context.LocalContextData.TryGetValue(
SkipFilteringKey,
out object? skipObject) &&
skipObject is bool skip &&
skip))
var skipFiltering =
context.LocalContextData.TryGetValue(SkipFilteringKey, out object? skip) &&
skip is true;

if (filter.IsNull() || skipFiltering)
{
return;
}
Expand All @@ -72,15 +71,12 @@ skipObject is bool skip &&
executorObj is VisitFilterArgument executor)
{
var inMemory =
context.Result is QueryableExecutable<TEntityType> executable &&
executable.InMemory ||
context.Result is QueryableExecutable<TEntityType> { InMemory: true } ||
context.Result is not IQueryable ||
context.Result is EnumerableQuery;

QueryableFilterContext visitorContext = executor(
filter,
filterInput,
inMemory);
QueryableFilterContext visitorContext =
executor(filter, filterInput, inMemory);

// compile expression tree
if (visitorContext.TryCreateLambda(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ async ValueTask ExecuteAsync(
IValueNode sort = context.ArgumentLiteral<IValueNode>(argumentName);

// if no sort is defined we can stop here and yield back control.
if (sort.IsNull() ||
(context.LocalContextData.TryGetValue(
SkipSortingKey,
out object? skipObject) &&
skipObject is bool skip &&
skip))
var skipSorting =
context.LocalContextData.TryGetValue(SkipSortingKey, out object? skip) &&
skip is true;

if (sort.IsNull() || skipSorting)
{
return;
}
Expand All @@ -63,8 +62,7 @@ lt.ElementType is NonNullType nn &&
executorObj is VisitSortArgument executor)
{
var inMemory =
context.Result is QueryableExecutable<TEntityType> executable &&
executable.InMemory ||
context.Result is QueryableExecutable<TEntityType> { InMemory: true } ||
context.Result is not IQueryable ||
context.Result is EnumerableQuery;

Expand Down
64 changes: 64 additions & 0 deletions src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,70 @@ public async Task CreateSchema_OnDifferentScope()
result.ToJson().MatchSnapshot(new SnapshotNameExtension("Result"));
}

[Fact]
public async Task Execute_And_OnRoot()
{
// arrange
IRequestExecutor executor = await new ServiceCollection()
.AddGraphQL()
.AddFiltering("Foo")
.AddSorting("Foo")
.AddProjections("Foo")
.AddQueryType<DifferentScope>()
.BuildRequestExecutorAsync();

// act
IExecutionResult result = await executor.ExecuteAsync(
@"
query GetBooks($title: String) {
books(where: {
and: [
{ title: { startsWith: $title } },
{ title: { eq: ""BookTitle"" } },
]
}) {
nodes { title }
}
}",
new Dictionary<string, object?> { ["title"] = "BookTitle" });

// assert
executor.Schema.Print().MatchSnapshot(new SnapshotNameExtension("Schema"));
result.ToJson().MatchSnapshot(new SnapshotNameExtension("Result"));
}

[Fact]
public async Task Execute_And_OnRoot_Reverse()
{
// arrange
IRequestExecutor executor = await new ServiceCollection()
.AddGraphQL()
.AddFiltering("Foo")
.AddSorting("Foo")
.AddProjections("Foo")
.AddQueryType<DifferentScope>()
.BuildRequestExecutorAsync();

// act
IExecutionResult result = await executor.ExecuteAsync(
@"
query GetBooks($title: String) {
books(where: {
and: [
{ title: { eq: ""BookTitle"" } },
{ title: { startsWith: $title } },
]
}) {
nodes { title }
}
}",
new Dictionary<string, object?> { ["title"] = "BookTitle" });

// assert
executor.Schema.Print().MatchSnapshot(new SnapshotNameExtension("Schema"));
result.ToJson().MatchSnapshot(new SnapshotNameExtension("Result"));
}

public class FooType : ObjectType
{
protected override void Configure(IObjectTypeDescriptor descriptor)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": {
"books": {
"nodes": [
{
"title": "BookTitle"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": {
"books": {
"nodes": [
{
"title": "BookTitle"
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
schema {
query: DifferentScope
}

type Author {
id: Int!
name: String
books: [Book!]!
}

type Book {
id: Int!
authorId: Int!
title: String
author: Author
}

"A connection to a list of items."
type BookConnection {
"Information to aid in pagination."
pageInfo: PageInfo!
"A list of edges."
edges: [BookEdge!]
"A flattened list of the nodes."
nodes: [Book!]
}

"An edge in a connection."
type BookEdge {
"A cursor for use in pagination."
cursor: String!
"The item at the end of the edge."
node: Book!
}

type DifferentScope {
books(first: Int after: String last: Int before: String where: Foo_BookFilterInput order: [Foo_BookSortInput!]): BookConnection
}

"Information about pagination in a connection."
type PageInfo {
"Indicates whether more edges exist following the set defined by the clients arguments."
hasNextPage: Boolean!
"Indicates whether more edges exist prior the set defined by the clients arguments."
hasPreviousPage: Boolean!
"When paginating backwards, the cursor to continue."
startCursor: String
"When paginating forwards, the cursor to continue."
endCursor: String
}

input Foo_AuthorFilterInput {
and: [Foo_AuthorFilterInput!]
or: [Foo_AuthorFilterInput!]
id: Foo_ComparableInt32OperationFilterInput
name: Foo_StringOperationFilterInput
books: Foo_ListFilterInputTypeOfBookFilterInput
}

input Foo_AuthorSortInput {
id: Foo_SortEnumType
name: Foo_SortEnumType
}

input Foo_BookFilterInput {
and: [Foo_BookFilterInput!]
or: [Foo_BookFilterInput!]
id: Foo_ComparableInt32OperationFilterInput
authorId: Foo_ComparableInt32OperationFilterInput
title: Foo_StringOperationFilterInput
author: Foo_AuthorFilterInput
}

input Foo_BookSortInput {
id: Foo_SortEnumType
authorId: Foo_SortEnumType
title: Foo_SortEnumType
author: Foo_AuthorSortInput
}

input Foo_ComparableInt32OperationFilterInput {
eq: Int
neq: Int
in: [Int!]
nin: [Int!]
gt: Int
ngt: Int
gte: Int
ngte: Int
lt: Int
nlt: Int
lte: Int
nlte: Int
}

input Foo_ListFilterInputTypeOfBookFilterInput {
all: Foo_BookFilterInput
none: Foo_BookFilterInput
some: Foo_BookFilterInput
any: Boolean
}

input Foo_StringOperationFilterInput {
and: [Foo_StringOperationFilterInput!]
or: [Foo_StringOperationFilterInput!]
eq: String
neq: String
contains: String
ncontains: String
in: [String]
nin: [String]
startsWith: String
nstartsWith: String
endsWith: String
nendsWith: String
}

enum Foo_SortEnumType {
ASC
DESC
}

"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`."
directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT

"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`."
directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! "Streamed when true." if: Boolean!) on FIELD
Loading

0 comments on commit 7156ab0

Please sign in to comment.