コード例 #1
0
        /// <summary>
        /// Attempts to drill into the supplied type and remove the next found wrapper (given the provided filters)
        /// to distill it to its core type. (i.e. convert IEnumerable[T] to 'T').
        /// </summary>
        /// <param name="type">The type to inspect.</param>
        /// <param name="eliminateEnumerables">if set to <c>true</c> this method will attempt to eliminate any wrapper type that declares <see cref="IEnumerable{T}" />.</param>
        /// <param name="eliminateTask">if set to <c>true</c> this method will attempt to remove a <see cref="Task{T}" /> declaration from the type.</param>
        /// <param name="eliminateNullableT">if set to <c>true</c> the Nullable{T} wrapper that can exist on value types will be stripped (leaving just T).</param>
        /// <returns>Type.</returns>
        public static Type EliminateNextWrapperFromCoreType(
            Type type,
            bool eliminateEnumerables = true,
            bool eliminateTask        = true,
            bool eliminateNullableT   = true)
        {
            if (type == null)
            {
                return(null);
            }

            // eliminate Task<T>
            if (eliminateTask && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task <>))
            {
                return(type.GetGenericArguments()[0]);
            }

            // drill into the concrete type if a list was supplied List<T>, IEnumerable<T>, ICollection<T> etc.
            if (eliminateEnumerables && GraphValidation.IsValidListType(type))
            {
                return(type.GetEnumerableUnderlyingType(false));
            }

            if (eliminateNullableT && Validation.IsNullableOfT(type))
            {
                return(Nullable.GetUnderlyingType(type));
            }

            return(type);
        }
コード例 #2
0
        /// <summary>
        /// Forciably assigns a graph name to a type, will override any previously assigned name.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="kind">The kind of object for which the name will be assigned.</param>
        /// <param name="graphTypeName">Name of the graph type.</param>
        public static void AssignName(Type type, TypeKind kind, string graphTypeName)
        {
            Validation.ThrowIfNull(type, nameof(type));

            if (!GraphValidation.IsValidGraphName(graphTypeName))
            {
                throw new GraphTypeDeclarationException(
                          $"The type, '{type.FriendlyName()}', declares an invalid graph type name, '{graphTypeName}'. Graph type names " +
                          "can only contain letters A-Z, numbers 0-9 and an underscore. They must also not start with a double underscore.");
            }

            var typeNameDictionary = RetrieveTypeDictionary(type);

            typeNameDictionary.AddOrUpdate(kind, graphTypeName, (_, __) => graphTypeName);
        }
