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

Descriptor Attributes #1238

Merged
merged 17 commits into from
Dec 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions src/Core/Types.Filters.Tests/QueryableFilterAttributeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System.Collections.Generic;
using HotChocolate.Execution;
using Snapshooter.Xunit;
using Xunit;

namespace HotChocolate.Types.Filters
{
public class QueryableFilterAttributeTests
{
[Fact]
public void Create_Schema_With_FilterType()
{
// arrange
// act
ISchema schema = SchemaBuilder.New()
.AddQueryType<Query1>()
.Create();

// assert
schema.ToString().MatchSnapshot();
}

[Fact]
public void Create_Schema_With_FilterType_With_Fluent_API()
{
// arrange
// act
ISchema schema = SchemaBuilder.New()
.AddQueryType<Query2>()
.Create();

// assert
schema.ToString().MatchSnapshot();
}

public class Query1
{
[UseFiltering]
public IEnumerable<Foo> Foos { get; } = new[]
{
new Foo { Bar = "aa", Baz = 1, Qux = 1 },
new Foo { Bar = "ba", Baz = 1 },
new Foo { Bar = "ca", Baz = 2 },
new Foo { Bar = "ab", Baz = 2 },
new Foo { Bar = "ac", Baz = 2 },
new Foo { Bar = "ad", Baz = 2 },
new Foo { Bar = null, Baz = 0 }
};
}

public class Query2
{
[UseFiltering(FilterType = typeof(FooFilterType))]
public IEnumerable<Foo> Foos { get; } = new[]
{
new Foo { Bar = "aa", Baz = 1, Qux = 1 },
new Foo { Bar = "ba", Baz = 1 },
new Foo { Bar = "ca", Baz = 2 },
new Foo { Bar = "ab", Baz = 2 },
new Foo { Bar = "ac", Baz = 2 },
new Foo { Bar = "ad", Baz = 2 },
new Foo { Bar = null, Baz = 0 }
};
}

public class FooFilterType : FilterInputType<Foo>
{
protected override void Configure(IFilterInputTypeDescriptor<Foo> descriptor)
{
descriptor.BindFieldsExplicitly()
.Filter(m => m.Bar)
.BindFiltersExplicitly()
.AllowEquals();
}
}

public class Foo
{
public string Bar { get; set; }

[GraphQLType(typeof(NonNullType<IntType>))]
public long Baz { get; set; }

[GraphQLType(typeof(IntType))]
public int? Qux { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
schema {
query: Query1
}

type Foo {
bar: String
baz: Int!
qux: Int
}

type Query1 {
foos(where: FooFilter): [Foo]
}

input FooFilter {
AND: [FooFilter!]
bar: String
bar_contains: String
bar_ends_with: String
bar_in: [String]
bar_not: String
bar_not_contains: String
bar_not_ends_with: String
bar_not_in: [String]
bar_not_starts_with: String
bar_starts_with: String
baz: Int
baz_gt: Int
baz_gte: Int
baz_in: [Int!]
baz_lt: Int
baz_lte: Int
baz_not: Int
baz_not_gt: Int
baz_not_gte: Int
baz_not_in: [Int!]
baz_not_lt: Int
baz_not_lte: Int
OR: [FooFilter!]
qux: Int
qux_gt: Int
qux_gte: Int
qux_in: [Int]
qux_lt: Int
qux_lte: Int
qux_not: Int
qux_not_gt: Int
qux_not_gte: Int
qux_not_in: [Int]
qux_not_lt: Int
qux_not_lte: Int
}

"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
scalar Int

"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."
scalar String
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
schema {
query: Query2
}

type Foo {
bar: String
baz: Int!
qux: Int
}

type Query2 {
foos(where: FooFilter): [Foo]
}

input FooFilter {
AND: [FooFilter!]
bar: String
OR: [FooFilter!]
}

"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
scalar Int

"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."
scalar String
36 changes: 36 additions & 0 deletions src/Core/Types.Filters/UseFilteringAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Reflection;

namespace HotChocolate.Types
{
public sealed class UseFilteringAttribute : ObjectFieldDescriptorAttribute
{
private static readonly MethodInfo _generic = typeof(FilterObjectFieldDescriptorExtensions)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Single(m => m.Name.Equals(
nameof(FilterObjectFieldDescriptorExtensions.UseFiltering),
StringComparison.Ordinal)
&& m.GetGenericArguments().Length == 1
&& m.GetParameters().Length == 1
&& m.GetParameters()[0].ParameterType == typeof(IObjectFieldDescriptor));

/// <summary>
/// Gets or sets the filter type which specifies the filter object structure.
/// </summary>
/// <value>The filter type</value>
public Type FilterType { get; set; }

public override void OnConfigure(IObjectFieldDescriptor descriptor)
{
if (FilterType is null)
{
descriptor.UseFiltering();
}
else
{
_generic.MakeGenericMethod(FilterType).Invoke(null, new[] { descriptor });
}
}
}
}
68 changes: 68 additions & 0 deletions src/Core/Types.Sorting.Tests/SortingAttributeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Collections.Generic;
using Snapshooter.Xunit;
using Xunit;

namespace HotChocolate.Types.Sorting
{
public class SortingAttributeTests
{
[Fact]
public void Use_Attribute_Without_SortType()
{
// act
ISchema schema = SchemaBuilder.New()
.AddQueryType<Query1>()
.Create();

// assert
schema.ToString().MatchSnapshot();
}

[Fact]
public void Use_Attribute_With_SortType()
{
// act
ISchema schema = SchemaBuilder.New()
.AddQueryType<Query2>()
.Create();

// assert
schema.ToString().MatchSnapshot();
}

public class Query1
{
[UseSorting]
public IEnumerable<Model> Models { get; } = new List<Model>
{
new Model { Foo = "Abc", Bar = 1 },
new Model { Foo = "Abc", Bar = 2 }
};
}

public class Query2
{
[UseSorting(SortType = typeof(ModelSortType))]
public IEnumerable<Model> Models { get; } = new List<Model>
{
new Model { Foo = "Abc", Bar = 1 },
new Model { Foo = "Abc", Bar = 2 }
};
}

public class ModelSortType : SortInputType<Model>
{
protected override void Configure(ISortInputTypeDescriptor<Model> descriptor)
{
descriptor.BindFieldsExplicitly().Sortable(t => t.Bar);
}
}

public class Model
{
public string Foo { get; set; }

public int Bar { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
schema {
query: Query2
}

type Model {
bar: Int!
foo: String
}

type Query2 {
models(order_by: ModelSort): [Model]
}

input ModelSort {
bar: SortOperationKind
}

enum SortOperationKind {
ASC
DESC
}

"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
scalar Int

"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."
scalar String
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
schema {
query: Query1
}

type Model {
bar: Int!
foo: String
}

type Query1 {
models(order_by: ModelSort): [Model]
}

input ModelSort {
bar: SortOperationKind
foo: SortOperationKind
}

enum SortOperationKind {
ASC
DESC
}

"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1."
scalar Int

"The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text."
scalar String
36 changes: 36 additions & 0 deletions src/Core/Types.Sorting/UseSortingAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Linq;
using System.Reflection;

namespace HotChocolate.Types
{
public sealed class UseSortingAttribute : ObjectFieldDescriptorAttribute
{
private static readonly MethodInfo _generic = typeof(SortObjectFieldDescriptorExtensions)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Single(m => m.Name.Equals(
nameof(SortObjectFieldDescriptorExtensions.UseSorting),
StringComparison.Ordinal)
&& m.GetGenericArguments().Length == 1
&& m.GetParameters().Length == 1
&& m.GetParameters()[0].ParameterType == typeof(IObjectFieldDescriptor));

/// <summary>
/// Gets or sets the sort type which specifies the sort object structure.
/// </summary>
/// <value>The sort type</value>
public Type SortType { get; set; }

public override void OnConfigure(IObjectFieldDescriptor descriptor)
{
if (SortType is null)
{
descriptor.UseSorting();
}
else
{
_generic.MakeGenericMethod(SortType).Invoke(null, new[] { descriptor });
}
}
}
}
Loading