/** * Copyright (c) 2015-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict */ import isInvalid from '../jsutils/isInvalid'; import objectValues from '../jsutils/objectValues'; import { astFromValue } from '../utilities/astFromValue'; import { print } from '../language/printer'; import { GraphQLObjectType, GraphQLEnumType, GraphQLList, GraphQLNonNull, isScalarType, isObjectType, isInterfaceType, isUnionType, isEnumType, isInputObjectType, isListType, isNonNullType, isAbstractType, isNamedType, } from './definition'; import { GraphQLString, GraphQLBoolean } from './scalars'; import { DirectiveLocation } from '../language/directiveLocation'; import type { GraphQLField } from './definition'; export const __Schema = new GraphQLObjectType({ name: '__Schema', isIntrospection: true, description: 'A GraphQL Schema defines the capabilities of a GraphQL server. It ' + 'exposes all available types and directives on the server, as well as ' + 'the entry points for query, mutation, and subscription operations.', fields: () => ({ types: { description: 'A list of all types supported by this server.', type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__Type))), resolve(schema) { return objectValues(schema.getTypeMap()); }, }, queryType: { description: 'The type that query operations will be rooted at.', type: GraphQLNonNull(__Type), resolve: schema => schema.getQueryType(), }, mutationType: { description: 'If this server supports mutation, the type that ' + 'mutation operations will be rooted at.', type: __Type, resolve: schema => schema.getMutationType(), }, subscriptionType: { description: 'If this server support subscription, the type that ' + 'subscription operations will be rooted at.', type: __Type, resolve: schema => schema.getSubscriptionType(), }, directives: { description: 'A list of all directives supported by this server.', type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__Directive))), resolve: schema => schema.getDirectives(), }, }), }); export const __Directive = new GraphQLObjectType({ name: '__Directive', isIntrospection: true, description: 'A Directive provides a way to describe alternate runtime execution and ' + 'type validation behavior in a GraphQL document.' + "\n\nIn some cases, you need to provide options to alter GraphQL's " + 'execution behavior in ways field arguments will not suffice, such as ' + 'conditionally including or skipping a field. Directives provide this by ' + 'describing additional information to the executor.', fields: () => ({ name: { type: GraphQLNonNull(GraphQLString) }, description: { type: GraphQLString }, locations: { type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__DirectiveLocation))), }, args: { type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))), resolve: directive => directive.args || [], }, // NOTE: the following three fields are deprecated and are no longer part // of the GraphQL specification. onOperation: { deprecationReason: 'Use `locations`.', type: GraphQLNonNull(GraphQLBoolean), resolve: d => d.locations.indexOf(DirectiveLocation.QUERY) !== -1 || d.locations.indexOf(DirectiveLocation.MUTATION) !== -1 || d.locations.indexOf(DirectiveLocation.SUBSCRIPTION) !== -1, }, onFragment: { deprecationReason: 'Use `locations`.', type: GraphQLNonNull(GraphQLBoolean), resolve: d => d.locations.indexOf(DirectiveLocation.FRAGMENT_SPREAD) !== -1 || d.locations.indexOf(DirectiveLocation.INLINE_FRAGMENT) !== -1 || d.locations.indexOf(DirectiveLocation.FRAGMENT_DEFINITION) !== -1, }, onField: { deprecationReason: 'Use `locations`.', type: GraphQLNonNull(GraphQLBoolean), resolve: d => d.locations.indexOf(DirectiveLocation.FIELD) !== -1, }, }), }); export const __DirectiveLocation = new GraphQLEnumType({ name: '__DirectiveLocation', isIntrospection: true, description: 'A Directive can be adjacent to many parts of the GraphQL language, a ' + '__DirectiveLocation describes one such possible adjacencies.', values: { QUERY: { value: DirectiveLocation.QUERY, description: 'Location adjacent to a query operation.', }, MUTATION: { value: DirectiveLocation.MUTATION, description: 'Location adjacent to a mutation operation.', }, SUBSCRIPTION: { value: DirectiveLocation.SUBSCRIPTION, description: 'Location adjacent to a subscription operation.', }, FIELD: { value: DirectiveLocation.FIELD, description: 'Location adjacent to a field.', }, FRAGMENT_DEFINITION: { value: DirectiveLocation.FRAGMENT_DEFINITION, description: 'Location adjacent to a fragment definition.', }, FRAGMENT_SPREAD: { value: DirectiveLocation.FRAGMENT_SPREAD, description: 'Location adjacent to a fragment spread.', }, INLINE_FRAGMENT: { value: DirectiveLocation.INLINE_FRAGMENT, description: 'Location adjacent to an inline fragment.', }, SCHEMA: { value: DirectiveLocation.SCHEMA, description: 'Location adjacent to a schema definition.', }, SCALAR: { value: DirectiveLocation.SCALAR, description: 'Location adjacent to a scalar definition.', }, OBJECT: { value: DirectiveLocation.OBJECT, description: 'Location adjacent to an object type definition.', }, FIELD_DEFINITION: { value: DirectiveLocation.FIELD_DEFINITION, description: 'Location adjacent to a field definition.', }, ARGUMENT_DEFINITION: { value: DirectiveLocation.ARGUMENT_DEFINITION, description: 'Location adjacent to an argument definition.', }, INTERFACE: { value: DirectiveLocation.INTERFACE, description: 'Location adjacent to an interface definition.', }, UNION: { value: DirectiveLocation.UNION, description: 'Location adjacent to a union definition.', }, ENUM: { value: DirectiveLocation.ENUM, description: 'Location adjacent to an enum definition.', }, ENUM_VALUE: { value: DirectiveLocation.ENUM_VALUE, description: 'Location adjacent to an enum value definition.', }, INPUT_OBJECT: { value: DirectiveLocation.INPUT_OBJECT, description: 'Location adjacent to an input object type definition.', }, INPUT_FIELD_DEFINITION: { value: DirectiveLocation.INPUT_FIELD_DEFINITION, description: 'Location adjacent to an input object field definition.', }, }, }); export const __Type = new GraphQLObjectType({ name: '__Type', isIntrospection: true, description: 'The fundamental unit of any GraphQL Schema is the type. There are ' + 'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' + '\n\nDepending on the kind of a type, certain fields describe ' + 'information about that type. Scalar types provide no information ' + 'beyond a name and description, while Enum types provide their values. ' + 'Object and Interface types provide the fields they describe. Abstract ' + 'types, Union and Interface, provide the Object types possible ' + 'at runtime. List and NonNull types compose other types.', fields: () => ({ kind: { type: GraphQLNonNull(__TypeKind), resolve(type) { if (isScalarType(type)) { return TypeKind.SCALAR; } else if (isObjectType(type)) { return TypeKind.OBJECT; } else if (isInterfaceType(type)) { return TypeKind.INTERFACE; } else if (isUnionType(type)) { return TypeKind.UNION; } else if (isEnumType(type)) { return TypeKind.ENUM; } else if (isInputObjectType(type)) { return TypeKind.INPUT_OBJECT; } else if (isListType(type)) { return TypeKind.LIST; } else if (isNonNullType(type)) { return TypeKind.NON_NULL; } throw new Error('Unknown kind of type: ' + type); }, }, name: { type: GraphQLString }, description: { type: GraphQLString }, fields: { type: GraphQLList(GraphQLNonNull(__Field)), args: { includeDeprecated: { type: GraphQLBoolean, defaultValue: false }, }, resolve(type, { includeDeprecated }) { if (isObjectType(type) || isInterfaceType(type)) { let fields = objectValues(type.getFields()); if (!includeDeprecated) { fields = fields.filter(field => !field.deprecationReason); } return fields; } return null; }, }, interfaces: { type: GraphQLList(GraphQLNonNull(__Type)), resolve(type) { if (isObjectType(type)) { return type.getInterfaces(); } }, }, possibleTypes: { type: GraphQLList(GraphQLNonNull(__Type)), resolve(type, args, context, { schema }) { if (isAbstractType(type)) { return schema.getPossibleTypes(type); } }, }, enumValues: { type: GraphQLList(GraphQLNonNull(__EnumValue)), args: { includeDeprecated: { type: GraphQLBoolean, defaultValue: false }, }, resolve(type, { includeDeprecated }) { if (isEnumType(type)) { let values = type.getValues(); if (!includeDeprecated) { values = values.filter(value => !value.deprecationReason); } return values; } }, }, inputFields: { type: GraphQLList(GraphQLNonNull(__InputValue)), resolve(type) { if (isInputObjectType(type)) { return objectValues(type.getFields()); } }, }, ofType: { type: __Type }, }), }); export const __Field = new GraphQLObjectType({ name: '__Field', isIntrospection: true, description: 'Object and Interface types are described by a list of Fields, each of ' + 'which has a name, potentially a list of arguments, and a return type.', fields: () => ({ name: { type: GraphQLNonNull(GraphQLString) }, description: { type: GraphQLString }, args: { type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))), resolve: field => field.args || [], }, type: { type: GraphQLNonNull(__Type) }, isDeprecated: { type: GraphQLNonNull(GraphQLBoolean) }, deprecationReason: { type: GraphQLString, }, }), }); export const __InputValue = new GraphQLObjectType({ name: '__InputValue', isIntrospection: true, description: 'Arguments provided to Fields or Directives and the input fields of an ' + 'InputObject are represented as Input Values which describe their type ' + 'and optionally a default value.', fields: () => ({ name: { type: GraphQLNonNull(GraphQLString) }, description: { type: GraphQLString }, type: { type: GraphQLNonNull(__Type) }, defaultValue: { type: GraphQLString, description: 'A GraphQL-formatted string representing the default value for this ' + 'input value.', resolve: inputVal => isInvalid(inputVal.defaultValue) ? null : print(astFromValue(inputVal.defaultValue, inputVal.type)), }, }), }); export const __EnumValue = new GraphQLObjectType({ name: '__EnumValue', isIntrospection: true, description: 'One possible value for a given Enum. Enum values are unique values, not ' + 'a placeholder for a string or numeric value. However an Enum value is ' + 'returned in a JSON response as a string.', fields: () => ({ name: { type: GraphQLNonNull(GraphQLString) }, description: { type: GraphQLString }, isDeprecated: { type: GraphQLNonNull(GraphQLBoolean) }, deprecationReason: { type: GraphQLString, }, }), }); export const TypeKind = { SCALAR: 'SCALAR', OBJECT: 'OBJECT', INTERFACE: 'INTERFACE', UNION: 'UNION', ENUM: 'ENUM', INPUT_OBJECT: 'INPUT_OBJECT', LIST: 'LIST', NON_NULL: 'NON_NULL', }; export const __TypeKind = new GraphQLEnumType({ name: '__TypeKind', isIntrospection: true, description: 'An enum describing what kind of type a given `__Type` is.', values: { SCALAR: { value: TypeKind.SCALAR, description: 'Indicates this type is a scalar.', }, OBJECT: { value: TypeKind.OBJECT, description: 'Indicates this type is an object. ' + '`fields` and `interfaces` are valid fields.', }, INTERFACE: { value: TypeKind.INTERFACE, description: 'Indicates this type is an interface. ' + '`fields` and `possibleTypes` are valid fields.', }, UNION: { value: TypeKind.UNION, description: 'Indicates this type is a union. ' + '`possibleTypes` is a valid field.', }, ENUM: { value: TypeKind.ENUM, description: 'Indicates this type is an enum. ' + '`enumValues` is a valid field.', }, INPUT_OBJECT: { value: TypeKind.INPUT_OBJECT, description: 'Indicates this type is an input object. ' + '`inputFields` is a valid field.', }, LIST: { value: TypeKind.LIST, description: 'Indicates this type is a list. ' + '`ofType` is a valid field.', }, NON_NULL: { value: TypeKind.NON_NULL, description: 'Indicates this type is a non-null. ' + '`ofType` is a valid field.', }, }, }); /** * Note that these are GraphQLField and not GraphQLFieldConfig, * so the format for args is different. */ export const SchemaMetaFieldDef: GraphQLField<*, *> = { name: '__schema', type: GraphQLNonNull(__Schema), description: 'Access the current type schema of this server.', args: [], resolve: (source, args, context, { schema }) => schema, }; export const TypeMetaFieldDef: GraphQLField<*, *> = { name: '__type', type: __Type, description: 'Request the type information of a single type.', args: [{ name: 'name', type: GraphQLNonNull(GraphQLString) }], resolve: (source, { name }, context, { schema }) => schema.getType(name), }; export const TypeNameMetaFieldDef: GraphQLField<*, *> = { name: '__typename', type: GraphQLNonNull(GraphQLString), description: 'The name of the current Object type at runtime.', args: [], resolve: (source, args, context, { parentType }) => parentType.name, }; export const introspectionTypes: $ReadOnlyArray<*> = [ __Schema, __Directive, __DirectiveLocation, __Type, __Field, __InputValue, __EnumValue, __TypeKind, ]; export function isIntrospectionType(type: mixed): boolean %checks { return ( isNamedType(type) && // Would prefer to use introspectionTypes.some(), however %checks needs // a simple expression. (type.name === __Schema.name || type.name === __Directive.name || type.name === __DirectiveLocation.name || type.name === __Type.name || type.name === __Field.name || type.name === __InputValue.name || type.name === __EnumValue.name || type.name === __TypeKind.name) ); }