コード例 #3
0
        /// <summary>
        /// Parses the name of the type as it would exist in the object graph and returns the name.
        /// </summary>
        /// <param name="type">The concrete type to parse.</param>
        /// <param name="kind">The kind of graph type being created.</param>
        /// <returns>System.String.</returns>
        public static string ParseName(Type type, TypeKind kind)
        {
            Validation.ThrowIfNull(type, nameof(type));

            kind = GraphValidation.ResolveTypeKind(type, kind);
            var typeNameDictionary = RetrieveTypeDictionary(type);

            if (typeNameDictionary.TryGetValue(kind, out var typeName))
            {
                return(typeName);
            }

            type = GraphValidation.EliminateWrappersFromCoreType(type);
            if (GraphQLProviders.ScalarProvider.IsScalar(type))
            {
                typeName = GraphQLProviders.ScalarProvider.RetrieveScalar(type).Name;
            }
            else if (type.IsEnum)
            {
                // enums always are their declared name (no changes needed for input types)
                typeName = type.SingleAttributeOrDefault <GraphTypeAttribute>()?.Name;
            }
            else if (kind == TypeKind.INPUT_OBJECT)
            {
                var inputNameAttrib = type.SingleAttributeOrDefault <GraphTypeAttribute>();
                if (inputNameAttrib != null && !string.IsNullOrWhiteSpace(inputNameAttrib.InputName))
                {
                    if (type.IsGenericType)
                    {
                        throw new GraphTypeDeclarationException(
                                  $"Generic Types such as '{type.FriendlyName()}', cannot use the '{nameof(GraphTypeAttribute)}'. " +
                                  "Doing so would result in a single common name across multiple different instances of the type. " +
                                  "Remove the attribute and try again, the runtime will generate an acceptable name automatically.");
                    }

                    typeName = inputNameAttrib.InputName;
                }
                else
                {
                    typeName = GraphTypeNames.ParseName(type, TypeKind.OBJECT);
                    typeName = $"Input_{typeName}";
                }
            }
            else
            {
                var graphTypeNameAttrib = type.SingleAttributeOrDefault <GraphTypeAttribute>();
                if (graphTypeNameAttrib != null && !string.IsNullOrWhiteSpace(graphTypeNameAttrib.Name))
                {
                    if (type.IsGenericType)
                    {
                        throw new GraphTypeDeclarationException(
                                  $"Generic Types such as '{type.FriendlyName()}', cannot use the '{nameof(GraphTypeAttribute)}'. " +
                                  "Doing so would result in a single common name across multiple different instances of the type. " +
                                  "Remove the declared attribute and try again.");
                    }

                    typeName = graphTypeNameAttrib.Name;
                }
            }

            typeName = typeName ?? type.FriendlyName("_");
            typeName = typeName.Replace(Constants.Routing.CLASS_META_NAME, type.Name).Trim();
            if (kind == TypeKind.DIRECTIVE && typeName.EndsWith(Constants.CommonSuffix.DIRECTIVE_SUFFIX))
            {
                typeName = typeName.ReplaceLastInstanceOfCaseInvariant(Constants.CommonSuffix.DIRECTIVE_SUFFIX, string.Empty);
            }

            AssignName(type, kind, typeName);
            return(typeName);
        }
コード例 #4
0
        /// <summary>
        /// Inspects the provided type and generates a type expression to represent it in the object grpah.
        /// </summary>
        /// <param name="typeToCheck">The complete type specification to check.</param>
        /// <param name="typeDefinition">An optional expression declaration to use as a set of overrides on the type provided.</param>
        /// <returns>GraphFieldOptions.</returns>
        public static GraphTypeExpression GenerateTypeExpression(Type typeToCheck, IGraphTypeExpressionDeclaration typeDefinition = null)
        {
            Validation.ThrowIfNull(typeToCheck, nameof(typeToCheck));

            if (typeDefinition?.TypeWrappers != null)
            {
                typeToCheck = GraphValidation.EliminateWrappersFromCoreType(
                    typeToCheck,
                    eliminateEnumerables: true,
                    eliminateTask: true,
                    eliminateNullableT: true);

                return(new GraphTypeExpression(typeToCheck.FriendlyName(), typeDefinition.TypeWrappers));
            }

            // strip out Task{T} before doin any type inspections
            typeToCheck = GraphValidation.EliminateWrappersFromCoreType(
                typeToCheck,
                eliminateEnumerables: false,
                eliminateTask: true,
                eliminateNullableT: false);

            var hasDefinedDefaultValue = typeDefinition?.HasDefaultValue ?? false;

            List <MetaGraphTypes> wrappers = new List <MetaGraphTypes>();

            if (GraphValidation.IsValidListType(typeToCheck))
            {
                // auto generated type expressions will always allow for a nullable list (since class references can be null)
                // unwrap any nested lists as necessary:  e.g.  IEnumerable<IEnumerable<IEnumerable<T>>>
                while (true)
                {
                    var unwrappedType = EliminateNextWrapperFromCoreType(
                        typeToCheck,
                        eliminateEnumerables: true,
                        eliminateTask: false,
                        eliminateNullableT: false);

                    if (unwrappedType == typeToCheck)
                    {
                        break;
                    }

                    wrappers.Add(MetaGraphTypes.IsList);
                    typeToCheck = unwrappedType;
                }

                if (!hasDefinedDefaultValue && GraphValidation.IsNotNullable(typeToCheck))
                {
                    wrappers.Add(MetaGraphTypes.IsNotNull);
                }
            }
            else if (!hasDefinedDefaultValue && GraphValidation.IsNotNullable(typeToCheck))
            {
                wrappers.Add(MetaGraphTypes.IsNotNull);
            }

            typeToCheck = EliminateWrappersFromCoreType(
                typeToCheck,
                eliminateEnumerables: false,
                eliminateTask: false,
                eliminateNullableT: true);

            return(new GraphTypeExpression(typeToCheck.FriendlyName(), wrappers));
        }
