graphql-compose
Version:
GraphQL schema builder from different data sources with middleware extensions.
138 lines (120 loc) • 3.82 kB
Flow
/* @flow */
/* eslint-disable no-use-before-define, prefer-template */
import {
GraphQLInputObjectType,
GraphQLObjectType,
isInputType,
isAbstractType,
GraphQLList,
GraphQLNonNull,
} from 'graphql';
import util from 'util';
import TypeComposer from './typeComposer';
import InputTypeComposer from './inputTypeComposer';
import GenericType from './type/generic';
import { upperFirst } from './utils/misc';
import type {
GraphQLFieldConfig,
GraphQLFieldConfigMap,
GraphQLType,
GraphQLInputFieldConfig,
GraphQLInputType,
} from './definition';
export function removeWrongFields<TSource, TContext>(
fields: GraphQLFieldConfigMap<TSource, TContext>
): GraphQLFieldConfigMap<TSource, TContext> {
const result = {};
Object.keys(fields).forEach((key) => {
const field = fields[key];
if (
!isAbstractType(field.type) // skip interface fields
&& !field._gqcResolver // skip fields that obtained via Resolver
) {
result[key] = field;
}
});
return result;
}
export type toInputObjectTypeOpts = {
prefix?: string,
postfix?: string,
}
export function toInputObjectType(
typeComposer: TypeComposer,
opts: toInputObjectTypeOpts = {},
cache: Map<GraphQLObjectType, InputTypeComposer> = new Map(),
): InputTypeComposer {
if (typeComposer.hasInputTypeComposer()) {
return typeComposer.getInputTypeComposer();
}
const prefix: string = opts.prefix || '';
const postfix: string = opts.postfix || 'Input';
const inputTypeName = `${prefix}${typeComposer.getTypeName()}${postfix}`;
if (cache.has(typeComposer.getType())) {
// $FlowFixMe
return cache.get(typeComposer.getType());
}
const inputTypeComposer = new InputTypeComposer(
new GraphQLInputObjectType({
name: inputTypeName,
fields: {},
})
);
cache.set(typeComposer.getType(), inputTypeComposer);
const outputFields = removeWrongFields(typeComposer.getFields());
const inputFields = {};
Object.keys(outputFields).forEach((key) => {
const fieldOpts = {
...opts,
fieldName: key,
outputTypeName: typeComposer.getTypeName(),
};
inputFields[key] = convertInputObjectField(outputFields[key], fieldOpts, cache);
});
inputTypeComposer.addFields(inputFields);
return inputTypeComposer;
}
export type convertInputObjectFieldOpts = {
prefix?: string,
postfix?: string,
fieldName?: string,
outputTypeName?: string,
}
export function convertInputObjectField<TSource, TContext>(
field: GraphQLFieldConfig<TSource, TContext>,
opts: convertInputObjectFieldOpts,
cache: Map<GraphQLObjectType, InputTypeComposer>
): GraphQLInputFieldConfig {
let fieldType: GraphQLType = field.type;
const wrappers = [];
while (fieldType instanceof GraphQLList ||
fieldType instanceof GraphQLNonNull
) {
wrappers.unshift(fieldType.constructor);
fieldType = fieldType.ofType;
}
if (!isInputType(fieldType)) {
if (fieldType instanceof GraphQLObjectType) {
const typeOpts = {
prefix: `${opts.prefix || ''}${upperFirst(opts.outputTypeName || '')}`,
postfix: opts.postfix || 'Input',
};
const typeComposer = new TypeComposer(fieldType);
fieldType = toInputObjectType(typeComposer, typeOpts, cache).getType();
} else {
// eslint-disable-next-line
console.error(
`GQC: can not convert field '${opts.outputTypeName || ''}.${opts.fieldName || ''}' to InputType`
+ '\nIt should be GraphQLObjectType, but got \n'
+ util.inspect(fieldType, { depth: 2, colors: true })
);
fieldType = GenericType;
}
}
// $FlowFixMe
const inputFieldType: GraphQLInputType = wrappers.reduce(
(type, Wrapper) => new Wrapper(type),
fieldType
);
return { type: inputFieldType, description: field.description };
}