Code Monkey home page Code Monkey logo

graphql-dotnet's People

Contributors

alexmcmillan avatar benmccallum avatar cable729 avatar dependabot[bot] avatar emmanuelponnudurai avatar fiyazbinhasan avatar gao-artur avatar giorgi avatar glennblock avatar jaymitchell avatar joemcbride avatar johnrutherford avatar jquense avatar kamil-mrzyglod avatar kiril-chilingarashvili avatar koditkarvedant avatar mnie avatar moserware avatar mvestergaard avatar pekkah avatar psmolinsky avatar randyridge avatar rehansaeed avatar scmccart avatar shane32 avatar simoncropp avatar slavautesinov avatar srs6814 avatar sungam3r avatar tlil avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-dotnet's Issues

Registering types in controller

@joemcbride It would be great if you can share your thoughts on these issues:

  • We register all the types and queries in the GraphQL controller constructor. What if I have 100 types and multiple schemas and queries in my system. Will I be registering all in this controller?

Please help!

How to generate query fields, mutation fields and input/output types for graphql schema using JSON schema?

Hi ,

While evaluation graphql, also gone through the reindex.io ,which has custom schema generation capability .
In that case it takes the json schema and based on it generate CRUD operation in grapghql using query and mutation..

To Give an example:

Following schema is uploaded to there endpoint:

[
  {
    "name": "User",
    "kind": "OBJECT",
    "interfaces": [
      "Node"
    ],
    "fields": [
      {
        "name": "id",
        "type": "ID",
        "nonNull": true,
        "unique": true
      },
     {
        "name": "username",
        "type": "String",
        "nonNull": true,
       }
    ]
  }
]

Based on this following query, mutation fields and input/output types are generated,
e.g.
Schema
|
|____ query:
| ....... |getById(id:ID):User
| ....... |
allUsers(filterInfo:UserFilterInfo):UserCollection
|
|___ mutation
....... |__ createUser(userInput:UserInputType):UserType
........|__updateUser(userInput:UserInputType):UserType

Please let me know, How can we achieve it using this library?

Thanks & Regards,
-Vinayak

Using fragments

Fragments don't seem to work for me: if I use the query:

query RouteQuery { viewer { ...first } } fragment first on App { routes { createdOn } }

then i don't get anything back:

{"data":{"viewer":{"routes":[]}}}

but if I do the same without the fragments in the query:

