graphql-platform icon indicating copy to clipboard operation
graphql-platform copied to clipboard

Primitive types such as UINT do not work with [UseFiltering] and [UseSorting] attributes

Open naymore opened this issue 1 year ago • 4 comments

Product

Hot Chocolate

Is your feature request related to a problem?

The type of the member CountUsed of the declaring type Shape is unknown (HotChocolate.Data.Filters.FilterInputType<MinimalGraphql.Entities.Shape>)

It does not work. Not even with the extra Nuget package for additional scalar types. And explicit Type Mapping.

// part of an EF entity
[GraphQLType(typeof(UnsignedIntType))]
public uint CountUsed { get; private set; }

Also adding an explicit type mapping doesn't help. Same error message.

builder.Services.AddGraphQLServer()
                                  .BindRuntimeType<uint, UnsignedIntType>()
                                  .InitializeOnStartup();

also doesn't help:

builder.Services.AddGraphQLServer()
                                  .AddType<UnsignedIntType>()
                                  .BindRuntimeType<uint, UnsignedIntType>()
                                  .InitializeOnStartup();

I'm absolutely puzzled. Can you shed some light on this and maybe update the docs?

The solution you'd like

Clear documentation on how to actually use these supported types.

naymore avatar Jun 27 '24 10:06 naymore

This type is not supported in filtering probably. @PascalSenn can this be mapped? should be just another comparable right?

michaelstaib avatar Jun 27 '24 22:06 michaelstaib

See:

  • https://github.com/ChilliCream/graphql-platform/issues/5262#issuecomment-1415967275
  • https://github.com/ChilliCream/graphql-platform/issues/5448#issuecomment-1415966688
  • https://github.com/ChilliCream/graphql-platform/issues/5923#issuecomment-1455065387

glen-84 avatar Jun 28 '24 18:06 glen-84

Program.cs

using MinimalGraphql.EntityFramework;
using Microsoft.EntityFrameworkCore;
using MinimalGraphql.Queries;

var builder = WebApplication.CreateBuilder(args);

string? connectionString = builder.Configuration.GetConnectionString("shapesdb");
builder.Services.AddDbContextFactory<ShapesDbContext>(options => options.UseSqlServer(connectionString));

builder.Services.AddGraphQLServer()
                    .BindRuntimeType<uint, UnsignedIntType>() // in some linked issue I read this should work
                    //.AddType<UnsignedIntType>() // not so sure about this one (see [#4305][(url](https://github.com/ChilliCream/graphql-platform/issues/4305))
                    .AddQueryType<Query>()
                    .AddFiltering()
                    .AddSorting()
                    .InitializeOnStartup();

WebApplication app = builder.Build();

using (IServiceScope scope = app.Services.CreateAsyncScope())
{
    await using ShapesDbContext dbContext = scope.ServiceProvider.GetRequiredService<ShapesDbContext>();
    await dbContext.Database.EnsureCreatedAsync();
    await dbContext.Database.MigrateAsync();
}

app.UseHttpsRedirection();
app.MapGraphQL();
app.Run();

EF Entity

namespace MinimalGraphql.Entities
{
    public class Shape
    {
        private Shape()
        {
            // NOP
        }

        protected Shape(...)
        {
             // left out for brevity
        }

        // [...]
        [GraphQLType(typeof(UnsignedIntType))]
        public uint CountUsed { get; set; }
        // [...]

        public static Shape Create(...)
        {
            return new Shape(...);
        }
    }
}

GraphQL Query

namespace MinimalGraphql.Queries
{
    public class Query
    {
        [UseDbContext(typeof(ShapesDbContext))]
        [UsePaging]
        [UseFiltering]
        [UseSorting]
        public IQueryable<Shape> GetShapes([Service(ServiceKind.Resolver)] ShapesDbContext dbContext)
        {
            return dbContext.Shapes.AsNoTracking();
        }
    }

...the exception

fail: Microsoft.Extensions.Hosting.Internal.Host[11]
      Hosting failed to start
      HotChocolate.SchemaException: For more details look at the `Errors` property.

      1. For more details look at the `Errors` property.

      1. The type of the member CountUsed of the declaring type Shape is unknown
       (HotChocolate.Data.Filters.FilterInputType<MinimalGraphql.Entities.Shape>)

         at HotChocolate.Configuration.TypeInitializer.DiscoverTypes()
         at HotChocolate.Configuration.TypeInitializer.Initialize()
         at HotChocolate.SchemaBuilder.Setup.InitializeTypes(SchemaBuilder builder, IDescriptorContext context, IReadOnlyList`1 types)
         at HotChocolate.SchemaBuilder.Setup.Create(SchemaBuilder builder, LazySchema lazySchema, IDescriptorContext context)
         at HotChocolate.SchemaBuilder.Create(IDescriptorContext context)
         at HotChocolate.SchemaBuilder.HotChocolate.ISchemaBuilder.Create(IDescriptorContext context)
         at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaAsync(ConfigurationContext context, RequestExecutorSetup setup, RequestExecutorOptions executorOptions, IServiceProvider schemaServices, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
         at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaServicesAsync(ConfigurationContext context, RequestExecutorSetup setup, CancellationToken cancellationToken)
         at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorNoLockAsync(String schemaName, CancellationToken cancellationToken)
         at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorAsync(String schemaName, CancellationToken cancellationToken)
         at HotChocolate.AspNetCore.Warmup.ExecutorWarmupService.ExecuteAsync(CancellationToken stoppingToken)
         at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token)
         at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)

I can confirm it DOES WORK as soon as I ommit the attributes for Filtering and Sorting. But that's exactly why I want to use GraphQL in the first place...

If I understand #5759 correctly you basically have to do the plumbing yourself now and write custom FilterInputTypes?

naymore avatar Jul 05 '24 06:07 naymore

Working code...

builder.Services.AddGraphQLServer()
                    .BindRuntimeType<uint, UnsignedIntType>()
                    .AddQueryType<Query>()
                    .AddFiltering(x => x.AddDefaults().BindRuntimeType<uint, UnsignedIntOperationFilterInputType>()) // <---
                    .AddSorting()
                    .InitializeOnStartup();

custom ComparableOperationFilterInputType

using HotChocolate.Data.Filters;

namespace MinimalGraphql.GraphQLFilters
{
    public class UnsignedIntOperationFilterInputType
     : ComparableOperationFilterInputType<UnsignedIntType>
    {
        protected override void Configure(IFilterInputTypeDescriptor descriptor)
        {
            descriptor.Name("UnsignedIntOperationFilterInputType");
            base.Configure(descriptor);
        }
    }
}

So this doesn't do much except setting a descriptor name. Refering to #5759 I don't really see why I should need to set the descriptor name myself. I couldn't really understand your concerns about it... at which point will I ever see this type name anyway?

It's actually in the docs. I just expected this to work automatically (hence the extra Scalars package).

When you add custom scalars, you will have to create custom filter types. Scalars are mapped to a FilterInputType that defines the operations that are possible for this scalar. The built-in scalars are already mapped to matching filter types. For custom scalars, or scalars from HotChocolate.Types.Scalars, you have to create and bind types.

naymore avatar Jul 05 '24 07:07 naymore