Skip to content

Commit

Permalink
Get default value from constructor when type is a record. (#2428)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Staib <michael@chillicream.com>
  • Loading branch information
PascalSenn and michaelstaib committed Oct 14, 2020
1 parent d7eba9d commit 1b34d9a
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 56 deletions.
Expand Up @@ -27,6 +27,7 @@ public class DefaultTypeInspector

private readonly Dictionary<MemberInfo, ExtendedMethodInfo> _methods =
new Dictionary<MemberInfo, ExtendedMethodInfo>();

private readonly Dictionary<Type, bool> _records =
new Dictionary<Type, bool>();

Expand Down Expand Up @@ -309,6 +310,11 @@ public virtual bool TryGetDefaultValue(PropertyInfo property, out object? defaul
return true;
}

if (TryGetDefaultValueFromConstructor(property, out defaultValue))
{
return true;
}

defaultValue = null;
return false;
}
Expand Down Expand Up @@ -566,9 +572,9 @@ private static bool HasConfiguration(ICustomAttributeProvider element)

private static bool IsIgnored(MemberInfo member)
{
if (IsCloneMember(member) ||
IsToString(member) ||
IsGetHashCode(member) ||
if (IsCloneMember(member) ||
IsToString(member) ||
IsGetHashCode(member) ||
IsEquals(member))
{
return true;
Expand Down Expand Up @@ -597,6 +603,7 @@ private bool IsRecord(Type type)
isRecord = IsRecord(type.GetMembers());
_records[type] = isRecord;
}

return isRecord;
}

Expand All @@ -609,6 +616,7 @@ private static bool IsRecord(IReadOnlyList<MemberInfo> members)
return true;
}
}

return false;
}

Expand All @@ -633,7 +641,8 @@ private static bool IsRecord(IReadOnlyList<MemberInfo> members)
}

private IEnumerable<T> GetCustomAttributesFromRecord<T>(
PropertyInfo property, bool inherit)
PropertyInfo property,
bool inherit)
where T : Attribute
{
Type recordType = property.DeclaringType!;
Expand Down Expand Up @@ -661,7 +670,8 @@ private static bool IsRecord(IReadOnlyList<MemberInfo> members)
}

private T? GetCustomAttributeFromRecord<T>(
PropertyInfo property, bool inherit)
PropertyInfo property,
bool inherit)
where T : Attribute
{
Type recordType = property.DeclaringType!;
Expand All @@ -687,7 +697,8 @@ private static bool IsRecord(IReadOnlyList<MemberInfo> members)
}

private static bool IsDefinedOnRecord<T>(
PropertyInfo property, bool inherit)
PropertyInfo property,
bool inherit)
where T : Attribute
{
Type recordType = property.DeclaringType!;
Expand All @@ -711,5 +722,29 @@ private static bool IsRecord(IReadOnlyList<MemberInfo> members)

return false;
}

private bool TryGetDefaultValueFromConstructor(
PropertyInfo property,
out object? defaultValue)
{
defaultValue = null;
if (IsRecord(property.DeclaringType!))
{
ConstructorInfo[] constructors = property.DeclaringType!.GetConstructors();

if (constructors.Length == 1)
{
foreach (ParameterInfo parameter in constructors[0].GetParameters())
{
if (parameter.Name.EqualsOrdinal(property.Name))
{
return TryGetDefaultValue(parameter, out defaultValue);
}
}
}
}

return false;
}
}
}
}
120 changes: 71 additions & 49 deletions src/HotChocolate/Core/test/Types.Records.Tests/RecordsTests.cs
@@ -1,49 +1,71 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using HotChocolate.Types.Relay;
using static HotChocolate.Tests.TestHelper;
using System.Threading.Tasks;
using HotChocolate.Tests;
using Snapshooter.Xunit;
using Xunit;
using HotChocolate.Execution;

namespace HotChocolate.Types
{
public class RecordsTests
{
[Fact]
public async Task Records_Clone_Member_Is_Removed()
{
Snapshot.FullName();

await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query>()
.Services
.BuildServiceProvider()
.GetSchemaAsync()
.MatchSnapshotAsync();
}

[Fact]
public async Task Relay_Id_Middleware_Is_Correctly_Applied()
{
Snapshot.FullName();

await ExpectValid
(
@"{ person { id name } }",
b => b.AddQueryType<Query>()
)
.MatchSnapshotAsync(); ;
}

public class Query
{
public Person GetPerson() => new Person(1, "Michael");
}

public record Person([ID] int Id, string Name);
}
}
using System;
using Microsoft.Extensions.DependencyInjection;
using HotChocolate.Types.Relay;
using static HotChocolate.Tests.TestHelper;
using System.Threading.Tasks;
using HotChocolate.Tests;
using Snapshooter.Xunit;
using Xunit;
using HotChocolate.Execution;

namespace HotChocolate.Types
{
public class RecordsTests
{
[Fact]
public async Task Records_Clone_Member_Is_Removed()
{
Snapshot.FullName();

await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query>()
.Services
.BuildServiceProvider()
.GetSchemaAsync()
.MatchSnapshotAsync();
}

[Fact]
public async Task Records_Default_Value_Is_Taken_From_Ctor()
{
Snapshot.FullName();

await new ServiceCollection()
.AddGraphQL()
.AddQueryType<Query2>()
.Services
.BuildServiceProvider()
.GetSchemaAsync()
.MatchSnapshotAsync();
}

[Fact]
public async Task Relay_Id_Middleware_Is_Correctly_Applied()
{
Snapshot.FullName();

await ExpectValid
(
@"{ person { id name } }",
b => b.AddQueryType<Query>()
)
.MatchSnapshotAsync();
}

public class Query
{
public Person GetPerson() => new Person(1, "Michael");
}

public record Person([ID] int Id, string Name);

public class Query2
{
public DefaultValueTest GetPerson(DefaultValueTest? defaultValueTest) =>
new DefaultValueTest(1, "Test");
}

public record DefaultValueTest([ID] int Id, string Name = "ShouldBeDefaultValue");
}
}
@@ -0,0 +1,23 @@
schema {
query: Query2
}

type DefaultValueTest {
id: ID!
name: String!
}

type Query2 {
person(defaultValueTest: DefaultValueTestInput): DefaultValueTest!
}

input DefaultValueTestInput {
id: ID!
name: String! = "ShouldBeDefaultValue"
}

"The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID."
scalar ID

"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

0 comments on commit 1b34d9a

Please sign in to comment.