コード例 #5
0
        /// <summary>
        /// Determines whether the given type CAN be represented in the object graph.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="throwOnFailure">if set to <c>true</c> a context rich <see cref="GraphTypeDeclarationException"/> will be thrown instead of returning false.</param>
        /// <returns><c>true</c> if the type can be correctly rendered otherwise, <c>false</c>.</returns>
        public static bool IsValidGraphType(Type type, bool throwOnFailure = false)
        {
            if (type == null)
            {
                return(false);
            }

            if (type == typeof(void))
            {
                return(false);
            }

            if (type.IsEnum)
            {
                return(true);
            }

            if (type == typeof(object))
            {
                if (throwOnFailure)
                {
                    throw new GraphTypeDeclarationException(
                              $"The type '{typeof(object).FriendlyName()}' cannot be used directly in an object graph. GraphQL requires a complete " +
                              "definition of a schema for validation and introspection.  Using a generic object prohibits this library from " +
                              "scanning for fields, interfaces and inputs.",
                              type);
                }

                return(false);
            }

            // remove any IEnumerable or Task wrappers from the supplied type
            // so we can validate the core entity
            type = GraphValidation.EliminateWrappersFromCoreType(type);

            // explicitly disallow dictionaries
            // graphQL doesn't allow for arbitrary keyvalue pairs in any type renderings
            if (typeof(IDictionary).IsAssignableFrom(type))
            {
                if (throwOnFailure)
                {
                    throw new GraphTypeDeclarationException(
                              $"The type '{type.FriendlyName()}' appears to be a {typeof(IDictionary).FriendlyName()}. Objects " +
                              "which allow for arbitrary key/value pairs of data are not allowed in graphQL. This type cannot be used as a publically" +
                              "available graph type.",
                              type);
                }

                return(false);
            }

            if (type.IsGenericType)
            {
                if (typeof(IDictionary <,>).IsAssignableFrom(type.GetGenericTypeDefinition()))
                {
                    if (throwOnFailure)
                    {
                        throw new GraphTypeDeclarationException(
                                  $"The type '{type.FriendlyName()}' appears to be a {typeof(IDictionary<,>).FriendlyName()}. Objects " +
                                  "which allow for arbitrary key/value pairs of data are not allowed in graphQL. This type cannot be used as a publically" +
                                  "available graph type.",
                                  type);
                    }

                    return(false);
                }

                if (typeof(IReadOnlyDictionary <,>).IsAssignableFrom(type.GetGenericTypeDefinition()))
                {
                    if (throwOnFailure)
                    {
                        throw new GraphTypeDeclarationException(
                                  $"The type '{type.FriendlyName()}' appears to be a {typeof(IReadOnlyDictionary<,>).FriendlyName()}. Objects " +
                                  "which allow for arbitrary key/value pairs of data are not allowed in graphQL. This type cannot be used as a publically" +
                                  "available graph type.",
                                  type);
                    }

                    return(false);
                }
            }

            return(true);
        }
コード例 #6
0
 /// <summary>
 /// Determines whether the given type CAN be represented in the object graph. An exception is thrown if it cannot be.
 /// </summary>
 /// <param name="type">The type to check.</param>
 public static void EnsureValidGraphTypeOrThrow(Type type)
 {
     GraphValidation.IsValidGraphType(type, true);
 }