Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup Filtering Typenames #4880

Merged
merged 11 commits into from
Apr 11, 2022
10 changes: 8 additions & 2 deletions src/HotChocolate/Data/src/Data/ErrorHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,24 @@ internal static class ErrorHelper
public static IError CreateNonNullError<T>(
IFilterField field,
IValueNode value,
IFilterVisitorContext<T> context)
IFilterVisitorContext<T> context,
bool isMemberInvalid = false)
{
IFilterInputType filterType = context.Types.OfType<IFilterInputType>().First();

INullabilityNode nullability =
isMemberInvalid && field.Type.IsListType()
? new ListNullabilityNode(null, new RequiredModifierNode(null, null))
: new RequiredModifierNode(null, null);

return ErrorBuilder.New()
.SetMessage(
DataResources.ErrorHelper_CreateNonNullError,
context.Operations.Peek().Name,
filterType.Print())
.AddLocation(value)
.SetCode(ErrorCodes.Data.NonNullError)
.SetExtension("expectedType", new NonNullType(field.Type).Print())
.SetExtension("expectedType", field.Type.RewriteNullability(nullability).Print())
.SetExtension("filterType", filterType.Print())
.Build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,9 @@ public static class HotChocolateDataRequestBuilderExtensions
/// </returns>
public static IRequestExecutorBuilder AddFiltering(
this IRequestExecutorBuilder builder,
string? name = null)
{
builder.Services.AddSingleton<IParameterExpressionBuilder>(
new FilterContextParameterExpressionBuilder());

return builder.ConfigureSchema(s => s.AddFiltering(name));
}
string? name = null,
bool compatabilityMode = false) =>
builder.ConfigureSchema(s => s.AddFiltering(name, compatabilityMode));

/// <summary>
/// Adds filtering support.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,21 @@ public static class FilterConventionDescriptorExtensions
/// <returns>The descriptor that was passed in as a parameter</returns>
public static IFilterConventionDescriptor AddDefaults(
this IFilterConventionDescriptor descriptor) =>
descriptor.AddDefaultOperations().BindDefaultTypes().UseQueryableProvider();
descriptor.AddDefaults(false);

/// <summary>
/// Initializes the default configuration for <see cref="IQueryable"/> and
/// <see cref="IEnumerable{T}"/> on the convention
/// </summary>
/// <param name="descriptor">The descriptor where the handlers are registered</param>
/// <returns>The descriptor that was passed in as a parameter</returns>
public static IFilterConventionDescriptor AddDefaults(
this IFilterConventionDescriptor descriptor,
bool compatabilityMode) =>
descriptor
.AddDefaultOperations()
.BindDefaultTypes(compatabilityMode)
.UseQueryableProvider();

