UNPKG

graphql-compose

Version:

GraphQL schema builder from different data sources with middleware extensions.

564 lines (463 loc) 18.2 kB
'use strict'; var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _map = require('babel-runtime/core-js/map'); var _map2 = _interopRequireDefault(_map); var _defineProperty = require('babel-runtime/core-js/object/define-property'); var _defineProperty2 = _interopRequireDefault(_defineProperty); Object.defineProperty(exports, "__esModule", { value: true }); 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; (0, _defineProperty2.default)(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* eslint-disable no-use-before-define, class-methods-use-this, no-unused-vars, no-param-reassign */ var _objectPath = require('object-path'); var _objectPath2 = _interopRequireDefault(_objectPath); var _graphql = require('graphql'); var _parser = require('graphql/language/parser'); var _kinds = require('graphql/language/kinds'); var _buildASTSchema = require('graphql/utilities/buildASTSchema'); var _keyValMap = require('graphql/jsutils/keyValMap'); var _keyValMap2 = _interopRequireDefault(_keyValMap); var _invariant = require('graphql/jsutils/invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _find = require('graphql/jsutils/find'); var _find2 = _interopRequireDefault(_find); var _values = require('graphql/execution/values'); var _json = require('./type/json'); var _json2 = _interopRequireDefault(_json); var _date = require('./type/date'); var _date2 = _interopRequireDefault(_date); var _typeComposer = require('./typeComposer'); var _typeComposer2 = _interopRequireDefault(_typeComposer); var _inputTypeComposer = require('./inputTypeComposer'); var _inputTypeComposer2 = _interopRequireDefault(_inputTypeComposer); var _resolver = require('./resolver'); var _resolver2 = _interopRequireDefault(_resolver); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 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 _map2.default(); 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', _graphql.GraphQLString); this.set('Float', _graphql.GraphQLFloat); this.set('Int', _graphql.GraphQLInt); this.set('Boolean', _graphql.GraphQLBoolean); this.set('ID', _graphql.GraphQLID); this.set('JSON', _json2.default); this.set('Json', _json2.default); this.set('Date', _date2.default); } }, { key: 'getWrapped', value: function getWrapped(str) { var inputTypeAST = (0, _parser.parseType)(str); return typeFromAST(inputTypeAST); } }, { key: 'createType', value: function createType(str) { var astDocument = (0, _parser.parse)(str); if (_objectPath2.default.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 _typeComposer2.default) { return { type: fieldConfig.getType() }; } if (fieldConfig instanceof _resolver2.default) { return fieldConfig.getFieldConfig(); } if (fieldConfig instanceof _inputTypeComposer2.default || fieldConfig.type instanceof _inputTypeComposer2.default) { throw new Error('You cannot provide InputTypeComposer to the field \'' + typeName + '.' + fieldName + '\'. It should be OutputType.'); } // $FlowFixMe if (typeof fieldConfig === 'string' || (0, _graphql.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 (!(0, _graphql.isOutputType)(type)) { throw new Error(typeName + '.' + fieldName + ' provided incorrect output type \'' + fieldTypeDef + '\''); } } else if (fieldConfig.type instanceof _typeComposer2.default) { fieldConfig.type = fieldConfig.type.getType(); } if (fieldConfig.args) { fieldConfig.args = this.convertArgConfigMap(fieldConfig.args, fieldName, typeName); } if (wrapWithList) { // $FlowFixMe fieldConfig.type = new _graphql.GraphQLList(fieldConfig.type); } return fieldConfig; } }, { key: 'convertOutputFieldConfigMap', value: function convertOutputFieldConfigMap(fields, typeName) { var _this = this; (0, _keys2.default)(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 _inputTypeComposer2.default) { return { type: argConfig.getType() }; } if (argConfig instanceof _typeComposer2.default || argConfig.type instanceof _typeComposer2.default) { throw new Error('You cannot provide TypeComposer to the arg \'' + typeName + '.' + fieldName + '.@' + argName + '\'. It should be InputType.'); } // $FlowFixMe if (typeof argConfig === 'string' || (0, _graphql.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 (!(0, _graphql.isInputType)(type)) { throw new Error(typeName + '.' + fieldName + '@' + argName + ' provided incorrect input type \'' + argTypeDef + '\''); } // $FlowFixMe argConfig.type = type; } else if (argConfig.type instanceof _inputTypeComposer2.default) { argConfig.type = argConfig.type.getType(); } if (wrapWithList) { // $FlowFixMe argConfig.type = new _graphql.GraphQLList(argConfig.type); } return argConfig; } }, { key: 'convertArgConfigMap', value: function convertArgConfigMap(argsConfigMap, fieldName, typeName) { var _this2 = this; if (argsConfigMap) { (0, _keys2.default)(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 _inputTypeComposer2.default) { return { type: fieldConfig.getType() }; } if (fieldConfig instanceof _typeComposer2.default || fieldConfig.type instanceof _typeComposer2.default) { throw new Error('You cannot provide TypeComposer to the field \'' + typeName + '.' + fieldName + '\'. It should be InputType.'); } // $FlowFixMe if (typeof fieldConfig === 'string' || (0, _graphql.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 (!(0, _graphql.isInputType)(type)) { throw new Error(typeName + '.' + fieldName + ' provided incorrect input type \'' + fieldTypeDef + '\''); } // $FlowFixMe fieldConfig.type = type; } else if (fieldConfig.type instanceof _inputTypeComposer2.default) { fieldConfig.type = fieldConfig.type.getType(); } if (wrapWithList) { // $FlowFixMe fieldConfig.type = new _graphql.GraphQLList(fieldConfig.type); } return fieldConfig; } }, { key: 'convertInputFieldConfigMap', value: function convertInputFieldConfigMap(fields, typeName) { var _this3 = this; (0, _keys2.default)(fields).forEach(function (name) { fields[name] = _this3.convertInputFieldConfig(fields[name], name, typeName); // eslint-disable-line }); return fields; } }]); return TypeMapper; }(); var typeMapper = new TypeMapper(); exports.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 === _kinds.LIST_TYPE) { innerType = typeFromAST(inputTypeAST.type); return innerType && new _graphql.GraphQLList(innerType); } if (inputTypeAST.kind === _kinds.NON_NULL_TYPE) { innerType = typeFromAST(inputTypeAST.type); return innerType && new _graphql.GraphQLNonNull(innerType); } (0, _invariant2.default)(inputTypeAST.kind === _kinds.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 _kinds.OBJECT_TYPE_DEFINITION: return makeTypeDef(def); // case INTERFACE_TYPE_DEFINITION: // return makeInterfaceDef(def); case _kinds.ENUM_TYPE_DEFINITION: return makeEnumDef(def); // case UNION_TYPE_DEFINITION: // return makeUnionDef(def); // case SCALAR_TYPE_DEFINITION: // return makeScalarDef(def); case _kinds.INPUT_OBJECT_TYPE_DEFINITION: return makeInputObjectDef(def); default: throw new Error('Type kind "' + def.kind + '" not supported.'); } } function makeInputValues(values) { return (0, _keyValMap2.default)(values, function (value) { return value.name.value; }, function (value) { var type = produceInputType(value.type); return { type: type, description: (0, _buildASTSchema.getDescription)(value), defaultValue: (0, _graphql.valueFromAST)(value.defaultValue, type) }; }); } function makeFieldDefMap(def) { return (0, _keyValMap2.default)(def.fields, function (field) { return field.name.value; }, function (field) { return { type: produceOutputType(field.type), description: (0, _buildASTSchema.getDescription)(field), args: makeInputValues(field.arguments), deprecationReason: getDeprecationReason(field.directives) }; }); } function makeEnumDef(def) { var enumType = new _graphql.GraphQLEnumType({ name: def.name.value, description: (0, _buildASTSchema.getDescription)(def), values: (0, _keyValMap2.default)(def.values, function (enumValue) { return enumValue.name.value; }, function (enumValue) { return { description: (0, _buildASTSchema.getDescription)(enumValue), deprecationReason: getDeprecationReason(enumValue.directives) }; }) }); return enumType; } function makeInputObjectDef(def) { return new _graphql.GraphQLInputObjectType({ name: def.name.value, description: (0, _buildASTSchema.getDescription)(def), fields: function fields() { return makeInputValues(def.fields); } }); } function getNamedTypeAST(typeAST) { var namedType = typeAST; while (namedType.kind === _kinds.LIST_TYPE || namedType.kind === _kinds.NON_NULL_TYPE) { namedType = namedType.type; } return namedType; } function buildWrappedType(innerType, inputTypeAST) { if (inputTypeAST.kind === _kinds.LIST_TYPE) { return new _graphql.GraphQLList(buildWrappedType(innerType, inputTypeAST.type)); } if (inputTypeAST.kind === _kinds.NON_NULL_TYPE) { var wrappedType = buildWrappedType(innerType, inputTypeAST.type); (0, _invariant2.default)(!(wrappedType instanceof _graphql.GraphQLNonNull), 'No nesting nonnull.'); return new _graphql.GraphQLNonNull(wrappedType); } return innerType; } function produceOutputType(typeAST) { var type = produceType(typeAST); (0, _invariant2.default)((0, _graphql.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); (0, _invariant2.default)((0, _graphql.isInputType)(type), 'Expected Input type.'); return type; } function produceInterfaceType(typeAST) { var type = produceType(typeAST); (0, _invariant2.default)(type instanceof _graphql.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 _graphql.GraphQLObjectType({ name: typeName, description: (0, _buildASTSchema.getDescription)(def), fields: function fields() { return makeFieldDefMap(def); }, interfaces: function interfaces() { return makeImplementedInterfaces(def); } }); } function getDeprecationReason(directives) { var deprecatedAST = directives && (0, _find2.default)(directives, function (directive) { return directive.name.value === _graphql.GraphQLDeprecatedDirective.name; }); if (!deprecatedAST) { return; } var _getArgumentValues = (0, _values.getArgumentValues)(_graphql.GraphQLDeprecatedDirective, deprecatedAST), reason = _getArgumentValues.reason; return reason; // eslint-disable-line }