graphql
Version:
A Query Language and Runtime which can target any service.
211 lines (190 loc) • 7.29 kB
JavaScript
/* @flow */
/**
* Copyright (c) 2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var _createClass = require('babel-runtime/helpers/create-class')['default'];
var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];
var _Object$defineProperty = require('babel-runtime/core-js/object/define-property')['default'];
var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];
_Object$defineProperty(exports, '__esModule', {
value: true
});
var _language = require('../language');
var _typeDefinition = require('../type/definition');
var _typeIntrospection = require('../type/introspection');
var _utilsTypeFromAST = require('../utils/typeFromAST');
var _utilsTypeFromAST2 = _interopRequireDefault(_utilsTypeFromAST);
var _find = require('./find');
var _find2 = _interopRequireDefault(_find);
/**
* FieldInfo is a utility class which, given a GraphQL schema, can keep track
* of the current field and type definitions at any point in a GraphQL document
* AST during a recursive descent by calling `enter(node)` and `leave(node)`.
*/
var TypeInfo = (function () {
function TypeInfo(schema) {
_classCallCheck(this, TypeInfo);
this._schema = schema;
this._typeStack = [];
this._parentTypeStack = [];
this._inputTypeStack = [];
this._fieldDefStack = [];
}
_createClass(TypeInfo, [{
key: 'getType',
value: function getType() {
if (this._typeStack.length > 0) {
return this._typeStack[this._typeStack.length - 1];
}
}
}, {
key: 'getParentType',
value: function getParentType() {
if (this._parentTypeStack.length > 0) {
return this._parentTypeStack[this._parentTypeStack.length - 1];
}
}
}, {
key: 'getInputType',
value: function getInputType() {
if (this._inputTypeStack.length > 0) {
return this._inputTypeStack[this._inputTypeStack.length - 1];
}
}
}, {
key: 'getFieldDef',
value: function getFieldDef() {
if (this._fieldDefStack.length > 0) {
return this._fieldDefStack[this._fieldDefStack.length - 1];
}
}
}, {
key: 'enter',
// Flow does not yet handle this case.
value: function enter(node) {
var schema = this._schema;
var type;
switch (node.kind) {
case _language.Kind.SELECTION_SET:
var compositeType;
var rawType = (0, _typeDefinition.getUnmodifiedType)(this.getType());
if ((0, _typeDefinition.isCompositeType)(rawType)) {
// isCompositeType is a type refining predicate, so this is safe.
compositeType = rawType;
}
this._parentTypeStack.push(compositeType);
break;
case _language.Kind.FIELD:
var parentType = this.getParentType();
var fieldDef;
if (parentType) {
fieldDef = getFieldDef(schema, parentType, node);
}
this._fieldDefStack.push(fieldDef);
this._typeStack.push(fieldDef && fieldDef.type);
break;
case _language.Kind.OPERATION_DEFINITION:
if (node.operation === 'query') {
type = schema.getQueryType();
} else if (node.operation === 'mutation') {
type = schema.getMutationType();
}
this._typeStack.push(type);
break;
case _language.Kind.INLINE_FRAGMENT:
case _language.Kind.FRAGMENT_DEFINITION:
type = schema.getType(node.typeCondition.value);
this._typeStack.push(type);
break;
case _language.Kind.VARIABLE_DEFINITION:
this._inputTypeStack.push((0, _utilsTypeFromAST2['default'])(schema, node.type));
break;
case _language.Kind.ARGUMENT:
var field = this.getFieldDef();
var argType;
if (field) {
var argDef = (0, _find2['default'])(field.args, function (arg) {
return arg.name === node.name.value;
});
if (argDef) {
argType = argDef.type;
}
}
this._inputTypeStack.push(argType);
break;
case _language.Kind.DIRECTIVE:
var directive = schema.getDirective(node.name.value);
this._inputTypeStack.push(directive ? directive.type : undefined);
break;
case _language.Kind.ARRAY:
var arrayType = (0, _typeDefinition.getNullableType)(this.getInputType());
this._inputTypeStack.push(arrayType instanceof _typeDefinition.GraphQLList ? arrayType.ofType : undefined);
break;
case _language.Kind.OBJECT_FIELD:
var objectType = (0, _typeDefinition.getUnmodifiedType)(this.getInputType());
var fieldType;
if (objectType instanceof _typeDefinition.GraphQLInputObjectType) {
var inputField = objectType.getFields()[node.name.value];
fieldType = inputField ? inputField.type : undefined;
}
this._inputTypeStack.push(fieldType);
break;
}
}
}, {
key: 'leave',
value: function leave(node) {
switch (node.kind) {
case _language.Kind.SELECTION_SET:
this._parentTypeStack.pop();
break;
case _language.Kind.FIELD:
this._fieldDefStack.pop();
this._typeStack.pop();
break;
case _language.Kind.OPERATION_DEFINITION:
case _language.Kind.INLINE_FRAGMENT:
case _language.Kind.FRAGMENT_DEFINITION:
this._typeStack.pop();
break;
case _language.Kind.VARIABLE_DEFINITION:
case _language.Kind.ARGUMENT:
case _language.Kind.DIRECTIVE:
case _language.Kind.ARRAY:
case _language.Kind.OBJECT_FIELD:
this._inputTypeStack.pop();
break;
}
}
}]);
return TypeInfo;
})();
exports['default'] = TypeInfo;
/**
* Not exactly the same as the executor's definition of getFieldDef, in this
* statically evaluated environment we do not always have an Object type,
* and need to handle Interface and Union types.
*/
function getFieldDef(schema, parentType, fieldAST) {
var name = fieldAST.name.value;
if (name === _typeIntrospection.SchemaMetaFieldDef.name && schema.getQueryType() === parentType) {
return _typeIntrospection.SchemaMetaFieldDef;
}
if (name === _typeIntrospection.TypeMetaFieldDef.name && schema.getQueryType() === parentType) {
return _typeIntrospection.TypeMetaFieldDef;
}
if (name === _typeIntrospection.TypeNameMetaFieldDef.name && (parentType instanceof _typeDefinition.GraphQLObjectType || parentType instanceof _typeDefinition.GraphQLInterfaceType || parentType instanceof _typeDefinition.GraphQLUnionType)) {
return _typeIntrospection.TypeNameMetaFieldDef;
}
if (parentType instanceof _typeDefinition.GraphQLObjectType || parentType instanceof _typeDefinition.GraphQLInterfaceType) {
return parentType.getFields()[name];
}
}
module.exports = exports['default'];
/*Node*/