Skip to content

Commit

Permalink
Merge 5b82f08 into fa1a320
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane32 committed Nov 17, 2021
2 parents fa1a320 + 5b82f08 commit 3c2e18f
Show file tree
Hide file tree
Showing 18 changed files with 2,303 additions and 493 deletions.
145 changes: 29 additions & 116 deletions src/GraphQL.DI/AutoInputObjectGraphType.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using GraphQL.Types;

namespace GraphQL.DI
Expand All @@ -14,43 +12,34 @@ namespace GraphQL.DI
/// </summary>
public class AutoInputObjectGraphType<TSourceType> : InputObjectGraphType<TSourceType>
{
private const string ORIGINAL_EXPRESSION_PROPERTY_NAME = nameof(ORIGINAL_EXPRESSION_PROPERTY_NAME);

/// <summary>
/// Creates a GraphQL type from <typeparamref name="TSourceType"/>.
/// </summary>
public AutoInputObjectGraphType()
{
var classType = typeof(TSourceType);

//allow default name / description / obsolete tags to remain if not overridden
var nameAttribute = classType.GetCustomAttribute<NameAttribute>();
if (nameAttribute != null)
Name = nameAttribute.Name;
else {
var name = GetDefaultName();
if (name != null)
Name = name;
}
GraphTypeHelper.ConfigureGraph<TSourceType>(this, GetDefaultName);

var descriptionAttribute = classType.GetCustomAttribute<DescriptionAttribute>();
if (descriptionAttribute != null)
Description = descriptionAttribute.Description;
var obsoleteAttribute = classType.GetCustomAttribute<ObsoleteAttribute>();
if (obsoleteAttribute != null)
DeprecationReason = obsoleteAttribute.Message;
//pull metadata
foreach (var metadataAttribute in classType.GetCustomAttributes<MetadataAttribute>())
Metadata.Add(metadataAttribute.Key, metadataAttribute.Value);

foreach (var property in GetRegisteredProperties()) {
if (property != null) {
var fieldType = ProcessProperty(property);
if (fieldType != null)
AddField(fieldType);
GraphTypeHelper.AddFields(this, CreateFieldTypeList());
}

}
/// <summary>
/// Returns a list of <see cref="FieldType"/> instances representing the fields ready to be
/// added to the graph type.
/// Sorts the list if specified by <see cref="SortMembers"/>.
/// </summary>
protected virtual IEnumerable<FieldType> CreateFieldTypeList()
{
//scan for public members
var properties = GetPropertiesToProcess();
var fieldTypeList = new List<FieldType>(properties.Count());
foreach (var property in properties) {
var fieldType = ProcessProperty(property);
if (fieldType != null)
fieldTypeList.Add(fieldType);
}
if (SortMembers)
return fieldTypeList.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
return fieldTypeList;
}

/// <summary>
Expand All @@ -76,14 +65,11 @@ public AutoInputObjectGraphType()

/// <summary>
/// Returns a list of properties that should have fields created for them.
/// Sorts the list if specified by <see cref="SortMembers"/>.
/// </summary>
protected virtual IEnumerable<PropertyInfo> GetRegisteredProperties()
protected virtual IEnumerable<PropertyInfo> GetPropertiesToProcess()
{
var props = typeof(TSourceType).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.CanWrite);
if (SortMembers)
props = props.OrderBy(x => x.Name, StringComparer.InvariantCultureIgnoreCase);
return props;
}

Expand All @@ -94,100 +80,27 @@ protected virtual IEnumerable<PropertyInfo> GetRegisteredProperties()
/// <returns></returns>
protected virtual FieldType? ProcessProperty(PropertyInfo property)
{
//get the field name
string fieldName = property.Name;
var fieldNameAttribute = property.GetCustomAttribute<NameAttribute>();
if (fieldNameAttribute != null) {
fieldName = fieldNameAttribute.Name;
}
if (fieldName == null)
return null; //ignore field if set to null (or Ignore is specified)

//determine the graphtype of the field
var graphTypeAttribute = property.GetCustomAttribute<GraphTypeAttribute>();
Type? graphType = graphTypeAttribute?.Type;
//infer the graphtype if it is not specified
if (graphType == null) {
graphType = InferGraphType(ApplyAttributes(GetTypeInformation(property)));
}
//load the description
string? description = property.GetCustomAttribute<DescriptionAttribute>()?.Description;
//load the deprecation reason
string? obsoleteDescription = property.GetCustomAttribute<ObsoleteAttribute>()?.Message;
var fieldType = GraphTypeHelper.CreateField(
property,
() => InferGraphType(ApplyAttributes(GetTypeInformation(property))));
if (fieldType == null)
return null;
//load the default value
object? defaultValue = property.GetCustomAttribute<DefaultValueAttribute>()?.Value;
//create the field
var fieldType = new FieldType() {
Type = graphType,
Name = fieldName,
Description = description,
DeprecationReason = obsoleteDescription,
DefaultValue = defaultValue,
};
//set name of property
fieldType.WithMetadata(ORIGINAL_EXPRESSION_PROPERTY_NAME, property.Name);
//load the metadata
foreach (var metaAttribute in property.GetCustomAttributes<MetadataAttribute>())
fieldType.WithMetadata(metaAttribute.Key, metaAttribute.Value);
fieldType.DefaultValue = property.GetCustomAttribute<DefaultValueAttribute>()?.Value;
//return the field
return fieldType;
}

/// <inheritdoc cref="ReflectionExtensions.GetNullabilityInformation(ParameterInfo)"/>
protected virtual IEnumerable<(Type Type, Nullability Nullable)> GetNullabilityInformation(PropertyInfo parameter)
{
return parameter.GetNullabilityInformation();
}

private static readonly Type[] _listTypes = new Type[] {
typeof(IEnumerable<>),
typeof(IList<>),
typeof(List<>),
typeof(ICollection<>),
typeof(IReadOnlyCollection<>),
typeof(IReadOnlyList<>),
typeof(HashSet<>),
typeof(ISet<>),
};
=> parameter.GetNullabilityInformation();

/// <summary>
/// Analyzes a property and returns a <see cref="TypeInformation"/>
/// struct containing type information necessary to select a graph type.
/// </summary>
protected virtual TypeInformation GetTypeInformation(PropertyInfo propertyInfo)
{
var isList = false;
var isNullableList = false;
var typeTree = GetNullabilityInformation(propertyInfo);
foreach (var type in typeTree) {
if (type.Type.IsArray) {
//unwrap type and mark as list
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
continue;
}
if (type.Type.IsGenericType) {
var g = type.Type.GetGenericTypeDefinition();
if (_listTypes.Contains(g)) {
//unwrap type and mark as list
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
continue;
}
}
if (type.Type == typeof(IEnumerable) || type.Type == typeof(ICollection)) {
//assume list of nullable object
isList = true;
isNullableList = type.Nullable != Nullability.NonNullable;
break;
}
//found match
var nullable = type.Nullable != Nullability.NonNullable;
return new TypeInformation(propertyInfo, true, type.Type, nullable, isList, isNullableList, null);
}
//unknown type
return new TypeInformation(propertyInfo, true, typeof(object), true, isList, isNullableList, null);
}
=> GraphTypeHelper.GetTypeInformation(propertyInfo, true, GetNullabilityInformation);

/// <summary>
/// Apply <see cref="RequiredAttribute"/>, <see cref="OptionalAttribute"/>, <see cref="RequiredListAttribute"/>,
Expand Down
Loading

0 comments on commit 3c2e18f

Please sign in to comment.