Skip to content

Commit

Permalink
Add support for inferrence of structs (#5752)
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalSenn authored Feb 3, 2023
1 parent 98c245b commit 967365a
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 6 deletions.
26 changes: 23 additions & 3 deletions src/HotChocolate/Core/src/Types/Internal/TypeDiscoveryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,38 @@ private static bool IsComplexTypeInternal(
IExtendedType unresolvedType,
bool isPublic)
{
var isComplexType =
var isComplexClass =
isPublic &&
unresolvedType.Type.IsClass &&
unresolvedType.Type != typeof(string);

if (!isComplexType && unresolvedType.IsGeneric)
#if NET6_0_OR_GREATER
var isComplexValueType =
isPublic &&
unresolvedType.Type is
{
IsValueType: true,
IsPrimitive: false,
IsEnum: false,
IsByRefLike: false
};

if (isComplexValueType && unresolvedType.IsGeneric)
{
var typeDefinition = unresolvedType.Definition;
return typeDefinition == typeof(KeyValuePair<,>);
}

return isComplexClass || isComplexValueType;
#else
if (!isComplexClass && unresolvedType.IsGeneric)
{
var typeDefinition = unresolvedType.Definition;
return typeDefinition == typeof(KeyValuePair<,>);
}

return isComplexType;
return isComplexClass;
#endif
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,47 @@ public void InferObjectType(TypeContext context)
});
}

[InlineData(TypeContext.Output)]
[InlineData(TypeContext.None)]
[Theory]
public void InferObjectTypeFromStruct(TypeContext context)
{
// arrange
var descriptorContext = DescriptorContext.Create();
var typeReference = TypeReference.Create(TypeOf<BarStruct>(), context);

// act
var success = descriptorContext.TryInferSchemaType(typeReference, out var schemaTypes);

// assert
Assert.True(success);
Assert.Collection(schemaTypes,
type =>
{
Assert.Equal(TypeContext.Output, type.Context);
Assert.Equal(typeof(ObjectType<BarStruct>), ((ExtendedTypeReference)type).Type.Source);
});
}

[InlineData(TypeContext.Output)]
[InlineData(TypeContext.None)]
[Theory]
public void RejectRefStructAsObjectType(TypeContext context)
{
// arrange
var descriptorContext = DescriptorContext.Create();
var typeReference = TypeReference.Create(
_typeInspector.GetType(typeof(BarRefStruct)),
context);

// act
var success = descriptorContext.TryInferSchemaType(typeReference, out var schemaTypes);

// assert
Assert.False(success);
Assert.Null(schemaTypes);
}

[InlineData(TypeContext.Output)]
[InlineData(TypeContext.None)]
[Theory]
Expand All @@ -54,7 +95,6 @@ public void InferInterfaceType(TypeContext context)
Assert.Equal(TypeContext.Output, type.Context);
Assert.Equal(typeof(InterfaceType<IBar>), ((ExtendedTypeReference)type).Type.Source);
});

}

[Fact]
Expand Down Expand Up @@ -108,6 +148,16 @@ public class Bar
public string Baz { get; }
}

public struct BarStruct
{
public string Baz { get; }
}

public ref struct BarRefStruct
{
public string Baz { get; }
}

public interface IBar
{
string Baz { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,20 @@ public void InferDateTimeFromModel()
.MatchSnapshot();
}

[Fact]
public void TypeDiscovery_Should_InferStructs()
{
SchemaBuilder.New()
.AddQueryType<QueryTypeWithStruct>()
.Create()
.Print()
.MatchSnapshot();
}

public class QueryWithDateTime
{
public DateTimeOffset DateTimeOffset(DateTimeOffset time) => time;

public DateTime DateTime(DateTime time) => time;
}

Expand All @@ -50,8 +61,7 @@ protected override void Configure(IObjectTypeDescriptor descriptor)

public class ModelType : ObjectType<Model>
{
protected override void Configure(
IObjectTypeDescriptor<Model> descriptor)
protected override void Configure(IObjectTypeDescriptor<Model> descriptor)
{
descriptor.Field(t => t.Time)
.Type<NonNullType<DateTimeType>>();
Expand All @@ -64,9 +74,39 @@ protected override void Configure(
public class Model
{
public string Foo { get; set; }

public int Bar { get; set; }

public bool Baz { get; set; }

public DateTime Time { get; set; }

public DateTime Date { get; set; }
}

public struct InferStruct
{
public Guid Id { get; set; }

public int Number { get; set; }
}

public class QueryTypeWithStruct
{
public InferStruct Struct { get; set; }

public InferStruct? NullableStruct { get; set; }

public InferStruct[] StructArray { get; set; }

public InferStruct?[] NullableStructArray { get; set; }

public InferStruct[][] StructNestedArray { get; set; }

public InferStruct?[][] NullableStructNestedArray { get; set; }

public Guid ScalarGuid { get; set; }

public DateTime ScalarDateTime { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
schema {
query: QueryTypeWithStruct
}

type InferStruct {
id: UUID!
number: Int!
}

type QueryTypeWithStruct {
struct: InferStruct!
nullableStruct: InferStruct
structArray: [InferStruct!]
nullableStructArray: [InferStruct]
structNestedArray: [[InferStruct!]]
nullableStructNestedArray: [[InferStruct]]
scalarGuid: UUID!
scalarDateTime: DateTime!
}

"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions."
directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR

"The `DateTime` scalar represents an ISO-8601 compliant date time type."
scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time")

scalar UUID @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc4122")

0 comments on commit 967365a

Please sign in to comment.