/// <summary>
/// Adds default operations to the descriptor
Expand Down Expand Up @@ -71,36 +85,81 @@ public static class FilterConventionDescriptorExtensions
/// Throws in case the argument <paramref name="descriptor"/> is null
/// </exception>
public static IFilterConventionDescriptor BindDefaultTypes(
this IFilterConventionDescriptor descriptor)
this IFilterConventionDescriptor descriptor,
bool compatabilityMode = false)
{
if (descriptor is null)
{
throw new ArgumentNullException(nameof(descriptor));
}

return descriptor
.BindRuntimeType<string, StringOperationFilterInputType>()
.BindRuntimeType<bool, BooleanOperationFilterInputType>()
.BindRuntimeType<bool?, BooleanOperationFilterInputType>()
.BindComparableType<byte>()
.BindComparableType<short>()
.BindComparableType<int>()
.BindComparableType<long>()
.BindComparableType<float>()
.BindComparableType<double>()
.BindComparableType<decimal>()
.BindComparableType<sbyte>()
.BindComparableType<ushort>()
.BindComparableType<uint>()
.BindComparableType<ulong>()
.BindComparableType<Guid>()
.BindComparableType<DateTime>()
.BindComparableType<DateTimeOffset>()
if (compatabilityMode)
{
return descriptor
.BindRuntimeType<string, StringOperationFilterInputType>()
.BindRuntimeType<bool, BooleanOperationFilterInputType>()
.BindRuntimeType<bool?, BooleanOperationFilterInputType>()
.BindComparableType<byte>()
.BindComparableType<short>()
.BindComparableType<int>()
.BindComparableType<long>()
.BindComparableType<float>()
.BindComparableType<double>()
.BindComparableType<decimal>()
.BindComparableType<sbyte>()
.BindComparableType<ushort>()
.BindComparableType<uint>()
.BindComparableType<ulong>()
.BindComparableType<Guid>()
.BindComparableType<DateTime>()
.BindComparableType<DateTimeOffset>()
#if NET6_0_OR_GREATER
.BindComparableType<DateOnly>()
.BindComparableType<TimeOnly>()
#endif
.BindComparableType<TimeSpan>()
.BindRuntimeType<Uri, ComparableOperationFilterInputType<Uri>>()
.BindRuntimeType<Uri?, ComparableOperationFilterInputType<Uri?>>();
}
else
{
return descriptor
.BindRuntimeType<string, StringOperationFilterInputType>()
.BindRuntimeType<bool, BooleanOperationFilterInputType>()
.BindRuntimeType<bool?, BooleanOperationFilterInputType>()
.BindRuntimeType<byte, ByteOperationFilterInputType>()
.BindRuntimeType<byte?, ByteOperationFilterInputType>()
.BindRuntimeType<sbyte, ByteOperationFilterInputType>()
.BindRuntimeType<sbyte?, ByteOperationFilterInputType>()
.BindRuntimeType<short, ShortOperationFilterInputType>()
.BindRuntimeType<short?, ShortOperationFilterInputType>()
.BindRuntimeType<int, IntOperationFilterInputType>()
.BindRuntimeType<int?, IntOperationFilterInputType>()
.BindRuntimeType<long, LongOperationFilterInputType>()
.BindRuntimeType<long?, LongOperationFilterInputType>()
.BindRuntimeType<float, FloatOperationFilterInputType>()
.BindRuntimeType<float?, FloatOperationFilterInputType>()
.BindRuntimeType<double, FloatOperationFilterInputType>()
.BindRuntimeType<double?, FloatOperationFilterInputType>()
.BindRuntimeType<decimal, DecimalOperationFilterInputType>()
.BindRuntimeType<decimal?, DecimalOperationFilterInputType>()
.BindRuntimeType<Guid, UuidOperationFilterInputType>()
.BindRuntimeType<Guid?, UuidOperationFilterInputType>()
.BindRuntimeType<DateTime, DateTimeOperationFilterInputType>()
.BindRuntimeType<DateTime?, DateTimeOperationFilterInputType>()
.BindRuntimeType<DateTimeOffset, DateTimeOperationFilterInputType>()
.BindRuntimeType<DateTimeOffset?, DateTimeOperationFilterInputType>()
#if NET6_0_OR_GREATER
.BindComparableType<DateOnly>()
.BindComparableType<TimeOnly>()
.BindRuntimeType<DateOnly, DateOperationFilterInputType>()
.BindRuntimeType<DateOnly?, DateOperationFilterInputType>()
.BindRuntimeType<TimeOnly, TimeSpanOperationFilterInputType>()
.BindRuntimeType<TimeOnly?, TimeSpanOperationFilterInputType>()
#endif
.BindComparableType<TimeSpan>();
.BindRuntimeType<TimeSpan, TimeSpanOperationFilterInputType>()
.BindRuntimeType<TimeSpan?, TimeSpanOperationFilterInputType>()
.BindRuntimeType<Uri, UrlOperationFilterInputType>()
.BindRuntimeType<Uri?, UrlOperationFilterInputType>();
}
}

private static IFilterConventionDescriptor BindComparableType<T>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using HotChocolate.Internal;
using HotChocolate.Language;
using HotChocolate.Types;

namespace HotChocolate.Data.Filters.Internal;

