Skip to content

Commit

Permalink
Merge branch 'hotfix/91'
Browse files Browse the repository at this point in the history
  • Loading branch information
sahb1239 committed Jan 7, 2020
2 parents 468e6ee + 077ee93 commit 6a3b497
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 20 deletions.
135 changes: 115 additions & 20 deletions src/SAHB.GraphQLClient/Introspection/Validation/GraphQLValidation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,24 @@ private static IEnumerable<ValidationError> ValidateSelectionSet(GraphQLIntrospe
// Validate type
if (selection.BaseType != null)
{
if (graphQLType.Kind == GraphQLTypeKind.Enum || graphQLType.Kind == GraphQLTypeKind.Scalar)
{
foreach (var error in ValidateNonNullScalar(selectionFieldPath, selection, IsListType(introspectionField.Type), IsNonNull(introspectionField.Type), graphQLType))
{
yield return error;
}
}

switch (graphQLType.Kind)
{
case GraphQLTypeKind.Enum:
foreach (var error in ValidateEnum(selectionFieldPath, selection, IsListType(introspectionField.Type), graphQLType))
foreach (var error in ValidateEnum(selectionFieldPath, selection, IsListType(introspectionField.Type), IsNonNull(introspectionField.Type), graphQLType))
{
yield return error;
}
break;
case GraphQLTypeKind.Scalar:
foreach (var error in ValidateScalar(selectionFieldPath, selection, IsListType(introspectionField.Type), graphQLType))
foreach (var error in ValidateScalar(selectionFieldPath, selection, IsListType(introspectionField.Type), IsNonNull(introspectionField.Type), graphQLType))
{
yield return error;
}
Expand Down Expand Up @@ -215,7 +223,7 @@ private static string GetTypeName(GraphQLIntrospectionTypeRef type)
}
}

private static IEnumerable<ValidationError> ValidateScalar(string selectionFieldPath, GraphQLField selection, bool isListType, GraphQLIntrospectionFullType graphQLType)
private static Type GetSpecificType(GraphQLField selection, bool isListType)
{
var type = selection.BaseType;

Expand All @@ -224,10 +232,23 @@ private static IEnumerable<ValidationError> ValidateScalar(string selectionField
type = GetIEnumerableType(type);
}

var isNullable = IsNullableType(type);
if (isNullable)
{
type = type.GenericTypeArguments.First();
}

return type;
}

private static IEnumerable<ValidationError> ValidateScalar(string selectionFieldPath, GraphQLField selection, bool isListType, bool isNonNull, GraphQLIntrospectionFullType graphQLType)
{
var type = GetSpecificType(selection, isListType);

switch (graphQLType.Name)
{
case "String":
if (type != typeof(string))
if (!IsValidStringType(type))
{
yield return new ValidationError(
selectionFieldPath,
Expand All @@ -238,7 +259,7 @@ private static IEnumerable<ValidationError> ValidateScalar(string selectionField
}
break;
case "Boolean":
if (type != typeof(bool))
if (!IsValidBooleanType(type, isNonNull))
{
yield return new ValidationError(
selectionFieldPath,
Expand All @@ -250,9 +271,7 @@ private static IEnumerable<ValidationError> ValidateScalar(string selectionField
break;
case "Float":
case "Decimal":
if (type != typeof(float)
&& type != typeof(double)
&& type != typeof(decimal))
if (!IsValidFloatOrDecimalType(type, isNonNull))
{
yield return new ValidationError(
selectionFieldPath,
Expand All @@ -265,14 +284,30 @@ private static IEnumerable<ValidationError> ValidateScalar(string selectionField
}
}

private static IEnumerable<ValidationError> ValidateEnum(string selectionFieldPath, GraphQLField selection, bool isListType, GraphQLIntrospectionFullType graphQLType)
private static bool IsValidStringType(Type type)
{
var type = selection.BaseType;
return type == typeof(string);
}

if (isListType)
{
type = GetIEnumerableType(type);
}
private static bool IsValidBooleanType(Type type, bool isNonNull)
{
return type == typeof(bool)
|| (!isNonNull && type == typeof(bool?));
}

private static bool IsValidFloatOrDecimalType(Type type, bool isNonNull)
{
return type == typeof(float)
|| type == typeof(double)
|| type == typeof(decimal)
|| (!isNonNull && type == typeof(float?))
|| (!isNonNull && type == typeof(double?))
|| (!isNonNull && type == typeof(decimal?));
}

private static IEnumerable<ValidationError> ValidateEnum(string selectionFieldPath, GraphQLField selection, bool isListType, bool isNonNull, GraphQLIntrospectionFullType graphQLType)
{
var type = GetSpecificType(selection, isListType);

if (type.GetTypeInfo().IsEnum)
{
Expand Down Expand Up @@ -316,24 +351,84 @@ private static IEnumerable<ValidationError> ValidateEnum(string selectionFieldPa
}
}

private static bool IsListType(GraphQLIntrospectionTypeRef graphQLIntrospectionTypeRef)
private static IEnumerable<ValidationError> ValidateNonNullScalar(string selectionFieldPath, GraphQLField selection, bool isListType, bool isNonNull, GraphQLIntrospectionFullType graphQLType)
{
if (graphQLIntrospectionTypeRef is null)
var type = selection.BaseType;

if (isListType)
{
throw new ArgumentNullException(nameof(graphQLIntrospectionTypeRef));
type = GetIEnumerableType(type);
}

if (graphQLIntrospectionTypeRef.Kind == GraphQLTypeKind.List)
// If type is class it is always nullable
if (type.GetTypeInfo().IsClass)
yield break;

var isNullable = IsNullableType(type);

// Validate nullable
if (!isNullable != isNonNull)
{
if (isNonNull)
{
yield return new ValidationError(selectionFieldPath, ValidationType.Field_Should_Be_NonNull, selection);
}
else
{
yield return new ValidationError(selectionFieldPath, ValidationType.Field_Should_Be_Nullable, selection);
}
}
}

private static bool IsNullableType(Type type)
{
var typeinfo = type.GetTypeInfo();
if (typeinfo.GenericTypeArguments.Length == 1)
{
var genericType = typeinfo.GetGenericTypeDefinition();
if (genericType == typeof(Nullable<>))
{
return true;
}
}
return false;
}

private static bool IsListType(GraphQLIntrospectionTypeRef type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}

if (type.Kind == GraphQLTypeKind.List)
return true;

if (HasSubtype(graphQLIntrospectionTypeRef.Kind))
if (HasSubtype(type.Kind))
{
return IsListType(graphQLIntrospectionTypeRef.OfType);
return IsListType(type.OfType);
}

return false;
}

private static bool IsNonNull(GraphQLIntrospectionTypeRef type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}

if (IsListType(type))
{
return IsNonNull(type.OfType);
}
else
{
return type.Kind == GraphQLTypeKind.NonNull;
}
}

private static GraphQLIntrospectionFullType GetSubtype(GraphQLIntrospectionSchema graphQLIntrospectionSchema, GraphQLIntrospectionTypeRef graphQLIntrospectionTypeRef)
{
if (graphQLIntrospectionSchema is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ public string Message {
return $"Field at {Path} was not found";
case ValidationType.Field_Should_Have_SelectionSet:
return $"Field at {Path} should have a selectionSet";
case ValidationType.Field_Should_Be_NonNull:
return $"Field at {Path} should be non nullable";
case ValidationType.Field_Should_Be_Nullable:
return $"Field at {Path} should be nullable";
case ValidationType.Operation_Type_Not_Found:
return $"OperationType {OperationType} was not found";
case ValidationType.PossibleType_Not_Found:
Expand Down
10 changes: 10 additions & 0 deletions src/SAHB.GraphQLClient/Introspection/Validation/ValidationType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ public enum ValidationType
/// </summary>
Field_Invalid_Type,

/// <summary>
/// This validationType is used if the specified type should be nullable
/// </summary>
Field_Should_Be_Nullable,

/// <summary>
/// This validationType is used if the specified type should be not nullable
/// </summary>
Field_Should_Be_NonNull,

/// <summary>
/// This validationType is used if the specified type of the field is not an enum and therefore not valid
/// </summary>
Expand Down
Loading

0 comments on commit 6a3b497

Please sign in to comment.