0

Im currently trying to call a field on graphql-query from code, without using the http layer. In a test case I had success using this snippet inside of a field resolver. The breakpoint hits.

var newContext = new ResolveFieldContext(context);
var query = context.ParentType;
var ticketQueryField = query.GetField("getTickets");
await (Task) ticketQueryField.Resolver.Resolve(context);

So I think its possible to fill the copied ResolveFieldContext with my real needed fields/arguments and call it like this. But its very ... complicated to fill the ResolveFieldContext by hand. So maybe there is a easier way to create the context. Like:

var newContext = new ResolveFieldContext("query test { getTickets(id: 1) { number, title } }");

That would be really awesome and in my real scenario there a more then just field which I want to access with the generated query.

Why I want to use the Graph like this? The Batch-Loader which we are using inside the GraphQL-Types are perfect for our needs.

1 Answer 1

1

You can execute a GraphQL query without http by using the DocumentExecutor directly, and providing your own DocumentWriter if you want the data in a specific format. There is an extension method which returns JSON, but you can write your own.

https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.NewtonsoftJson/DocumentWriter.cs

This is an example test base class for testing queries: https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.Tests/BasicQueryTestBase.cs

This is a console example that returns JSON, not using http.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Authorization;
using GraphQL.SystemTextJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.Extensions.DependencyInjection;

namespace BasicSample
{
    internal class Program
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "main")]
        private static async Task Main()
        {
            using var serviceProvider = new ServiceCollection()
                .AddSingleton<IAuthorizationEvaluator, AuthorizationEvaluator>()
                .AddTransient<IValidationRule, AuthorizationValidationRule>()
                .AddTransient(s =>
                {
                    var authSettings = new AuthorizationSettings();
                    authSettings.AddPolicy("AdminPolicy", p => p.RequireClaim("role", "Admin"));
                    return authSettings;
                })
                .BuildServiceProvider();

            string definitions = @"
                type User {
                    id: ID
                    name: String
                }

                type Query {
                    viewer: User
                    users: [User]
                }
            ";
            var schema = Schema.For(definitions, builder => builder.Types.Include<Query>());

            // remove claims to see the failure
            var authorizedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("role", "Admin") }));

            string json = await schema.ExecuteAsync(_ =>
            {
                _.Query = "{ viewer { id name } }";
                _.ValidationRules = serviceProvider
                    .GetServices<IValidationRule>()
                    .Concat(DocumentValidator.CoreRules);
                _.RequestServices = serviceProvider;
                _.UserContext = new GraphQLUserContext { User = authorizedUser };
            });

            Console.WriteLine(json);
        }
    }

    /// <summary>
    /// Custom context class that implements <see cref="IProvideClaimsPrincipal"/>.
    /// </summary>
    public class GraphQLUserContext : Dictionary<string, object>, IProvideClaimsPrincipal
    {
        /// <inheritdoc />
        public ClaimsPrincipal User { get; set; }
    }

    /// <summary>
    /// CLR type to map to the 'Query' graph type.
    /// </summary>
    public class Query
    {
        /// <summary>
        /// Resolver for 'Query.viewer' field.
        /// </summary>
        [GraphQLAuthorize("AdminPolicy")]
        public User Viewer() => new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" };

        /// <summary>
        /// Resolver for 'Query.users' field.
        /// </summary>
        public List<User> Users() => new List<User> { new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" } };
    }

    /// <summary>
    /// CLR type to map to the 'User' graph type.
    /// </summary>
    public class User
    {
        /// <summary>
        /// Resolver for 'User.id' field. Just a simple property.
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// Resolver for 'User.name' field. Just a simple property.
        /// </summary>
        public string Name { get; set; }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This works just perfect! Thank you very very much! When I use the ExecutionResult I also get the strong typed objects which I can use in my UnionType. 10/10!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.