/// <summary>
/// Common helpers for 'in' and 'nin' filter operations
/// </summary>
public static class ValueNullabilityHelpers
{
/// <summary>
/// Validates if the provided array is valid
/// </summary>
public static bool IsListValueValid(
IType type,
IExtendedType runtimeType,
IValueNode node)
{
if (type.IsListType() &&
!runtimeType.IsNullable &&
node is ListValueNode values)
{
for (var i = 0; i < values.Items.Count; i++)
{
if (values.Items[i].Kind == SyntaxKind.NullValue)
{
return false;
}
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using System;
using System.Linq.Expressions;
using HotChocolate.Internal;
using HotChocolate.Language;
using HotChocolate.Types;
using HotChocolate.Types.Descriptors;
using HotChocolate.Utilities;

namespace HotChocolate.Data.Filters.Expressions;

public class QueryableComparableInHandler
: QueryableComparableOperationHandler
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Linq.Expressions;
using HotChocolate.Internal;
using HotChocolate.Language;
using HotChocolate.Types;
using HotChocolate.Utilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public abstract class QueryableListOperationHandlerBase
context.GetLevel().Enqueue(expression);
}


action = SyntaxVisitor.Continue;
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using HotChocolate.Data.Filters.Internal;
using HotChocolate.Internal;
using HotChocolate.Language;
using HotChocolate.Types;
Expand Down Expand Up @@ -40,12 +41,33 @@ protected QueryableOperationHandlerBase(InputParser inputParser)
return false;
}

if (!ValueNullabilityHelpers.IsListValueValid(field.Type, runtimeType, node.Value))
{
IError error = ErrorHelper.CreateNonNullError(field, value, context, true);
context.ReportError(error);
result = null!;
return false;
}

result = HandleOperation(context, field, value, parsedValue);
return true;
}

/// <summary>
/// if this value is true, null values are allowed as inputs
/// </summary>
protected bool CanBeNull { get; set; } = true;

/// <summary>
/// Maps a operation field to a provider specific result.
/// This method is called when the <see cref="FilterVisitor{TContext,T}"/> enters a
/// field
/// </summary>
/// <param name="context">The <see cref="IFilterVisitorContext{T}"/> of the visitor</param>
/// <param name="field">The field that is currently being visited</param>
/// <param name="value">The value node of this field</param>
/// <param name="parsedValue">The value of the value node</param>
/// <returns>If <c>true</c> is returned the action is used for further processing</returns>
public abstract Expression HandleOperation(
QueryableFilterContext context,
IFilterOperationField field,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ public static class FilterSchemaBuilderExtensions
/// </returns>
public static ISchemaBuilder AddFiltering(
this ISchemaBuilder builder,
string? name = null) =>
AddFiltering(builder, x => x.AddDefaults(), name);
string? name = null,
bool compatabilityMode = false) =>
AddFiltering(builder, x => x.AddDefaults(compatabilityMode), name);

/// <summary>
/// Adds filtering support.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using HotChocolate.Data.Filters;
using HotChocolate.Types;

namespace HotChocolate.Data;

public class ByteOperationFilterInputType
: ComparableOperationFilterInputType<ByteType>
{
protected override void Configure(IFilterInputTypeDescriptor descriptor)
{
descriptor.Name("ByteOperationFilterInput");
base.Configure(descriptor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,26 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor)
descriptor.Operation(DefaultFilterOperations.NotEquals)
.Type(typeof(T))
.MakeNullable();
descriptor.Operation(DefaultFilterOperations.In)
.Type(typeof(IEnumerable<T>))
.MakeNullable();
descriptor.Operation(DefaultFilterOperations.NotIn)
.Type(typeof(IEnumerable<T>))
.MakeNullable();

if (typeof(IType).IsAssignableFrom(typeof(T)))
{
descriptor.Operation(DefaultFilterOperations.In)
.Type(typeof(ListType<>).MakeGenericType(typeof(T)))
.MakeNullable();
descriptor.Operation(DefaultFilterOperations.NotIn)
.Type(typeof(ListType<>).MakeGenericType(typeof(T)))
.MakeNullable();
}
else
{
descriptor.Operation(DefaultFilterOperations.In)
.Type(typeof(IEnumerable<T>))
.MakeNullable();
descriptor.Operation(DefaultFilterOperations.NotIn)
.Type(typeof(IEnumerable<T>))
.MakeNullable();
}

descriptor.Operation(DefaultFilterOperations.GreaterThan)
.Type(typeof(T))
.MakeNullable();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using HotChocolate.Data.Filters;
using HotChocolate.Types;

namespace HotChocolate.Data;

public class DateOperationFilterInputType
: ComparableOperationFilterInputType<DateType>
{
protected override void Configure(IFilterInputTypeDescriptor descriptor)
{
descriptor.Name("DateOperationFilterInput");
base.Configure(descriptor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using HotChocolate.Data.Filters;
using HotChocolate.Types;

namespace HotChocolate.Data;

public class DateTimeOperationFilterInputType
: ComparableOperationFilterInputType<DateTimeType>
{
protected override void Configure(IFilterInputTypeDescriptor descriptor)
{
descriptor.Name("DateTimeOperationFilterInput");
base.Configure(descriptor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using HotChocolate.Data.Filters;
using HotChocolate.Types;

namespace HotChocolate.Data;

public class DecimalOperationFilterInputType
: ComparableOperationFilterInputType<DecimalType>
{
protected override void Configure(IFilterInputTypeDescriptor descriptor)
{
descriptor.Name("DecimalOperationFilterInput");
base.Configure(descriptor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using HotChocolate.Data.Filters;
using HotChocolate.Types;

namespace HotChocolate.Data;

public class FloatOperationFilterInputType
: ComparableOperationFilterInputType<FloatType>
{
protected override void Configure(IFilterInputTypeDescriptor descriptor)
{
descriptor.Name("FloatOperationFilterInput");
base.Configure(descriptor);
}
}