query RouteQuery { viewer { routes { createdOn } }

then I get stuff back:

{"data":{"viewer":{"routes":[{"createdOn":"0001-01-01 00:00:00"}]}}}

Does anyone know where I am going wrong?

Using GraphQLAbstractType as schema objects

Hello,

I've implemented a graphql object in the following way -

class DynamicTemplateType : GraphQLAbstractType

This seems to work well for most purposes, and returns a dynamically resolved type through the use of the GetObjectType method on GraphQLAbstractType. I encounter a problem when using introspection however, as the __Type object is unable to resolve it's TypeKind. The code which determines this is as follows:

public TypeKind KindForInstance(GraphType type)
{
    if (type is EnumerationGraphType)
    {
        return TypeKind.ENUM;
    }
    if (type is ScalarGraphType)
    {
        return TypeKind.SCALAR;
    }
    if (type is ObjectGraphType)
    {
        return TypeKind.OBJECT;
    }
    if (type is InterfaceGraphType)
    {
        return TypeKind.INTERFACE;
    }
    if (type is UnionGraphType)
    {
        return TypeKind.UNION;
    }
    if (type is InputObjectGraphType)
    {
        return TypeKind.INPUT_OBJECT;
    }
    if (type is ListGraphType)
    {
        return TypeKind.LIST;
    }
    if (type is NonNullGraphType)
    {
        return TypeKind.NON_NULL;
    }

    throw new ExecutionError("Unkown kind of type: {0}".ToFormat(type));
}

Note the lack of a comparison for GraphQLAbstractType. Was this an oversight, or is it intentional and I'm not meant to be using this type like this?

Cheers

Connection Sample

Hi.

Sorry I'm unsure of how to use Connection<,>
As I understand it it should be used like Interface<>, in the constructor of the parent type ?

Is there a Test/Example of how to use it with edges etc. ?
Thanks!

EnumerationGraphType.Coerce compares Value instead of Name

Problem

It seems that graphql-dotnet resolves the value of a EnumerationGraphType differently then graphql-js and graphql-java implementations.

I have query with has a variable of type InputObjectGraphType, which contains a field of type EnumerationGraphType.
When using the enum below, graphql-dotnet expects the value 0, 1, 2, where as GraphiQL forces me to use one of the names RED, GREEN, BLUE.
After reading the spec, and checking the graphql-js reference and java implementation, it seems that graphql-dot-net doesn't follow the spec/reference implementation.
It compares the given value with EnumValue.Value instead of EnumValue.Name. It returns also the EnumValue.Name instead of EnumValue.Value.

Example of Enum:
public class RGBType : EnumerationGraphType
{
    public RGBType()
    {
        Name = "RGBType";

        AddValue("RED", string.Empty, 0);
        AddValue("GREEN", string.Empty, 1);
        AddValue("BLUE", string.Empty, 3);
    }
}

Solution

Replacing the contents of the EnumerationGraphType.Coerce method, with the following code, alings the behavior of graphql-dotnet with the graphql-js reference implementation.

public override object Coerce(object value)
{
    var found = Values.FirstOrDefault(v => v.Name.Equals(value));
    return found != null ? found.Value : null;
}

DocumentWriter and ILMerge /internalize nuget package

I am trying to construct a DocumentWriter instance with a specific formatting (Indented). However, with the NuGet version of GraphQL, that is not possible as the public constructor takes an internal Enum as argument (not allowed by the compiler, but ILMerge and the verifyer does not seem to care).

I am not sure why Antlr4.Runtime.dll and Newtonsoft.Json.dll are embedded. I assume that will cause some problems like license-issues (your license must be either the same or atleast reference their licenses) and effectivly disabling bindings redirects, also causing bigger products when the same (or different versioned) nuget packages is installed elsewhere.

The workaround is rather simple - either build GraphQL.dll and use that instead of nuget (unpleasent) or simply replicate the functionality of DocumentWriter elsewhere (slightly annoying but reasonable for now).

Use something other than Antlr

It has been suggested that the dependency on Antlr is off-putting due to the possible Java/IKVM dependency (note that the NuGet package doesn't have a Java/IKVM dependency, just the development project has that dependency). I don't have plans on switching currently, though if there is enough interest in doing so that is certainly an option.

Possible replacements:

Return non-scalar field

  • I want to return a non-scalar field for a type.
    How do I return fields that do not extend graph type by default, like returning a dictionary or System.Drawing.Size

Thought - Transpiling the JS implementation?

Discovered GraphQL today. I would LOVE to use this at work - But we're a dot net stack.

I would be interested in contributing (a very small amount of time, measured in hours / month is all I can spare) to this project.

At first glance, it appears that the quickest way to implement the project, would be to transpile the ES6 implementation https://github.com/graphql/graphql-js to C#. I also feel like that's the easiest way I could contribute.

Does this align with the way you're approaching the project?

Argument datatype mismatch

I have noticed that an error is not thrown when the argument datatypes do not match.

public APIQuery()
        {
            Name = "APIQuery";
            Field<UserType>(
                "GetUserById",
                arguments: new QueryArguments(
                    new[]
                    {
                        new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id",      Description = "id of the user" }
                    }),
                resolve: context => UserData.GetUser((string)context.Arguments["id"])
                );
        }

If I pass an int as the id, there is no error. I thought GraphQL is strongly typed. Or am I missing something?

PS. I am using id as a string for testing purposes
Thank you.

ResolutionArguments.GetArgument<T>() fails if T is an Enum

      Connection<FooType>()
        .Name("foo")
        .Argument<EnumType<MyEnum>>("p", "p")
        .WithObject<Bar>()
        .Unidirectional()
        .Resolve(
          c => GetFoos(c.Object, c.GetArgument<MyEnum>("p"));

I had hoped c.GetArgument("p") would work, but it returns defailt(MyEnum), ignoring the actual value of p. If i use c.GetArgument("p") I get the string.

It seems the GetArgument<> method has all the references needed to get an instance of the underlying EnumerationGraphType (that knows the actual mapping from string to the enum value) for the named argument, so I guess it should be possible to resolve it - however it might complicate the GetArgument<> method a bit...

Relay Node Interface

Hi.
Firstly thanks for a dotnet implementation of graphQL!

I'm trying to implement the relay node interface.

I have a UserType

public class UserType : ObjectGraphType
{
    public UserType(Entities db)
    {
        Name = "User";

        Field<NonNullGraphType<StringGraphType>>("id",
                resolve: context =>
                {
                    User o = (User)context.Source;
                    return Util.ToGlobalID(this.GetType(), o.Id);
                }
            );

        Interface<NodeInterface>();

        Field<StringGraphType>("Firstname");
        Field<StringGraphType>("Lastname");
    }
}

The UserType implements the NodeInterface

public NodeInterface(UserType userType)
{
    Name = "Node";
    Field<NonNullGraphType<StringGraphType>>("id");

    ResolveType = obj =>
    {                                
        if (obj is DataLayer.User)
            return userType;
        throw new Exception("Not handeling type: " + obj.GetType().Name);
    };

}

Relay then generates a query that look something like this

query User
{
  node(id:\"T3JnQ2F0ZWdvcnk=:MQ==\") {...__RelayQueryFragment1}
} 
fragment __RelayQueryFragment1 on User{id,Firstname} 

The result returned by grapql is

{"data":{"node":{}}}

While investigating the issue I found what may or may not be the issue
DocumentExecuter Line 587 does an equality test, the GetType().FullName of both variables on that line returns MyNamespace.UserType, yet fails to be equal.

So two questions:

  1. Any idea of why the relay query fails ?
  2. Do we still need to implement an "id" field if the interface defines it ?

Any other tips on how to get relay working would be awesome!

Thanks!

Query Validation

ObjectExtensions.ToObject<T>() and Guid / Nullable<T>

Hi,

ObjectExtensions.ToObject<T>() seems to have some limitations when it comes to Guids and Nullable<>. I believe there is similar cases with more complex structures (e.g. an array of Nullable or array of non-structs), where the method should likely be recursive. I made a local modified version of the method that seems to make the tests parse, where the isNullable bool is ignored and the use of Convert.ChangeType has been changed to something like this:

...
var isNullable = fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>);
if (isNullable)
{
  fieldType = fieldType.GenericTypeArguments.First();
}

propertyType.SetValue(obj, GetValue(value, fieldType), null);
...

static object GetValue(object value, Type fieldType)
{
  if (value == null) return null;

  var text = value as string;
  return text != null 
    ? TypeDescriptor.GetConverter(fieldType).ConvertFromInvariantString(text) 
    : Convert.ChangeType(value, fieldType);
}

Extending InputConversionTests with test cases:

    public class MyInput
    {
      ..
        public int? D { get; set; }
        public Guid E { get; set; }
      }

    [Test]
    public void can_convert_json_to_input_object_with_nullable_int()
    {
      var json = @"{'a': 1, 'b': '2', 'd': '5'}";
      var inputs = json.ToInputs();
      inputs.ShouldNotBeNull();
      var myInput = inputs.ToObject<MyInput>();
      myInput.ShouldNotBeNull();
      myInput.D.ShouldEqual(5);
    }

    [Test]
    public void can_convert_json_to_input_object_with_guid()
    {
      var json = @"{'a': 1, 'b': '2', 'e': '920a1b6d-f75a-4594-8567-e2c457b29cc0'}";
      var inputs = json.ToInputs();
      inputs.ShouldNotBeNull();
      var myInput = inputs.ToObject<MyInput>();
      myInput.ShouldNotBeNull();
      myInput.E.ShouldEqual(new Guid("920a1b6d-f75a-4594-8567-e2c457b29cc0"));
    }

ResolutionArguments vs ResolveFieldContext

Hi,

As I understand, in a GraphType, (atleast) two ways of adding fields is provided: a single method and a builder-pattern method:

Field<StringGraphType>("foo", resolve: (ResolveFieldContext context) => "foo");

Field<StringGraphType>()
  .Name("bar")
  .WithObject<int>()
  .Returns<string>()
  .Resolve((FieldBuilder<StringGraphType, int, string>.ResolutionArguments resolutionArguments) => "bar");

While the ResolutionArguments contains nicly typed properties etc. it does not expose the original ResolveFieldContext, nor all of it's fields. Espcially the CancellerationToken seem to be missing.

Is there a good reason for this? Or would it make sense to either:

  • Expose the ResolveFieldContext
  • Expose the missing parts from the ResolveFieldContext (CancellerationToken being one of them)
  • Make ResolutionArguments extend ResolveFieldContext (with some unfortunate "new" stuff required)

Support .NET Core

Do you have plans to release a version that supports DNXCore,Version=v5.0? Are there any external dependencies blocking this?

Optimizing Queries

This issue has been brought up elsewhere, but I wanted to specifically address it here in the context of this implementation. Without some basic optimizations, GraphQL might not get widely adopted when connecting to more traditional stores (like SQL).

Let's take this query:

posts(sortBy: RECENT, take: 10) {
    id,
    date,
    author {
        name
    },
    content
}

and assume a SQL backend with these tables and columns:

Posts(Id, PostDate, AuthorId, Content, many more columns which we don't care about)
Authors(Id, Name, many more columns which we don't care about)

We have two issues we'd like to optimize when executing this query with GraphQL:

  1. Issue a single query to fetch just the columns we care about (Id, PostDate, AuthorId, Content) and not the other columns we don't care about (i.e. overfetching).
  2. Efficiently handling the implicit JOIN to the Authors table.

Optimizing Multiple Field Selects in a Single Object

We'd like to look at all requested fields at once so that we can avoid having to fetch the entire object from our database (e.g. SELECT TOP 10 * FROM Posts ORDER BY Id DESC).

Optimizing Parent->Child JOINs

The Posts -> Author connection presents an interesting problem. There are several ways to resolve it:

1. By default, a simple GraphQL.NET implementation might execute:

SELECT TOP 10 Id, PostDate, Content, AuthorId FROM Posts ORDER BY Id DESC

To fetch the posts(sortBy: RECENT, take: 10)

and then for each one of the posts, issue a separate SELECT:

SELECT Name FROM Authors WHERE Id = 12
SELECT Name FROM Authors WHERE Id = 23
SELECT Name FROM Authors WHERE Id = 34
SELECT Name FROM Authors WHERE Id = 45
SELECT Name FROM Authors WHERE Id = 56
SELECT Name FROM Authors WHERE Id = 67
SELECT Name FROM Authors WHERE Id = 78
SELECT Name FROM Authors WHERE Id = 89
SELECT Name FROM Authors WHERE Id = 90
SELECT Name FROM Authors WHERE Id = 12

This exhibits the infamous N+1 problem and is something we'd like to avoid for performance reasons.

Performance is one of the reasons why people often build custom endpoints (something GraphQL tries to eliminate).

NOTE: In the query above, I'm ignoring the additional optimization problem of ensuring that we just fetch a given object once (e.g. Author 12 above)

2. A clever optimizer could do this:

  SELECT TOP 10 p.Id, p.PostDate, p.Content, a.Name 
    FROM Posts p
    JOIN Authors a 
      ON p.AuthorId = a.Id
ORDER BY p.Id DESC

This is the ideal query because it leads to a single trip to the database. However, it required understanding how to map the implicit Post -> Author JOIN in the GraphQL query.

3. A less clever, but still helpful optimizer could do this:

SELECT TOP 10 Id, PostDate, AuthorId, Content FROM Posts ORDER BY Id DESC
SELECT Id, Name FROM Authors WHERE Id IN (
    SELECT AuthorId 
      FROM Posts 
  ORDER BY Id DESC
)

or even more explicitly:

SELECT TOP 10 Id, PostDate, Content, AuthorId FROM Posts ORDER BY Id DESC
SELECT Id, Name FROM Authors WHERE Id IN (12, 23, 34, 45, 56, 67, 78, 89, 90)

This requires two queries to satisfy the GraphQL query. While not as efficient as the JOIN-based second approach, it's better than the N+1 first approach.

DocumentExecutor Features?

The above optimization issues bring up two possible additional features for DocumentExecutor:

1. Allow custom Executors for portions of a Document

We could potentially use the existing ResolveFieldContext and look at the AST for sibling nodes and generate a single SQL query and then memoize/cache that retrieved object in a new state dictionary object (that we'd add to the context.). If we did this, we'd have to effectively disable parallelism until the first field resolved.

Alternatively, we could have something like a special object-level resolver that gets access to all the fields and could then generate a single query for just the columns we care about. This would also allow more macro-level optimizations (like the JOIN based one mentioned above).

Perhaps we could learn something from IQueryProvider.Execute if we went down this route.

2. Support a simple breadth-first search Executor that keeps a list of foreign keys/references at each "level"

This would allow the executor to work in "levels" of the GraphQL AST and keep track of Ids/references from one level to the next. This would allow the final explicit optimization I outlined above (of collecting Post.AuthorId(s) that will be used when fetching Author). While not optimal for SQL, it eliminates the N+1 issue and is still useful when queries span backend storage implementations (e.g. a mixture of SQL Server and Azure Table Storage)

According to this comment by GraphQL co-creator Dan Schafer ( @dschafer ), this is roughly how Facebook's PHP implementation works. It coalesces queries by "steps"/levels and then demultiplexes the result.

Is anyone else thinking about these optimization issues with respect to GraphQL.NET? I'd be interested in your thoughts.

NonNullGraphType<EnumType<MyEnum>> fails introspection querys

I am not sure if this is a conventions issue or a GraphQL issue, but I think it is a GraphQL issue.

Adding the attached test NonNullEnumGraphTypeTests.cs to the conventions test project illustrates the problem.

I have my own version of the EnumType<>-class which works outside the context of conventions
EnumType.cs. That version also causes the test to fail.

The ExecutionResult.Errors contains 2 errors:
"Error trying to resolve kind."
"Error trying to resolve name."

During execution of the retrospection query, somehow ends up calling GraphQL.Introspection.__Type.KindForType(Type type) with the type-object of the enum (in the example, 'Tests.Reflection.Wrappers.NonNullEnumGraphTypeTests+Foo') which ends up in

  throw new ExecutionError(StringExtensions.ToFormat("Unkown kind of type: {0}", (object) type));

I have attempted to understand why, but so fare have not been able to get the big picture...

The test is a highly modified version of NonNullGraphTypeTests and from that I know that it seems to be only the introspection query that fails - querying data works fine.

Passing datetime as argument - Antlr fails - Help

Hi.
I am trying to pass a number of arguments to a Field.
Example below:

public ArticleSearchQuery()
        {
            Name = "Query";

            Field<ListGraphType<ArticleType>>("articles",
                arguments: new QueryArguments(
                    new[]
                    {
                        new QueryArgument<NonNullGraphType<StringGraphType>>
                        {
                            Name = "iql",
                            Description = "The Iql Query",
                            DefaultValue = string.Empty
                        },
                        new QueryArgument<NonNullGraphType<StringGraphType>>
                        {
                            Name = "startIndex",
                            Description = "The Index to start paging from",
                            DefaultValue = 0
                        },
                        new QueryArgument<NonNullGraphType<StringGraphType>>
                        {
                            Name = "pageSize",
                            Description = "The size of the page you want to received",
                            DefaultValue = "10"
                        },
                        new QueryArgument<NonNullGraphType<StringGraphType>>
                        {
                            Name = "searchFrom",
                            Description = "The datetime to search from. Format",
                            DefaultValue = DateTime.Now.ToString(CultureInfo.InvariantCulture)
                        },
                        new QueryArgument<NonNullGraphType<StringGraphType>>
                        {
                            Name = "searchTo",
                            Description = "The Index to start paging from",
                            DefaultValue = DateTime.Now.ToString(CultureInfo.InvariantCulture)
                        }
                    }),
                resolve:
                    context =>
                        Search((string) context.Arguments["iql"], int.Parse((string) context.Arguments["startIndex"]),
                            int.Parse((string) context.Arguments["pageSize"]),
                            (string) context.Arguments["searchFrom"],
                            (string) context.Arguments["searchTo"]));

Since i have mixed types i have chosen to use the StringGraphType and then manually convert the arguments when received.

I do however have a multitude of issues getting datetimes to be passed as a string argument.

I have tried variations:

Request via fiddler

{
  "Query": "{articles(iql:hund, startIndex:0, pageSize:12, searchFrom:2013-04-05T23, searchTo:2013-04-05T23){name}}"
}

Here Antlr fails with a Reference not set to an object
I have tried wrapping in quotes but then the queryobject is null.

I have tried:

{
  "Query": "{articles(iql:hund, startIndex:0, pageSize:12, searchFrom:2013-04-05T23:52:05.568Z, searchTo:2014-04-05T23:52:05.568Z){name}}"
}

And yet again Antrl fails.

Bottomline.
How can i pass a string representing a DateTime that will allow Antlr to parse it correct?

Exception StackTrace for brevity

   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitArgument(ArgumentContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 215
   at GraphQL.Parsing.GraphQLParser.ArgumentContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 777
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitArguments(ArgumentsContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 205
   at GraphQL.Parsing.GraphQLParser.ArgumentsContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 716
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitField(FieldContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 326
   at GraphQL.Parsing.GraphQLParser.FieldContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 537
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitSelection(SelectionContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 361
   at GraphQL.Parsing.GraphQLParser.SelectionContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 464
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitSelectionSet(SelectionSetContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 348
   at GraphQL.Parsing.GraphQLParser.SelectionSetContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 349
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.VisitOperationDefinition(OperationDefinitionContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 385
   at GraphQL.Parsing.GraphQLParser.OperationDefinitionContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 268
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.VisitChildren(IRuleNode node)
   at GraphQL.Parsing.GraphQLBaseVisitor`1.VisitDefinition(DefinitionContext context) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLBaseVisitor.cs:line 55
   at GraphQL.Parsing.GraphQLParser.DefinitionContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 198
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Language.GraphQLVisitor.<>c__DisplayClass24_0.<VisitDocument>b__0(DefinitionContext childContext) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 411
   at GraphQL.EnumerableExtensions.Apply[T](IEnumerable`1 items, Action`1 action) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\EnumerableExtensions.cs:line 19
   at GraphQL.Language.GraphQLVisitor.VisitDocument(DocumentContext context) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Language\GraphQLVisitor.cs:line 409
   at GraphQL.Parsing.GraphQLParser.DocumentContext.Accept[TResult](IParseTreeVisitor`1 visitor) in E:\tbl\Programming\GitHub\graphql-dotnet\src\GraphQL\obj\Debug\GraphQLParser.cs:line 137
   at Antlr4.Runtime.Tree.AbstractParseTreeVisitor`1.Visit(IParseTree tree)
   at GraphQL.Execution.AntlrDocumentBuilder.Build(String data) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Execution\AntlrDocumentBuilder.cs:line 21
   at GraphQL.DocumentExecuter.Execute(Schema schema, Object root, String query, String operationName, Inputs inputs) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\GraphQL\Execution\DocumentExecuter.cs:line 38
   at Infomedia.PublishingServices.MediaResearch.Api.Controllers.GraphQl.ArticleSearchGraphQlController.Execute(Schema schema, Object rootObject, String query, String operationName, Inputs inputs) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\PS.MediaResearch.Api\Controllers\GraphQl\ArticleSearchGraphQlController.cs:line 38
   at Infomedia.PublishingServices.MediaResearch.Api.Controllers.GraphQl.ArticleSearchGraphQlController.Post(GraphQLQuery query) in E:\tbl\gitlab\infomedia-publishingservices-mediaresearch\PS.MediaResearch.Api\Controllers\GraphQl\ArticleSearchGraphQlController.cs:line 27
   at lambda_method(Closure , Object , Object[] )
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.AuthenticationFilterResult.<ExecuteAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Owin.PassiveAuthenticationMessageHandler.<SendAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()

Enum is not working as input value in mutation operation

Hi,

I am trying to do simple mutation operation for a domain object. Where trying to get input value which is having Enum. At the time of resolving it is showing null value.

e.g. To give an example, For user creation

mutation createUser {
  createUser(userInput:{
    profileImage:"myimage.png",
    gender:Female
  }){
    id
    gender
  }
}

But at the server side the argument value is

{"profileImage":"myimage.png","gender":null}

Type system for user is

  public class UserInputType : InputObjectGraphType
    {
        public UserInputType() {
            Name = "UserInput";
            Description = "User information for user creation";                           
            Field<StringGraphType>("profileImage", "profileImage of user.");        
            Field<GenderEnum>("gender", "user gender.");
        }
    }

public class GenderEnum : EnumerationGraphType
    {
        public GenderEnum()
        {
            Name = "Gender";
            Description = "User gender";
            AddValue("NotSpecified", "NotSpecified gender.", 0);
            AddValue("Male", "gender Male", 1);
            AddValue("Female", "gender female", 2);
        }
    }

 public class MutationRoot: ObjectGraphType
    {

        public MutationRoot() {
            Name = "MutationRoot";
            Description = "GraphQL MutationRoot for supporting create, update, delete or peroform custom actions";

            Field<UserType>("createUser", "create user api",
                arguments: new QueryArguments(
                 new QueryArgument[]{
                        new QueryArgument<NonNullGraphType<UserInputType>> { Name = "userInput", Description = "user info details"}
                }
            ),
            resolve: UserDelegate.CreateUser);
        }
    }

One more thing is observed, when I pass the userInput value as variable the argument value is null.

 string output = JsonConvert.SerializeObject(context.Arguments["userInput"]);

Any help would be appreciated.

Thanks & Regards,
-Vinayak

Convention-based approach leveraging .NET's type system

This project looks interesting!

What do you think about supporting a convention-based approach mapping Plain Old CLR Objects (with optional attribute annotations) using .NET's existing type system where possible?

I'm just starting to look into this approach, but I thought I'd get your feedback early before getting too deep into a possible implementation.

Here's a rough sketch of the idea:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace GraphQLPlayground {
    class Program {
        static void Main(string[] args) {
            var repository = new StarWarsRepository();
            var endpoint = new GraphQLWrapper<StarWarsSchema>(new StarWarsSchema(repository));
            var query = endpoint.ExecuteAsync(@"query HeroNameAndFriendsQuery {
                  hero {
                    id
                    name
                    friends {
                      name
                    }
                  }
                }");
        }
    }

    public class GraphQLWrapper<TSchema> {
        // TODO: can use reflection to recursively examine the TSchema to generate an internal representation if needed

        public GraphQLWrapper(TSchema schema) {
        }

        public async Task<ExecutionResult> ExecuteAsync(string query) {
            throw new NotImplementedException();
        }
    }

    public class StarWarsSchema {
        private readonly StarWarsRepository _Repository;

        public StarWarsSchema(StarWarsRepository repository) {
            _Repository = repository;
        }

        // Although we follow .NET's convention of PascalCasing, these names 
        // can get automatically converted to lowerCamelCase in a similiar
        // mechanism as JSON.NET's CamelCasePropertyNamesContractResolver:
        public async Task<StarWarsCharacter> Hero() {
            return await _Repository.GetDroidAsync(3);
        }

        // Function arguments would get automatically mapped.
        // Note the use of System.ComponentModel.DescriptionAttribute to tag things;
        // this would automatically be extracted by the GraphQLWrapper.
        public async Task<Human> Human([Description("id of the human")] int id) {
            return await _Repository.GetHumanAsync(id);
        }

        public async Task<Droid> Droid([Description("id of the droid")] int id) {
            return await _Repository.GetDroidAsync(id);
        }
    }

    // Abstract classes (and interfaces) can be mapped to a GraphQL interface:
    public abstract class StarWarsCharacter {
        private readonly StarWarsRepository _Repository;
        protected StarWarsCharacter(StarWarsRepository repository) {
            _Repository = repository;
        }

        // Fields can be either simple properties or functions.

        [NotNull]
        [Description("The id of the character.")]
        public int Id { get; set; }

        [Description("The name of the character.")]
        public string Name { get; set; }

        // If the function returns a Task, the executor will execute it asynchronously
        public async Task<IList<StarWarsCharacter>> Friends() {
            return await _Repository.GetFriendsForIdAsync(Id);
        }

        [Description("Which movie they appear in.")]
        public async Task<IList<Episode>> AppearsIn() {
            return await _Repository.GetAppearsInById(Id);
        }
    }

    public class Human : StarWarsCharacter {
        public Human(StarWarsRepository repository) : base(repository) {
        }

        [Description("The home planet of the human.")]
        public string HomePlanet { get; set; }
    }

    public class Droid : StarWarsCharacter {
        public Droid(StarWarsRepository repository) : base(repository) {
        }

        [Description("The primary function of the droid.")]
        public string PrimaryFunction { get; set; }
    }

    [Description("One of the films in the Star Wars Trilogy.")]
    public enum Episode {
        // These could automatically get converted to SCREAMING_CAPS in the generated schema if needed:

        [Description("Released in 1977.")]
        NewHope = 4,

        [Description("Released in 1980.")]
        Empire = 5,

        [Description("Released in 1983.")]
        Jedi = 6
    }

    // Likely defined elsewhere:

    public class StarWarsRepository {
        private readonly JsonSerializerSettings _JsonSerializerSettings = new JsonSerializerSettings {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };

        public async Task<Human> GetHumanAsync(int id) {
            return await GetCharacterFromServerAsync<Human>("/humans/" + id);
        }

        public async Task<Droid> GetDroidAsync(int id) {
            return await GetCharacterFromServerAsync<Droid>("/droids/" + id);
        }

        public async Task<IList<StarWarsCharacter>> GetFriendsForIdAsync(int id) {
            // TODO: implement real logic
            return new[] { await GetDroidAsync(3) };
        }

        public async Task<IList<Episode>> GetAppearsInById(int id) {
            // TODO: implement real logic
            await Task.Delay(200);
            return new[] {
                Episode.Empire,
                Episode.Jedi,
                Episode.NewHope
            };
        }

        private async Task<T> GetCharacterFromServerAsync<T>(string relativePath) where T : StarWarsCharacter {
            // TODO: implement real logic
            await Task.Delay(200);
            var json = @"{
                id: 3,
                name: ""R2-D2"",                
                primaryFunction: ""Astromech""
            }";
            var result = new Droid(this);
            JsonConvert.PopulateObject(json, result, _JsonSerializerSettings);
            return result as T;
        }
    }

    public class ExecutionResult {
        // TODO
    }

    public class NotNullAttribute : Attribute {
        // TODO
    }
}

Passing variables to executor.

So this is a question as to how you pass variables.

Following the demo in the repo i have the following query object:

 public class GraphQlQuery
    {
        public string Query { get; set; }
        public string Variables { get; set; }
    }

Processing of the query:

        public async Task<IHttpActionResult> Query(GraphQlQuery query)
        {
            var input = new Inputs();
            var responseModel = await Execute(new MediaSchema(), null, query.Query, inputs: input);
            return Ok(responseModel);
        }

        private async Task<ExecutionResult> Execute(
            Schema schema,
            object rootObject,
            string query,
            string operationName = null,
            Inputs inputs = null)
        {
            var executer = new DocumentExecuter();

            return await executer.ExecuteAsync(schema, rootObject, query, operationName, inputs);
        }

And this is the actual Query from the GrahiQl IDE

query getSomeMedia($page:Int!){
  allMedia(pageSize:$page, starts:"P"){
    ...mediaInfo,
    articles(amount:2){
      articleId
    }
  }
}

fragment mediaInfo on Media{
  id
  name
  mediaType
  customerSpecific
}

VARIABLES
{
   "page":10
}

So how do i pass the variables on to the executor?

I have tried a number of variations to get the system to handle the variables, and also looked into the source and tests as to how to add the variables to the executor, but in vain.
Help is very much appreciated.

And it is an awesome work you are doing here. Kudos 👍

Does InputObjectGraphType support the polymorphism ?

Hi,

Like ObjectGraphType supports the polymorphism as shown below,

public class NamedType : InterfaceGraphType
{
    public NamedType()
    {
        Name = "Named";

        Field<StringGraphType>("name");
    }
}

public class DogType : ObjectGraphType
{
    public DogType()
    {
        Name = "Dog";

        Field<StringGraphType>("name");
        Field<BooleanGraphType>("barks");

        Interface<NamedType>();

        IsTypeOf = value => value is Dog;
    }
}

can we use the same thing for InputObjectGraphType ?

Thanks & Regards,
-Vinayak

Organizing Queries

Hello. I'm new to graphql and thank you very much for implementing it for .net.

How can I separate HumanQuery and DroidQuery to individual classes and compose them under StarWarsQuery?

(taken from StarWarsQuery.cs)

public class StarWarsQuery : ObjectGraphType
    {
        public StarWarsQuery(StarWarsData data)
        {
            Name = "Query";

            Field<CharacterInterface>("hero", resolve: context => data.GetDroidByIdAsync("3"));
            Field<HumanType>(
                "human",
                arguments: new QueryArguments(
                    new []
                    {
                        new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the human" }
                    }),
                resolve: context => data.GetHumanByIdAsync((string)context.Arguments["id"])
            );
            Field<DroidType>(
                "droid",
                arguments: new QueryArguments(
                    new []
                    {
                        new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "id", Description = "id of the droid" }
                    }),
                resolve: context => data.GetDroidByIdAsync((string)context.Arguments["id"])
            );
        }
    }

The Errors section should not be present in the response if there were no errors.

Currently, even if no errors occurred during the execution of a query, the "errors" property of the response is populated with an empty array. This is a violation of the GraphQL specification:

7.2 Response Format

[...]

If the operation encountered any errors, the response map must contain an entry with key errors. The value of this entry is described in the “Errors” section. If the operation completed without encountering any errors, this entry must not be present.

[...]

The current response does not work with the latest version of Relay, as it assumes that if the "errors" property is present (even if it's just an empty array) the query has failed with an error.

Dependency injection?

I'm really excited that this project exists, and I'm hoping to get an opportunity to make use of it - and thereby also make time to help - at some point. For now, though, I'm just toying around to see where I can get with it :)

I've noticed a lack of DI in the examples - var data = new StarwarsData() is used in a bunch of places.
What I'd like to accomplish, is basically to send dependencies (like data stores) as constructor arguments to the various types that need them for resolving things, e.g.

    public class StarWarsQuery : ObjectGraphType
    {
        public StarWarsQuery(IDataReader data)
        {
            Name = "Query";
            Field<CharacterInterface>("hero", resolve: context => data.GetDroid(id: 3));
        }
    }

And, of course, the data parameter here could be just passed-through to wherever it's needed for resolving more stuff.

Are there any plans for DI support in the future? Perhaps it's already possible, but not shown off in the examples?

building the solution is problematic

Checking out the code it is not possible to build the solution.
I have attempted the latest commit as well as tag 0.3.0

It requires the files in the namespace GraphQL.Parsing, which is missing in the codebase.
I guess it is a product of ANTLR4 building the graphql DSL definition. => GraphQl.g4

I suggest that you check in the required files in GraphQl.Parsing or as a minimum provide a short description of how to manually build the dependency.

I am trying to debug the code to understand why i get an error from the executor so it would be nice to simply just being able to either build using VS or running a script that handles the specialized parts of the build.

Otherwise an awesome job you are doing here.

Regards.

Typed Arguments

Hi.

I'm trying to understand how to use GraphQL.Types.ResolveFieldContext.Arguments.

            Field<UserPayloadType>(
                "UpdateUser",
                arguments: new QueryArguments(
                    new List<QueryArgument>
                    {
                        new QueryArgument<UserInputType> { Name = "input" }
                    }),
                resolve: context =>
                {
                   Dictionary<string, object> input = (context.Arguments["input"] 
                                                       as Dictionary<string, object>);
                   string id = (string)input["id"];
                   ...

Arguments as a dictionary work ok when its simple types like id, but I have a complex input type

    public class SomeInputType : InputObjectGraphType
    {
        public SomeInputType(Entities db, TheDataQuery query)
        {
            Name = "SomeInput";
            Field<StringGraphType>("id");
            Field<StringGraphType>("Name");
            Field<StringGraphType>("clientMutationId");
            Field<ListGraphType<SomeOtherInputType>>("Others");
        }
    }

Is there any built in way to get an instance of the actual argument type ?
At the moment I have to do something like this:

(((input["Others"] as Object[])[0] as object) as Dictionary<string, object>)["id"]

Thanks!

Redesign: Register Field Type vs. Instance of Type

Now that I understand GraphQL a lot better, one of the flaws of the API I created is the registration of an instance of the field Type vs. just the Type itself. This has lead to issues with completing the introspection feature.

To fix this I think all field registrations will change to something like:

Field<StringGraphType>("name", "The name of the person.");

This syntax is currently supported, though it immediately creates an instance of the given type. This will be changed to just capture the type and the instance of the type will be created later.

There really only ever needs to be a single instance of each defined type. At runtime a type will be requested and it will check a cache to see if an instance of that type has been created yet. This is sort of already being done within the Schema class. The difference will be the types will be created here vs. just collecting them into a lookup. I will design this so that if you want to resolve the type through an IOC container you can.

Guid String Argument Parsing Errors

String argument parsing appears to terminate upon transition from a alpha character to a numeric character, or accept only 1 character if the first is numeric. Weird thing going on.

Field<ServiceWorkDayQ>(
    "removeEmployeeFromWorkDay",
    arguments: new QueryArguments(new QueryArgument[] {
        new QueryArgument<IntGraphType> { 
            Name = "workDayId" 
        },
        new QueryArgument<IntGraphType> { 
            Name = "employeeId" 
        },
        new QueryArgument<StringGraphType> { 
            Name = "employeeType" 
        },
        new QueryArgument<StringGraphType> { 
            Name = "workDayEmployeeId" 
        }
    }),
    resolve: context =>
    {
        ...
    }
);

Query:

mutation { Graph { removeEmployeeFromWorkDay(workDayEmployeeId: 00000000-0000-0000-0000-000000000000, workDayId: 10409737, employeeId: 34081, employeeType: Primary) { WorkDayId, WorkDayEmployees { RowId, } } } }

Parsed: [3] = {[workDayEmployeeId, 0]}


Query:

mutation { Graph { removeEmployeeFromWorkDay(workDayEmployeeId: de276d1d-c1aa-4e32-bec7-5a4749843a76, workDayId: 10409737, employeeId: 14480, employeeType: Secondary) { WorkDayId, WorkDayEmployees { RowId, } } } }

Parsed: {[workDayEmployeeId, de276d1d]}


Query:

mutation { Graph { removeEmployeeFromWorkDay(workDayEmployeeId: 96471b08-0e3c-427f-a6bc-289a18a34ff0, workDayId: 10409737, employeeId: 34097, employeeType: Assistant) { WorkDayId, WorkDayEmployees { RowId, } } } }

Parsed: {[workDayEmployeeId, 96471]}

Schema types not directly exposed in Query / Mutation

I have a type which is only exposed in the schema via it's interface (in fact, the relay node interface). This means that I need to expose (register) the type in the schema in some other way. I can not seem to figure out how/when/where.

My first attempt was to call FindType(typeof(MyGraphType)) in the constructor of my schema. This however caused part of my schema to go missing (everything added to Query/Mutation AFTER this line of the constructor) since FindType triggers the only-once-GraphTypeLookup.Create-call added as the solution to issue 67. (a way to issue an error if Query/Mutation objects are modified AFTER GraphTypeLookup has been created would be nice - but definitely not straight forward).

My workaround now is to just expose a dummy field of the type I would like exposed somewhere in my schema.

I guess I could ensure that extra types are added AFTER all fields have been added to the Query/Mutation fields - to me this sound more like a hack - but it might work (while being a bit inconvenient in my case due to some IoC-plugin-architecture).

How should I do this the right way?

Question about Generating Entire Schema

It's pretty awesome what you've done.Thanks!!! It could be used as an ORM extension of some sort that'd create a graph over underline database model while correctly maintaining foreign key relationships.

I was looking into GraphQL.Tests project trying to find a way how to build an entire schema that could be used to pass to babel-relay-plugin for validation when compiling the source - but haven't figured out that part yet.

Any idea?

Backend implementation

I'm trying to find out how I can create a custom graphql-dotnet backend, but there is no mention of it as far as I can see? Any pointers on where I can look?

GraphType.Connection<TGraphType> with new() constraint

Hi,

GraphType.Connection() has a new() constraint on TGraphType which doesn't seem to be needed, and is causing me some headache! I would be happy to make an attempt of creating a pull request - but with the amount of refactoring and extension going on, I though it might better be done in some other context.

        public ConnectionBuilder<TGraphType, object> Connection<TGraphType>()
            where TGraphType : ObjectGraphType, new()

https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL/Types/GraphType.cs#L34

https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL/Builders/ConnectionBuilder.cs#L10

GraphTypesLookup._types modified while enumerating.

From time to time I get the exception bellow. I am not sure exactly why this happen, but it looks like a concurrency issue as the code in the indexer only iterates the collection without modifying it.

The easy fix would be to replace the _types Dictionary<,> with a ConcurrentDictionary<,>, but as I do not fully understand the problem, I am not sure that is the right solution.

It could obviously be my code that does something wrong - but from a stacktrace with only System and GraphQL namespaces present and with the high degree of thread-awareness in GraphQL, I have a hard time figuring out where to start search for the problem in our code base.

  • Is it the intention that the _field dictionary should be fully populated before used?
  • Should each thread accessing the GraphTypeLookup object have it's own instance?
  • Should it have been guarded with semaphors/be concurrent?
  • Or do I miss something obvious?
{
  "message": "An error has occurred.", 
  "exceptionMessage": "Collection was modified; enumeration operation may not execute.",
  "exceptionType": "System.InvalidOperationException",
  "stackTrace": "
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at GraphQL.Types.GraphTypesLookup.get_Item(Type type) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 64
   at GraphQL.Types.GraphTypesLookup.AddTypeIfNotRegistered(Type type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 182
   at GraphQL.Types.GraphTypesLookup.<>c__DisplayClass12_0.<AddType>b__0(FieldType field) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 116
   at GraphQL.EnumerableExtensions.Apply[T](IEnumerable`1 items, Action`1 action) in C:\Home\projects\graphql-dotnet\src\GraphQL\EnumerableExtensions.cs:line 29
   at GraphQL.Types.GraphTypesLookup.AddType(GraphType type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 127
   at GraphQL.Types.Schema.<EnsureLookup>b__24_0(String name, GraphType graphType, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\Schema.cs:line 116
   at GraphQL.Types.ListGraphType.CollectTypes(TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\ListGraphType.cs:line 28
   at GraphQL.Types.GraphTypesLookup.AddType(GraphType type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 111
   at GraphQL.Types.GraphTypesLookup.AddTypeIfNotRegistered(Type type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 187
   at GraphQL.Types.GraphTypesLookup.<>c__DisplayClass12_0.<AddType>b__0(FieldType field) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 116
   at GraphQL.EnumerableExtensions.Apply[T](IEnumerable`1 items, Action`1 action) in C:\Home\projects\graphql-dotnet\src\GraphQL\EnumerableExtensions.cs:line 29
   at GraphQL.Types.GraphTypesLookup.AddType(GraphType type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 127
   at GraphQL.Types.GraphTypesLookup.AddTypeIfNotRegistered(Type type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 187
   at GraphQL.Types.GraphTypesLookup.<>c__DisplayClass12_0.<AddType>b__0(FieldType field) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 116
   at GraphQL.EnumerableExtensions.Apply[T](IEnumerable`1 items, Action`1 action) in C:\Home\projects\graphql-dotnet\src\GraphQL\EnumerableExtensions.cs:line 29
   at GraphQL.Types.GraphTypesLookup.AddType(GraphType type, TypeCollectionContext context) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\GraphTypesLookup.cs:line 127
   at GraphQL.Types.Schema.EnsureLookup() in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\Schema.cs:line 119
   at GraphQL.Types.Schema.FindType(Type type) in C:\Home\projects\graphql-dotnet\src\GraphQL\Types\Schema.cs:line 78
   at GraphQL.DocumentExecuter.<>c__DisplayClass11_0.<GetArgumentValues>b__0(Dictionary`2 acc, QueryArgument arg) in C:\Home\projects\graphql-dotnet\src\GraphQL\Execution\DocumentExecuter.cs:line 307
   at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
   at GraphQL.DocumentExecuter.<ResolveField>d__8.MoveNext() in C:\Home\projects\graphql-dotnet\src\GraphQL\Execution\DocumentExecuter.cs:line 150
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.EnumerableExtensions.<>c__DisplayClass3_0`4.<<ToDictionaryAsync>b__0>d.MoveNext() in :line 0
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.EnumerableExtensions.<ToDictionaryAsync>d__3`4.MoveNext() in :line 0
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.DocumentExecuter.<ExecuteFields>d__7.MoveNext() in :line 0
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at GraphQL.DocumentExecuter.<ExecuteAsync>d__4.MoveNext() in C:\Home\projects\graphql-dotnet\src\GraphQL\Execution\DocumentExecuter.cs:line 67
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__3`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}

Relay & NonNull-arguments in variables

I have a schema with a root query of Foo[MyEnum]. This works fine when I make queries using GraphiQL. However, when Relay makes queries, it makes the arguments into variables and relay specifies these variables of type MyEnum! (non-null enum). Since I nowhere i my schema have used NonNullGraphType<EnumType<MyEnum>>, the schema lookup method is not able to map the string MyEnum! to a GraphType. If I manually registers this type or makes a dummy field somewhere with this type, everything is fine - but otherwise the query fails with:

Variable '$arg_0' expected value of type 'MyEnum!'
 at GraphQL.DocumentExecuter.GetVariableValue(ISchema schema, Variable variable, Object input) in C:\Home\projects\graphql-dotnet\src\GraphQL\Execution\DocumentExecuter.cs:line 388

While one could argue that Relay should not have specified a NonNullType, it seems that other GraphQL implementations allows this, so having relay fix the problem may not be the right solution.

I am not sure what right fix for GraphQL-dotnet is. A simple one could be to register all types with and without NonNull-type in the string-to-GraphType-dictionary (like what is done for simple scalar types already). An alternativ could be to make the lookup logic understand trailing !.

ConnectionBuilder<,>.Argument<TArgumentGraphType> without constraint.

The ConnectionBuilder<,>.Argument does not have a GraphType constraint. Specifying something not a GraphType causes a cast exception which I found to be fairly hard to track back to the source - adding a constraint would be very nice - And I can't come up with a situation where it makes sense to use it with a non-GraphType type argument.

Parsing queries from Relay

Hi
POSTS from Relay come in the form of json i.e wrapped like this:

{"query":"query xxxx","variables":{}}
e.g {"query":"query RouteQuery { viewer { routes{ createdOn, machine } } }","variables":{}}

In order for this to work with graphql.net you need to remove the starting {"query":" and ending ","variables":{}} and pass that as the query.

I have tried parsing the whole thing with a NewtonSoft.JObject but it fails, which has left me with the only option of using string.Replace to remove them. Is there a better way anyone knows of that will do this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.