graphql-compose
Version:
GraphQL schema builder from different data sources with middleware extensions.
516 lines (443 loc) • 16.9 kB
JavaScript
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/* eslint-disable no-use-before-define, class-methods-use-this, no-unused-vars, no-param-reassign */
import objectPath from 'object-path';
import { GraphQLInt, GraphQLFloat, GraphQLString, GraphQLBoolean, GraphQLScalarType, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLEnumType, GraphQLObjectType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLDirective, GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLDeprecatedDirective, isOutputType, isInputType, valueFromAST } from 'graphql';
import { parse, parseType } from 'graphql/language/parser';
import { LIST_TYPE, NON_NULL_TYPE, DOCUMENT, SCHEMA_DEFINITION, SCALAR_TYPE_DEFINITION, OBJECT_TYPE_DEFINITION, INTERFACE_TYPE_DEFINITION, ENUM_TYPE_DEFINITION, UNION_TYPE_DEFINITION, INPUT_OBJECT_TYPE_DEFINITION, DIRECTIVE_DEFINITION, NAMED_TYPE } from 'graphql/language/kinds';
import { getDescription } from 'graphql/utilities/buildASTSchema';
import keyValMap from 'graphql/jsutils/keyValMap';
import invariant from 'graphql/jsutils/invariant';
import find from 'graphql/jsutils/find';
import { getArgumentValues } from 'graphql/execution/values';
import GraphQLJSON from './type/json';
import GraphQLDate from './type/date';
import TypeComposer from './typeComposer';
import InputTypeComposer from './inputTypeComposer';
import Resolver from './resolver';
var RegexpOutputTypeDefinition = /type\s[^{]+\{[^}]+\}/im;
var RegexpInputTypeDefinition = /input\s[^{]+\{[^}]+\}/im;
var RegexpEnumTypeDefinition = /enum\s[^{]+\{[^}]+\}/im;
var TypeMapper = function () {
function TypeMapper() {
_classCallCheck(this, TypeMapper);
this.map = new Map();
this.addBasicScalarTypes();
}
_createClass(TypeMapper, [{
key: 'get',
value: function get(name) {
return this.map.get(name);
}
}, {
key: 'set',
value: function set(name, type) {
this.map.set(name, type);
}
}, {
key: 'has',
value: function has(name) {
return this.map.has(name);
}
}, {
key: 'delete',
value: function _delete(name) {
return this.map.delete(name);
}
}, {
key: 'keys',
value: function keys() {
// eslint-disable-line no-undef
return this.map.keys();
}
}, {
key: 'addBasicScalarTypes',
value: function addBasicScalarTypes() {
this.set('String', GraphQLString);
this.set('Float', GraphQLFloat);
this.set('Int', GraphQLInt);
this.set('Boolean', GraphQLBoolean);
this.set('ID', GraphQLID);
this.set('JSON', GraphQLJSON);
this.set('Json', GraphQLJSON);
this.set('Date', GraphQLDate);
}
}, {
key: 'getWrapped',
value: function getWrapped(str) {
var inputTypeAST = parseType(str);
return typeFromAST(inputTypeAST);
}
}, {
key: 'createType',
value: function createType(str) {
var astDocument = parse(str);
if (objectPath.get(astDocument, 'kind') !== 'Document') {
throw new Error('You should provide correct type syntax. ' + "Eg. createType('type IntRange { min: Int, max: Int }')");
}
var types = parseTypes(astDocument);
var type = types[0];
if (type) {
this.set(type.name, type);
return type;
}
return undefined;
}
}, {
key: 'convertOutputFieldConfig',
value: function convertOutputFieldConfig(fieldConfig, fieldName, typeName) {
var wrapWithList = false;
if (Array.isArray(fieldConfig)) {
fieldConfig = {
type: fieldConfig
};
}
if (Array.isArray(fieldConfig.type)) {
if (fieldConfig.type.length !== 1) {
throw new Error(typeName + '.' + fieldName + ' can accept Array exact with one output type definition');
}
wrapWithList = true;
fieldConfig.type = fieldConfig.type[0];
}
if (fieldConfig instanceof TypeComposer) {
return { type: fieldConfig.getType() };
}
if (fieldConfig instanceof Resolver) {
return fieldConfig.getFieldConfig();
}
if (fieldConfig instanceof InputTypeComposer || fieldConfig.type instanceof InputTypeComposer) {
throw new Error('You cannot provide InputTypeComposer to the field \'' + typeName + '.' + fieldName + '\'. It should be OutputType.');
}
// $FlowFixMe
if (typeof fieldConfig === 'string' || isOutputType(fieldConfig)) {
fieldConfig = {
// $FlowFixMe
type: fieldConfig
};
}
if (typeof fieldConfig.type === 'string') {
var fieldTypeDef = fieldConfig.type;
if (RegexpInputTypeDefinition.test(fieldTypeDef)) {
throw new Error(typeName + '.' + fieldName + ' should be OutputType, but got input type definition \'' + fieldTypeDef + '\'');
}
var type = RegexpOutputTypeDefinition.test(fieldTypeDef) || RegexpEnumTypeDefinition.test(fieldTypeDef) ? this.createType(fieldTypeDef) : this.getWrapped(fieldTypeDef);
if (!type) {
throw new Error(typeName + '.' + fieldName + ' can not conver to OutputType following string: \'' + fieldTypeDef + '\'');
}
// $FlowFixMe
fieldConfig.type = type;
if (!isOutputType(type)) {
throw new Error(typeName + '.' + fieldName + ' provided incorrect output type \'' + fieldTypeDef + '\'');
}
} else if (fieldConfig.type instanceof TypeComposer) {
fieldConfig.type = fieldConfig.type.getType();
}
if (fieldConfig.args) {
fieldConfig.args = this.convertArgConfigMap(fieldConfig.args, fieldName, typeName);
}
if (wrapWithList) {
// $FlowFixMe
fieldConfig.type = new GraphQLList(fieldConfig.type);
}
return fieldConfig;
}
}, {
key: 'convertOutputFieldConfigMap',
value: function convertOutputFieldConfigMap(fields, typeName) {
var _this = this;
Object.keys(fields).forEach(function (name) {
fields[name] = _this.convertOutputFieldConfig(fields[name], name, typeName); // eslint-disable-line
});
return fields;
}
}, {
key: 'convertArgConfig',
value: function convertArgConfig(argConfig, argName, fieldName, typeName) {
var wrapWithList = false;
if (Array.isArray(argConfig)) {
argConfig = {
type: argConfig
};
}
// $FlowFixMe
if (Array.isArray(argConfig.type)) {
if (argConfig.type.length !== 1) {
throw new Error(typeName + '.' + fieldName + '@' + argName + ' can accept Array exact with one input type definition');
}
wrapWithList = true;
// $FlowFixMe
argConfig.type = argConfig.type[0];
}
if (argConfig instanceof InputTypeComposer) {
return { type: argConfig.getType() };
}
if (argConfig instanceof TypeComposer || argConfig.type instanceof TypeComposer) {
throw new Error('You cannot provide TypeComposer to the arg \'' + typeName + '.' + fieldName + '.@' + argName + '\'. It should be InputType.');
}
// $FlowFixMe
if (typeof argConfig === 'string' || isInputType(argConfig)) {
argConfig = {
// $FlowFixMe
type: argConfig
};
}
if (typeof argConfig.type === 'string') {
var argTypeDef = argConfig.type;
if (RegexpOutputTypeDefinition.test(argTypeDef)) {
throw new Error(typeName + '.' + fieldName + '@' + argName + ' should be InputType, but got output type definition \'' + argTypeDef + '\'');
}
var type = RegexpInputTypeDefinition.test(argTypeDef) || RegexpEnumTypeDefinition.test(argTypeDef) ? this.createType(argTypeDef) : this.getWrapped(argTypeDef);
if (!isInputType(type)) {
throw new Error(typeName + '.' + fieldName + '@' + argName + ' provided incorrect input type \'' + argTypeDef + '\'');
}
// $FlowFixMe
argConfig.type = type;
} else if (argConfig.type instanceof InputTypeComposer) {
argConfig.type = argConfig.type.getType();
}
if (wrapWithList) {
// $FlowFixMe
argConfig.type = new GraphQLList(argConfig.type);
}
return argConfig;
}
}, {
key: 'convertArgConfigMap',
value: function convertArgConfigMap(argsConfigMap, fieldName, typeName) {
var _this2 = this;
if (argsConfigMap) {
Object.keys(argsConfigMap).forEach(function (argName) {
argsConfigMap[argName] = _this2.convertArgConfig(
// eslint-disable-line
argsConfigMap[argName], argName, fieldName, typeName);
});
}
return argsConfigMap;
}
}, {
key: 'convertInputFieldConfig',
value: function convertInputFieldConfig(fieldConfig, fieldName, typeName) {
var wrapWithList = false;
if (Array.isArray(fieldConfig)) {
fieldConfig = {
type: fieldConfig
};
}
if (Array.isArray(fieldConfig.type)) {
if (fieldConfig.type.length !== 1) {
throw new Error(typeName + '.' + fieldName + ' can accept Array exact with one input type definition');
}
wrapWithList = true;
fieldConfig.type = fieldConfig.type[0];
}
if (fieldConfig instanceof InputTypeComposer) {
return { type: fieldConfig.getType() };
}
if (fieldConfig instanceof TypeComposer || fieldConfig.type instanceof TypeComposer) {
throw new Error('You cannot provide TypeComposer to the field \'' + typeName + '.' + fieldName + '\'. It should be InputType.');
}
// $FlowFixMe
if (typeof fieldConfig === 'string' || isInputType(fieldConfig)) {
fieldConfig = {
// $FlowFixMe
type: fieldConfig
};
}
if (typeof fieldConfig.type === 'string') {
var fieldTypeDef = fieldConfig.type;
if (RegexpOutputTypeDefinition.test(fieldTypeDef)) {
throw new Error(typeName + '.' + fieldName + ' should be InputType, but got output type definition \'' + fieldTypeDef + '\'');
}
var type = RegexpInputTypeDefinition.test(fieldTypeDef) || RegexpEnumTypeDefinition.test(fieldTypeDef) ? this.createType(fieldTypeDef) : this.getWrapped(fieldTypeDef);
if (!isInputType(type)) {
throw new Error(typeName + '.' + fieldName + ' provided incorrect input type \'' + fieldTypeDef + '\'');
}
// $FlowFixMe
fieldConfig.type = type;
} else if (fieldConfig.type instanceof InputTypeComposer) {
fieldConfig.type = fieldConfig.type.getType();
}
if (wrapWithList) {
// $FlowFixMe
fieldConfig.type = new GraphQLList(fieldConfig.type);
}
return fieldConfig;
}
}, {
key: 'convertInputFieldConfigMap',
value: function convertInputFieldConfigMap(fields, typeName) {
var _this3 = this;
Object.keys(fields).forEach(function (name) {
fields[name] = _this3.convertInputFieldConfig(fields[name], name, typeName); // eslint-disable-line
});
return fields;
}
}]);
return TypeMapper;
}();
var typeMapper = new TypeMapper();
export default typeMapper;
function parseTypes(astDocument) {
var types = [];
for (var i = 0; i < astDocument.definitions.length; i++) {
var def = astDocument.definitions[i];
types[i] = makeSchemaDef(def);
}
return types;
}
function typeFromAST(inputTypeAST) {
var innerType = void 0;
if (inputTypeAST.kind === LIST_TYPE) {
innerType = typeFromAST(inputTypeAST.type);
return innerType && new GraphQLList(innerType);
}
if (inputTypeAST.kind === NON_NULL_TYPE) {
innerType = typeFromAST(inputTypeAST.type);
return innerType && new GraphQLNonNull(innerType);
}
invariant(inputTypeAST.kind === NAMED_TYPE, 'Must be a named type.');
return typeMapper.get(inputTypeAST.name.value);
}
function typeDefNamed(typeName) {
var type = typeMapper.get(typeName);
if (type) {
return type;
}
throw new Error('Cannot find type with name \'' + typeName + '\' in TypeMapper.');
}
function makeSchemaDef(def) {
if (!def) {
throw new Error('def must be defined');
}
switch (def.kind) {
case OBJECT_TYPE_DEFINITION:
return makeTypeDef(def);
// case INTERFACE_TYPE_DEFINITION:
// return makeInterfaceDef(def);
case ENUM_TYPE_DEFINITION:
return makeEnumDef(def);
// case UNION_TYPE_DEFINITION:
// return makeUnionDef(def);
// case SCALAR_TYPE_DEFINITION:
// return makeScalarDef(def);
case INPUT_OBJECT_TYPE_DEFINITION:
return makeInputObjectDef(def);
default:
throw new Error('Type kind "' + def.kind + '" not supported.');
}
}
function makeInputValues(values) {
return keyValMap(values, function (value) {
return value.name.value;
}, function (value) {
var type = produceInputType(value.type);
return {
type: type,
description: getDescription(value),
defaultValue: valueFromAST(value.defaultValue, type)
};
});
}
function makeFieldDefMap(def) {
return keyValMap(def.fields, function (field) {
return field.name.value;
}, function (field) {
return {
type: produceOutputType(field.type),
description: getDescription(field),
args: makeInputValues(field.arguments),
deprecationReason: getDeprecationReason(field.directives)
};
});
}
function makeEnumDef(def) {
var enumType = new GraphQLEnumType({
name: def.name.value,
description: getDescription(def),
values: keyValMap(def.values, function (enumValue) {
return enumValue.name.value;
}, function (enumValue) {
return {
description: getDescription(enumValue),
deprecationReason: getDeprecationReason(enumValue.directives)
};
})
});
return enumType;
}
function makeInputObjectDef(def) {
return new GraphQLInputObjectType({
name: def.name.value,
description: getDescription(def),
fields: function fields() {
return makeInputValues(def.fields);
}
});
}
function getNamedTypeAST(typeAST) {
var namedType = typeAST;
while (namedType.kind === LIST_TYPE || namedType.kind === NON_NULL_TYPE) {
namedType = namedType.type;
}
return namedType;
}
function buildWrappedType(innerType, inputTypeAST) {
if (inputTypeAST.kind === LIST_TYPE) {
return new GraphQLList(buildWrappedType(innerType, inputTypeAST.type));
}
if (inputTypeAST.kind === NON_NULL_TYPE) {
var wrappedType = buildWrappedType(innerType, inputTypeAST.type);
invariant(!(wrappedType instanceof GraphQLNonNull), 'No nesting nonnull.');
return new GraphQLNonNull(wrappedType);
}
return innerType;
}
function produceOutputType(typeAST) {
var type = produceType(typeAST);
invariant(isOutputType(type), 'Expected Output type.');
return type;
}
function produceType(typeAST) {
var typeName = getNamedTypeAST(typeAST).name.value;
var typeDef = typeDefNamed(typeName);
return buildWrappedType(typeDef, typeAST);
}
function produceInputType(typeAST) {
var type = produceType(typeAST);
invariant(isInputType(type), 'Expected Input type.');
return type;
}
function produceInterfaceType(typeAST) {
var type = produceType(typeAST);
invariant(type instanceof GraphQLInterfaceType, 'Expected Object type.');
return type;
}
function makeImplementedInterfaces(def) {
return def.interfaces && def.interfaces.map(function (iface) {
return produceInterfaceType(iface);
});
}
function makeTypeDef(def) {
var typeName = def.name.value;
return new GraphQLObjectType({
name: typeName,
description: getDescription(def),
fields: function fields() {
return makeFieldDefMap(def);
},
interfaces: function interfaces() {
return makeImplementedInterfaces(def);
}
});
}
function getDeprecationReason(directives) {
var deprecatedAST = directives && find(directives, function (directive) {
return directive.name.value === GraphQLDeprecatedDirective.name;
});
if (!deprecatedAST) {
return;
}
var _getArgumentValues = getArgumentValues(GraphQLDeprecatedDirective, deprecatedAST),
reason = _getArgumentValues.reason;
return reason; // eslint-disable-line
}